diff --git a/source/funkin/backend/scripting/events/note/NoteHitEvent.hx b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx index 6253c679..c9f8c4c9 100644 --- a/source/funkin/backend/scripting/events/note/NoteHitEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx @@ -118,10 +118,6 @@ final class NoteHitEvent extends CancellableEvent { * The attached healthIcon used distinction for icons amongst others */ public var healthIcon:HealthIcon; - /** - * Whether note hits are judged in the old way or not. - */ - public var legacyJudge(get, set):Bool; /** * Prevents the default sing animation from being played. @@ -200,15 +196,4 @@ final class NoteHitEvent extends CancellableEvent { characters = [char]; return char; } - - private var _explicitLegacyJudge:Null = null; - private inline function get_legacyJudge():Bool { - if (_explicitLegacyJudge != null) - return _explicitLegacyJudge; - return Flags.MOD_API_VERSION == 1; - } - private function set_legacyJudge(value:Bool):Bool { - _explicitLegacyJudge = value; - return value; - } } diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 07b9a453..b085123c 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -15,14 +15,18 @@ import lime.utils.AssetType; */ @:build(funkin.backend.system.macros.FlagMacro.build()) class Flags { + public static var overridenFlags:Map = []; + // -- Codename's Addon Config -- @:bypass public static var addonFlags:Map = []; + public static var CURRENT_API_VERSION:Int = 2; + // -- Codename's Mod Config -- public static var MOD_NAME:String = ""; public static var MOD_DESCRIPTION:String = ""; public static var MOD_AUTHOR:String = ""; - public static var MOD_API_VERSION:Int = 1; + @:lazy public static var MOD_API_VERSION:Null = null; public static var MOD_DOWNLOAD_LINK:String = ""; public static var MOD_DEPENDENCIES:Array = []; @@ -41,12 +45,11 @@ class Flags { @:lazy public static var SAVE_PATH:String = haxe.macro.Compiler.getDefine("SAVE_PATH"); @:lazy public static var SAVE_NAME:String = haxe.macro.Compiler.getDefine("SAVE_NAME"); - public static var CURRENT_API_VERSION:Int = 2; public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber; public static var COMMIT_HASH:String = GitCommitMacro.commitHash; public static var COMMIT_MESSAGE:String = 'Commit $COMMIT_NUMBER ($COMMIT_HASH)'; - @:bypass public static var WINDOW_TITLE_USE_MOD_NAME:Bool = false; + @:lazy public static var WINDOW_TITLE_USE_MOD_NAME:Null = null; @:lazy public static var TITLE:String = Application.current.meta.get('name'); @:lazy public static var VERSION:String = Application.current.meta.get('version'); @@ -125,6 +128,8 @@ class Flags { @:also(funkin.game.PlayState.opponentMode) public static var DEFAULT_OPPONENT_MODE:Bool = false; + @:lazy public static var USE_LEGACY_TIMING:Null = null; + public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500; public static var DEFAULT_NOTE_SCALE:Float = 0.7; #if MODCHARTING_FEATURES @@ -277,8 +282,6 @@ class Flags { @:bypass public static var customFlags:Map = []; public static function loadFromData(flags:Map, data:String) { - WINDOW_TITLE_USE_MOD_NAME = false; - if (!(data.length > 0)) return; var res = IniUtil.parseString(data); @@ -295,26 +298,34 @@ class Flags { else trace('Invalid section $name'); } } - - if (!flags.exists("WINDOW_TITLE_USE_MOD_NAME")) WINDOW_TITLE_USE_MOD_NAME = !flags.exists('TITLE') && flags.exists('MOD_NAME'); - else WINDOW_TITLE_USE_MOD_NAME = parseBool(flags.get("WINDOW_TITLE_USE_MOD_NAME")); - - flags.remove("WINDOW_TITLE_USE_MOD_NAME"); } - public static function loadFromDatas(datas:Array) { + private static function loadPost() { + if (MOD_API_VERSION == null) MOD_API_VERSION = CURRENT_API_VERSION; + if (WINDOW_TITLE_USE_MOD_NAME == null) WINDOW_TITLE_USE_MOD_NAME = !overridenFlags.exists('TITLE') && overridenFlags.exists('MOD_NAME'); + if (USE_LEGACY_TIMING == null) USE_LEGACY_TIMING = MOD_API_VERSION <= 1; + } + + public static function loadFromDatas(datas:Array):Map { var flags:Map = []; - for(data in datas) { - if(data != null) + for (data in datas) { + if (data != null) loadFromData(flags, data); } + loadPost(); return flags; } public static function parseFlags(flags:Map) { - for(name=>value in flags) - if(!parse(name, value)) - customFlags.set(name, value); + var parsed:Bool; + for (name => value in flags) switch (name) { + case "MOD_API_VERSION": + var version = Std.parseInt(value) ?? CURRENT_API_VERSION; + if (version > MOD_API_VERSION || MOD_API_VERSION == null) MOD_API_VERSION = version; + default: + if (!(parsed = parse(name, value))) customFlags.set(name, value); + if (!overridenFlags.exists(name)) overridenFlags.set(name, parsed); + } Options.modchartingHoldSubdivisions = DEFAULT_MODCHART_HOLD_SUBDIVISIONS; } @@ -365,5 +376,6 @@ class Flags { parseFlags(flags); } } + loadPost(); } } \ No newline at end of file diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 9da64a6e..07968a27 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -557,10 +557,11 @@ class PlayState extends MusicBeatState public var noteTypesArray:Array = [null]; /** - * Hit window, in milliseconds. Defaults to 250ms unless changed in options. - * Base game hit window is 175ms. + * Hit window, in milliseconds. A Legacy CNE Hit window configuration, + * Don't use this, it's for mods that still uses the old judgement timing, instead use ratingManager. */ - public var hitWindow:Float = Options.hitWindow; // is calculated in create(), is safeFrames in milliseconds. + public var hitWindow:Float = Options.hitWindow; + @:noCompletion @:dox(hide) private var _legacyRating:Rating = {name: "", window: 0, accuracy: 0, score: 0}; @:noCompletion @:dox(hide) private var _startCountdownCalled:Bool = false; @:noCompletion @:dox(hide) private var _endSongCalled:Bool = false; @@ -1884,48 +1885,48 @@ class PlayState extends MusicBeatState note.wasGoodHit = true; - /** - * CALCULATES RATING - */ - var noteDiff = Math.abs(Conductor.songPosition - note.strumTime); - var daRating:Rating = ratingManager.judgeNote(noteDiff); + var noteDiff = Math.abs(Conductor.songPosition - note.strumTime), rating:Rating; + if (!Flags.USE_LEGACY_TIMING) rating = ratingManager.judgeNote(noteDiff); + else { + (rating = _legacyRating).splash = false; + if (noteDiff > hitWindow * 0.9) { + rating.window = hitWindow; + rating.name = "shit"; + rating.score = 50; + rating.accuracy = 0.25; + } + else if (noteDiff > hitWindow * 0.75) { + rating.window = hitWindow * 0.9; + rating.name = "bad"; + rating.score = 100; + rating.accuracy = 0.45; + } + else if (noteDiff > hitWindow * 0.2) { + rating.window = hitWindow * 0.75; + rating.name = "good"; + rating.score = 200; + rating.accuracy = 0.75; + } + else { + rating.window = hitWindow * 0.2; + rating.name = "sick"; + rating.score = 300; + rating.accuracy = 1; + rating.splash = true; + } + } var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, daRating.score, note.isSustainNote ? null : daRating.accuracy, 0.023, daRating.name, Options.splashesEnabled && !note.isSustainNote && daRating.splash, 0.5, true, 0.7, true, true, iconP1); + event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, rating.score, note.isSustainNote ? null : rating.accuracy, 0.023, rating.name, Options.splashesEnabled && !note.isSustainNote && rating.splash, 0.5, true, 0.7, true, true, iconP1); else - event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating.name, false, 0.5, true, 0.7, true, true, iconP2); + event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, rating.name, false, 0.5, true, 0.7, true, true, iconP2); event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event); strumLine.onHit.dispatch(event); gameAndCharsEvent("onNoteHit", event); if (!event.cancelled) { - if (event.legacyJudge) { - event.rating = 'sick'; - event.score = 300; - event.accuracy = 1; - - if (noteDiff > hitWindow * 0.9) - { - event.rating = 'shit'; - event.score = 50; - event.accuracy = 0.25; - } - else if (noteDiff > hitWindow * 0.75) - { - event.rating = 'bad'; - event.score = 100; - event.accuracy = 0.45; - } - else if (noteDiff > hitWindow * 0.2) - { - event.rating = 'good'; - event.score = 200; - event.accuracy = 0.75; - } - } - if (!note.isSustainNote) { if (event.countScore) songScore += event.score; if (event.accuracy != null) { diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 41acd96d..ee7fefb7 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -216,7 +216,7 @@ class StrumLine extends FlxTypedGroup { if (__updateNote_event.cancelled) return; if (__updateNote_event.__updateHitWindow) { - var hitWindow = PlayState.instance.hitWindow; + var hitWindow = Flags.USE_LEGACY_TIMING ? PlayState.instance.hitWindow : PlayState.instance.ratingManager.lastHitWindow; daNote.canBeHit = (daNote.strumTime > __updateNote_songPos - (hitWindow * daNote.latePressWindow) && daNote.strumTime < __updateNote_songPos + (hitWindow * daNote.earlyPressWindow)); diff --git a/source/funkin/game/scoring/RatingManager.hx b/source/funkin/game/scoring/RatingManager.hx index 2e420ee4..a538e79a 100644 --- a/source/funkin/game/scoring/RatingManager.hx +++ b/source/funkin/game/scoring/RatingManager.hx @@ -12,6 +12,7 @@ class RatingManager { public var hitWindows:StringMap; public var ratingData:Array = []; + public var lastHitWindow:Float = -1; public function new(?preset:WindowPreset):Void { @@ -63,6 +64,8 @@ class RatingManager ? data.window : (hitWindows.exists(name) ? hitWindows.get(name) : -1); + if (window > lastHitWindow) lastHitWindow = window; + var newRating:Rating = { name: name, window: window, diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 1debe9bf..894f00fe 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -38,7 +38,7 @@ class Options public static var devMode:Bool = false; public static var betaUpdates:Bool = false; public static var splashesEnabled:Bool = true; - public static var hitWindow:Float = 250; + @:dox(hide) @:doNotSave public static var hitWindow:Float = 250; // DEPRECATED public static var songOffset:Float = 0; public static var framerate:Int = 120; public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web