diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index 0624c2503..ccad14d62 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -138,7 +138,7 @@ class HitNoteScriptEvent extends NoteScriptEvent /** * The score the player received for hitting the note. */ - public var score:Int; + public var score:Float; /** * If the hit causes a combo break. @@ -156,7 +156,7 @@ class HitNoteScriptEvent extends NoteScriptEvent */ public var doesNotesplash:Bool = false; - public function new(note:NoteSprite, healthChange:Float, score:Int, judgement:String, isComboBreak:Bool, comboCount:Int = 0, hitDiff:Float = 0, + public function new(note:NoteSprite, healthChange:Float, score:Float, judgement:String, isComboBreak:Bool, comboCount:Int = 0, hitDiff:Float = 0, doesNotesplash:Bool = false):Void { super(NOTE_HIT, note, healthChange, comboCount, true); @@ -198,7 +198,7 @@ class GhostMissNoteScriptEvent extends ScriptEvent /** * How much score should be lost when this ghost note is pressed. */ - public var scoreChange(default, default):Int; + public var scoreChange(default, default):Float; /** * Whether to play the record scratch sound. @@ -210,7 +210,7 @@ class GhostMissNoteScriptEvent extends ScriptEvent */ public var playAnim(default, default):Bool; - public function new(dir:NoteDirection, hasPossibleNotes:Bool, healthChange:Float, scoreChange:Int):Void + public function new(dir:NoteDirection, hasPossibleNotes:Bool, healthChange:Float, scoreChange:Float):Void { super(NOTE_GHOST_MISS, true); this.dir = dir; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index c662f9918..7aa176868 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -197,7 +197,7 @@ class PlayState extends MusicBeatSubState * The player's current score. * TODO: Move this to its own class. */ - public var songScore:Int = 0; + public var songScore:Float = 0; /** * Start at this point in the song once the countdown is done. @@ -569,6 +569,17 @@ class PlayState extends MusicBeatSubState return FlxG?.sound?.music?.length; } + /** + * The player's current score, as an integer. + * TODO: Move songScore to its own class with this functionality. + */ + var songScoreInt(get, never):Int; + + function get_songScoreInt():Int + { + return Std.int(songScore); + } + // TODO: Refactor or document var generatedMusic:Bool = false; @@ -2126,7 +2137,7 @@ class PlayState extends MusicBeatSubState { // TODO: Add an option for this maybe? var commaSeparated:Bool = true; - scoreText.text = 'Score: ${FlxStringUtil.formatMoney(songScore, false, commaSeparated)}'; + scoreText.text = 'Score: ${FlxStringUtil.formatMoney(songScoreInt, false, commaSeparated)}'; } } @@ -2356,7 +2367,6 @@ class PlayState extends MusicBeatSubState } // Process hold notes on the player's side. - // This handles scoring so we don't need it on the opponent's side. for (holdNote in playerStrumline.holdNotes.members) { if (holdNote == null || !holdNote.alive) continue; @@ -2364,13 +2374,6 @@ class PlayState extends MusicBeatSubState // While the hold note is being hit, and there is length on the hold note... if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0) { - // Grant the player health. - if (!isBotPlayMode) - { - health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed; - songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed); - } - // Make sure the player keeps singing while the note is held by the bot. if (isBotPlayMode && currentStage != null && currentStage.getBoyfriend() != null && currentStage.getBoyfriend().isSinging()) { @@ -2503,6 +2506,20 @@ class PlayState extends MusicBeatSubState } } + public function sustainHit(note:SustainTrail, lastLength:Float):Void + { + // Don't grant sustain bonuses on botplay. + // TODO: Maybe make this scriptable? Performance is a concern, though. + if (isBotPlayMode) return; + + // Calculate song score and health gain based on sustain amount eaten, not by elapsed -- + // This is to avoid inconsistency with sustain scores. Previously handled by the animation handling... + // NOTE: Having PlayState stuff be handled by the strumline is weird. Maybe find a way around that? + var processed = Math.max(Math.min(lastLength, note.fullSustainLength) - Math.max(note.sustainLength, 0), 0) * 0.001; + health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * processed; + songScore += Constants.SCORE_HOLD_BONUS_PER_SECOND * processed; + } + function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void { // Calculate the input latency (do this as late as possible). @@ -2738,7 +2755,7 @@ class PlayState extends MusicBeatSubState /** * Handles applying health, score, and ratings. */ - function applyScore(score:Int, daRating:String, healthChange:Float, isComboBreak:Bool) + function applyScore(score:Float, daRating:String, healthChange:Float, isComboBreak:Bool) { switch (daRating) { @@ -2921,7 +2938,7 @@ class PlayState extends MusicBeatSubState // crackhead double thingie, sets whether was new highscore, AND saves the song! var data = { - score: songScore, + score: songScoreInt, tallies: { sick: Highscore.tallies.sick, @@ -2961,7 +2978,7 @@ class PlayState extends MusicBeatSubState { isNewHighscore = false; - PlayStatePlaylist.campaignScore += songScore; + PlayStatePlaylist.campaignScore += songScoreInt; // Pop the next song ID from the list. // Returns null if the list is empty. @@ -3257,7 +3274,7 @@ class PlayState extends MusicBeatSubState prevScoreData: prevScoreData, scoreData: { - score: PlayStatePlaylist.isStoryMode ? PlayStatePlaylist.campaignScore : songScore, + score: PlayStatePlaylist.isStoryMode ? PlayStatePlaylist.campaignScore : songScoreInt, tallies: { sick: talliesToUse.sick, diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index e894f9c62..67b18d82f 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -47,6 +47,13 @@ class Strumline extends FlxSpriteGroup */ public var isPlayer:Bool; + /** + * Whether this strumline should reward scores on hold. + * Should usually be the same as isPlayer, but modders may want to modify sustain/input behavior. + * Assumes strumline is in PlayState, nothing happens otherwise. + */ + public var rewardSustains:Bool; + /** * Usually you want to keep this as is, but if you are using a Strumline and * playing a sound that has it's own conductor, set this (LatencyState for example) @@ -113,6 +120,7 @@ class Strumline extends FlxSpriteGroup super(); this.isPlayer = isPlayer; + this.rewardSustains = isPlayer; this.noteStyle = noteStyle; this.strumlineNotes = new FlxTypedSpriteGroup(); @@ -468,7 +476,11 @@ class Strumline extends FlxSpriteGroup holdConfirm(holdNote.noteDirection); holdNote.visible = true; - holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - conductorInUse.songPosition; + var lastLength = holdNote.sustainLength; + holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - conductorInUse.songPosition + conductorInUse.inputOffset; + + // Don't reward hitting too early, don't penalize hitting too late + if (rewardSustains) PlayState?.instance.sustainHit(holdNote, lastLength); if (holdNote.sustainLength <= 10) { @@ -649,7 +661,11 @@ class Strumline extends FlxSpriteGroup note.holdNoteSprite.hitNote = true; note.holdNoteSprite.missedNote = false; - note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition; + var lastLength = note.holdNoteSprite.sustainLength; + note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) + - (conductorInUse.songPosition - conductorInUse.inputOffset); + + if (rewardSustains) PlayState?.instance.sustainHit(note.holdNoteSprite, lastLength); } #if FEATURE_GHOST_TAPPING