mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-15 11:22:55 +00:00
Save high scores and high ranks separately.
This commit is contained in:
parent
00021523bc
commit
b30faad7d9
|
@ -2818,8 +2818,13 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
deathCounter = 0;
|
deathCounter = 0;
|
||||||
|
|
||||||
|
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
|
||||||
|
// `easy`, `erect`, `normal-pico`, etc.
|
||||||
|
var suffixedDifficulty = (currentVariation != Constants.DEFAULT_VARIATION
|
||||||
|
&& currentVariation != 'erect') ? '$currentDifficulty-${currentVariation}' : currentDifficulty;
|
||||||
|
|
||||||
var isNewHighscore = false;
|
var isNewHighscore = false;
|
||||||
var prevScoreData:Null<SaveScoreData> = Save.instance.getSongScore(currentSong.id, currentDifficulty);
|
var prevScoreData:Null<SaveScoreData> = Save.instance.getSongScore(currentSong.id, suffixedDifficulty);
|
||||||
|
|
||||||
if (currentSong != null && currentSong.validScore)
|
if (currentSong != null && currentSong.validScore)
|
||||||
{
|
{
|
||||||
|
@ -2844,13 +2849,21 @@ class PlayState extends MusicBeatSubState
|
||||||
// adds current song data into the tallies for the level (story levels)
|
// adds current song data into the tallies for the level (story levels)
|
||||||
Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel);
|
Highscore.talliesLevel = Highscore.combineTallies(Highscore.tallies, Highscore.talliesLevel);
|
||||||
|
|
||||||
if (!isPracticeMode && !isBotPlayMode && Save.instance.isSongHighScore(currentSong.id, currentDifficulty, data))
|
if (!isPracticeMode && !isBotPlayMode)
|
||||||
{
|
{
|
||||||
Save.instance.setSongScore(currentSong.id, currentDifficulty, data);
|
isNewHighscore = Save.instance.isSongHighScore(currentSong.id, suffixedDifficulty, data);
|
||||||
#if newgrounds
|
|
||||||
NGio.postScore(score, currentSong.id);
|
// If no high score is present, save both score and rank.
|
||||||
#end
|
// If score or rank are better, save the highest one.
|
||||||
isNewHighscore = true;
|
// If neither are higher, nothing will change.
|
||||||
|
Save.instance.applySongRank(currentSong.id, suffixedDifficulty, data);
|
||||||
|
|
||||||
|
if (isNewHighscore)
|
||||||
|
{
|
||||||
|
#if newgrounds
|
||||||
|
NGio.postScore(score, currentSong.id);
|
||||||
|
#end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,10 @@ class Scoring
|
||||||
|
|
||||||
// Perfect (Platinum) is a Sick Full Clear
|
// Perfect (Platinum) is a Sick Full Clear
|
||||||
var isPerfectGold = scoreData.tallies.sick == scoreData.tallies.totalNotes;
|
var isPerfectGold = scoreData.tallies.sick == scoreData.tallies.totalNotes;
|
||||||
if (isPerfectGold) return ScoringRank.PERFECT_GOLD;
|
if (isPerfectGold)
|
||||||
|
{
|
||||||
|
return ScoringRank.PERFECT_GOLD;
|
||||||
|
}
|
||||||
|
|
||||||
// Else, use the standard grades
|
// Else, use the standard grades
|
||||||
|
|
||||||
|
@ -397,62 +400,79 @@ enum abstract ScoringRank(String)
|
||||||
var GOOD;
|
var GOOD;
|
||||||
var SHIT;
|
var SHIT;
|
||||||
|
|
||||||
@:op(A > B) static function compare(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
/**
|
||||||
|
* Converts ScoringRank to an integer value for comparison.
|
||||||
|
* Better ranks should be tied to a higher value.
|
||||||
|
*/
|
||||||
|
static function getValue(rank:Null<ScoringRank>):Int
|
||||||
|
{
|
||||||
|
if (rank == null) return -1;
|
||||||
|
switch (rank)
|
||||||
|
{
|
||||||
|
case PERFECT_GOLD:
|
||||||
|
return 5;
|
||||||
|
case PERFECT:
|
||||||
|
return 4;
|
||||||
|
case EXCELLENT:
|
||||||
|
return 3;
|
||||||
|
case GREAT:
|
||||||
|
return 2;
|
||||||
|
case GOOD:
|
||||||
|
return 1;
|
||||||
|
case SHIT:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yes, we really need a different function for each comparison operator.
|
||||||
|
@:op(A > B) static function compareGT(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||||
{
|
{
|
||||||
if (a != null && b == null) return true;
|
if (a != null && b == null) return true;
|
||||||
if (a == null || b == null) return false;
|
if (a == null || b == null) return false;
|
||||||
|
|
||||||
var temp1:Int = 0;
|
var temp1:Int = getValue(a);
|
||||||
var temp2:Int = 0;
|
var temp2:Int = getValue(b);
|
||||||
|
|
||||||
// temp 1
|
return temp1 > temp2;
|
||||||
switch (a)
|
|
||||||
{
|
|
||||||
case PERFECT_GOLD:
|
|
||||||
temp1 = 5;
|
|
||||||
case PERFECT:
|
|
||||||
temp1 = 4;
|
|
||||||
case EXCELLENT:
|
|
||||||
temp1 = 3;
|
|
||||||
case GREAT:
|
|
||||||
temp1 = 2;
|
|
||||||
case GOOD:
|
|
||||||
temp1 = 1;
|
|
||||||
case SHIT:
|
|
||||||
temp1 = 0;
|
|
||||||
default:
|
|
||||||
temp1 = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp 2
|
|
||||||
switch (b)
|
|
||||||
{
|
|
||||||
case PERFECT_GOLD:
|
|
||||||
temp2 = 5;
|
|
||||||
case PERFECT:
|
|
||||||
temp2 = 4;
|
|
||||||
case EXCELLENT:
|
|
||||||
temp2 = 3;
|
|
||||||
case GREAT:
|
|
||||||
temp2 = 2;
|
|
||||||
case GOOD:
|
|
||||||
temp2 = 1;
|
|
||||||
case SHIT:
|
|
||||||
temp2 = 0;
|
|
||||||
default:
|
|
||||||
temp2 = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp1 > temp2)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:op(A >= B) static function compareGTEQ(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||||
|
{
|
||||||
|
if (a != null && b == null) return true;
|
||||||
|
if (a == null || b == null) return false;
|
||||||
|
|
||||||
|
var temp1:Int = getValue(a);
|
||||||
|
var temp2:Int = getValue(b);
|
||||||
|
|
||||||
|
return temp1 >= temp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:op(A < B) static function compareLT(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||||
|
{
|
||||||
|
if (a != null && b == null) return true;
|
||||||
|
if (a == null || b == null) return false;
|
||||||
|
|
||||||
|
var temp1:Int = getValue(a);
|
||||||
|
var temp2:Int = getValue(b);
|
||||||
|
|
||||||
|
return temp1 < temp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:op(A <= B) static function compareLTEQ(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||||
|
{
|
||||||
|
if (a != null && b == null) return true;
|
||||||
|
if (a == null || b == null) return false;
|
||||||
|
|
||||||
|
var temp1:Int = getValue(a);
|
||||||
|
var temp2:Int = getValue(b);
|
||||||
|
|
||||||
|
return temp1 <= temp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @:op(A == B) isn't necessary!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay in seconds
|
* Delay in seconds
|
||||||
*/
|
*/
|
||||||
|
@ -462,15 +482,15 @@ enum abstract ScoringRank(String)
|
||||||
{
|
{
|
||||||
case PERFECT_GOLD | PERFECT:
|
case PERFECT_GOLD | PERFECT:
|
||||||
// return 2.5;
|
// return 2.5;
|
||||||
return 95/24;
|
return 95 / 24;
|
||||||
case EXCELLENT:
|
case EXCELLENT:
|
||||||
return 0;
|
return 0;
|
||||||
case GREAT:
|
case GREAT:
|
||||||
return 5/24;
|
return 5 / 24;
|
||||||
case GOOD:
|
case GOOD:
|
||||||
return 3/24;
|
return 3 / 24;
|
||||||
case SHIT:
|
case SHIT:
|
||||||
return 2/24;
|
return 2 / 24;
|
||||||
default:
|
default:
|
||||||
return 3.5;
|
return 3.5;
|
||||||
}
|
}
|
||||||
|
@ -482,15 +502,15 @@ enum abstract ScoringRank(String)
|
||||||
{
|
{
|
||||||
case PERFECT_GOLD | PERFECT:
|
case PERFECT_GOLD | PERFECT:
|
||||||
// return 2.5;
|
// return 2.5;
|
||||||
return 95/24;
|
return 95 / 24;
|
||||||
case EXCELLENT:
|
case EXCELLENT:
|
||||||
return 97/24;
|
return 97 / 24;
|
||||||
case GREAT:
|
case GREAT:
|
||||||
return 95/24;
|
return 95 / 24;
|
||||||
case GOOD:
|
case GOOD:
|
||||||
return 95/24;
|
return 95 / 24;
|
||||||
case SHIT:
|
case SHIT:
|
||||||
return 95/24;
|
return 95 / 24;
|
||||||
default:
|
default:
|
||||||
return 3.5;
|
return 3.5;
|
||||||
}
|
}
|
||||||
|
@ -502,15 +522,15 @@ enum abstract ScoringRank(String)
|
||||||
{
|
{
|
||||||
case PERFECT_GOLD | PERFECT:
|
case PERFECT_GOLD | PERFECT:
|
||||||
// return 2.5;
|
// return 2.5;
|
||||||
return 129/24;
|
return 129 / 24;
|
||||||
case EXCELLENT:
|
case EXCELLENT:
|
||||||
return 122/24;
|
return 122 / 24;
|
||||||
case GREAT:
|
case GREAT:
|
||||||
return 109/24;
|
return 109 / 24;
|
||||||
case GOOD:
|
case GOOD:
|
||||||
return 107/24;
|
return 107 / 24;
|
||||||
case SHIT:
|
case SHIT:
|
||||||
return 186/24;
|
return 186 / 24;
|
||||||
default:
|
default:
|
||||||
return 3.5;
|
return 3.5;
|
||||||
}
|
}
|
||||||
|
@ -522,15 +542,15 @@ enum abstract ScoringRank(String)
|
||||||
{
|
{
|
||||||
case PERFECT_GOLD | PERFECT:
|
case PERFECT_GOLD | PERFECT:
|
||||||
// return 2.5;
|
// return 2.5;
|
||||||
return 140/24;
|
return 140 / 24;
|
||||||
case EXCELLENT:
|
case EXCELLENT:
|
||||||
return 140/24;
|
return 140 / 24;
|
||||||
case GREAT:
|
case GREAT:
|
||||||
return 129/24;
|
return 129 / 24;
|
||||||
case GOOD:
|
case GOOD:
|
||||||
return 127/24;
|
return 127 / 24;
|
||||||
case SHIT:
|
case SHIT:
|
||||||
return 207/24;
|
return 207 / 24;
|
||||||
default:
|
default:
|
||||||
return 3.5;
|
return 3.5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package funkin.save;
|
package funkin.save;
|
||||||
|
|
||||||
import flixel.util.FlxSave;
|
import flixel.util.FlxSave;
|
||||||
|
import funkin.util.FileUtil;
|
||||||
import funkin.input.Controls.Device;
|
import funkin.input.Controls.Device;
|
||||||
import funkin.play.scoring.Scoring;
|
import funkin.play.scoring.Scoring;
|
||||||
import funkin.play.scoring.Scoring.ScoringRank;
|
import funkin.play.scoring.Scoring.ScoringRank;
|
||||||
|
@ -58,7 +59,7 @@ class Save
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
// Make sure the verison number is up to date before we flush.
|
// Make sure the verison number is up to date before we flush.
|
||||||
this.data.version = Save.SAVE_DATA_VERSION;
|
updateVersionToLatest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDefault():RawSaveData
|
public static function getDefault():RawSaveData
|
||||||
|
@ -503,7 +504,7 @@ class Save
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the score the user achieved for a given song on a given difficulty.
|
* Directly set the score the user achieved for a given song on a given difficulty.
|
||||||
*/
|
*/
|
||||||
public function setSongScore(songId:String, difficultyId:String, score:SaveScoreData):Void
|
public function setSongScore(songId:String, difficultyId:String, score:SaveScoreData):Void
|
||||||
{
|
{
|
||||||
|
@ -518,6 +519,44 @@ class Save
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only replace the ranking data for the song, because the old score is still better.
|
||||||
|
*/
|
||||||
|
public function applySongRank(songId:String, difficultyId:String, newScoreData:SaveScoreData):Void
|
||||||
|
{
|
||||||
|
var newRank = Scoring.calculateRank(newScoreData);
|
||||||
|
if (newScoreData == null || newRank == null) return;
|
||||||
|
|
||||||
|
var song = data.scores.songs.get(songId);
|
||||||
|
if (song == null)
|
||||||
|
{
|
||||||
|
song = [];
|
||||||
|
data.scores.songs.set(songId, song);
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScoreData = song.get(difficultyId);
|
||||||
|
|
||||||
|
var previousRank = Scoring.calculateRank(previousScoreData);
|
||||||
|
|
||||||
|
if (previousScoreData == null || previousRank == null)
|
||||||
|
{
|
||||||
|
// Directly set the highscore.
|
||||||
|
setSongScore(songId, difficultyId, newScoreData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the high score and the high rank separately.
|
||||||
|
var newScore:SaveScoreData =
|
||||||
|
{
|
||||||
|
score: (previousScoreData.score > newScoreData.score) ? previousScoreData.score : newScoreData.score,
|
||||||
|
tallies: (previousRank > newRank) ? previousScoreData.tallies : newScoreData.tallies
|
||||||
|
};
|
||||||
|
|
||||||
|
song.set(difficultyId, newScore);
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the provided score data better than the current high score for the given song?
|
* Is the provided score data better than the current high score for the given song?
|
||||||
* @param songId The song ID to check.
|
* @param songId The song ID to check.
|
||||||
|
@ -543,6 +582,39 @@ class Save
|
||||||
return score.score > currentScore.score;
|
return score.score > currentScore.score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the provided score data better than the current rank for the given song?
|
||||||
|
* @param songId The song ID to check.
|
||||||
|
* @param difficultyId The difficulty to check.
|
||||||
|
* @param score The score to check the rank for.
|
||||||
|
* @return Whether the score's rank is better than the current rank.
|
||||||
|
*/
|
||||||
|
public function isSongHighRank(songId:String, difficultyId:String = 'normal', score:SaveScoreData):Bool
|
||||||
|
{
|
||||||
|
var newScoreRank = Scoring.calculateRank(score);
|
||||||
|
if (newScoreRank == null)
|
||||||
|
{
|
||||||
|
// The provided score is invalid.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var song = data.scores.songs.get(songId);
|
||||||
|
if (song == null)
|
||||||
|
{
|
||||||
|
song = [];
|
||||||
|
data.scores.songs.set(songId, song);
|
||||||
|
}
|
||||||
|
var currentScore = song.get(difficultyId);
|
||||||
|
var currentScoreRank = Scoring.calculateRank(currentScore);
|
||||||
|
if (currentScoreRank == null)
|
||||||
|
{
|
||||||
|
// There is no primary highscore for this song.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newScoreRank > currentScoreRank;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has the provided song been beaten on one of the listed difficulties?
|
* Has the provided song been beaten on one of the listed difficulties?
|
||||||
* @param songId The song ID to check.
|
* @param songId The song ID to check.
|
||||||
|
@ -832,6 +904,29 @@ class Save
|
||||||
return cast legacySave.data;
|
return cast legacySave.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize this Save into a JSON string.
|
||||||
|
* @param pretty Whether the JSON should be big ol string (false),
|
||||||
|
* or formatted with tabs (true)
|
||||||
|
* @return The JSON string.
|
||||||
|
*/
|
||||||
|
public function serialize(pretty:Bool = true):String
|
||||||
|
{
|
||||||
|
var ignoreNullOptionals = true;
|
||||||
|
var writer = new json2object.JsonWriter<RawSaveData>(ignoreNullOptionals);
|
||||||
|
return writer.write(data, pretty ? ' ' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateVersionToLatest():Void
|
||||||
|
{
|
||||||
|
this.data.version = Save.SAVE_DATA_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debug_dumpSave():Void
|
||||||
|
{
|
||||||
|
FileUtil.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtil.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -904,6 +999,9 @@ typedef SaveHighScoresData =
|
||||||
typedef SaveDataMods =
|
typedef SaveDataMods =
|
||||||
{
|
{
|
||||||
var enabledMods:Array<String>;
|
var enabledMods:Array<String>;
|
||||||
|
|
||||||
|
// TODO: Make this not trip up the serializer when debugging.
|
||||||
|
@:jignored
|
||||||
var modOptions:Map<String, Dynamic>;
|
var modOptions:Map<String, Dynamic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1608,7 +1608,19 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].songData;
|
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].songData;
|
||||||
if (daSong != null)
|
if (daSong != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, currentDifficulty);
|
// TODO: Make this actually be the variation you're focused on. We don't need to fetch the song metadata just to calculate it.
|
||||||
|
var targetSong:Song = SongRegistry.instance.fetchEntry(grpCapsules.members[curSelected].songData.songId);
|
||||||
|
if (targetSong == null)
|
||||||
|
{
|
||||||
|
FlxG.log.warn('WARN: could not find song with id (${grpCapsules.members[curSelected].songData.songId})');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty);
|
||||||
|
|
||||||
|
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
|
||||||
|
var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION
|
||||||
|
&& targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty;
|
||||||
|
var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, suffixedDifficulty);
|
||||||
intendedScore = songScore?.score ?? 0;
|
intendedScore = songScore?.score ?? 0;
|
||||||
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
|
||||||
rememberedDifficulty = currentDifficulty;
|
rememberedDifficulty = currentDifficulty;
|
||||||
|
|
|
@ -371,6 +371,33 @@ class MainMenuState extends MusicBeatState
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)
|
||||||
|
{
|
||||||
|
// Give the user a hypothetical overridden score,
|
||||||
|
// and see if we can maintain that golden P rank.
|
||||||
|
funkin.save.Save.instance.setSongScore('tutorial', 'easy',
|
||||||
|
{
|
||||||
|
score: 1234567,
|
||||||
|
tallies:
|
||||||
|
{
|
||||||
|
sick: 0,
|
||||||
|
good: 0,
|
||||||
|
bad: 0,
|
||||||
|
shit: 1,
|
||||||
|
missed: 0,
|
||||||
|
combo: 0,
|
||||||
|
maxCombo: 0,
|
||||||
|
totalNotesHit: 1,
|
||||||
|
totalNotes: 10,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.E)
|
||||||
|
{
|
||||||
|
funkin.save.Save.instance.debug_dumpSave();
|
||||||
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
if (FlxG.sound.music != null && FlxG.sound.music.volume < 0.8)
|
if (FlxG.sound.music != null && FlxG.sound.music.volume < 0.8)
|
||||||
|
|
|
@ -19,6 +19,7 @@ import haxe.ui.containers.dialogs.Dialogs.FileDialogExtensionInfo;
|
||||||
class FileUtil
|
class FileUtil
|
||||||
{
|
{
|
||||||
public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc");
|
public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc");
|
||||||
|
public static final FILE_FILTER_JSON:FileFilter = new FileFilter("JSON Data File (.json)", "*.json");
|
||||||
public static final FILE_FILTER_ZIP:FileFilter = new FileFilter("ZIP Archive (.zip)", "*.zip");
|
public static final FILE_FILTER_ZIP:FileFilter = new FileFilter("ZIP Archive (.zip)", "*.zip");
|
||||||
public static final FILE_FILTER_PNG:FileFilter = new FileFilter("PNG Image (.png)", "*.png");
|
public static final FILE_FILTER_PNG:FileFilter = new FileFilter("PNG Image (.png)", "*.png");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue