1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-03-24 02:49:33 +00:00

Merge pull request #285 from FunkinCrew/feature/play-state-note-polish

Feature/play state note polish
This commit is contained in:
Cameron Taylor 2024-01-16 06:27:30 -05:00 committed by GitHub
commit 4375dfd13a
10 changed files with 139 additions and 45 deletions

2
assets

@ -1 +1 @@
Subproject commit 9b9baa6f2b38dc9bd3354b350084f548e1e16c0f Subproject commit d094640f727a670a348b3579d11af5ff6a2ada3a

View file

@ -21,7 +21,6 @@ abstract Tallies(RawTallies)
bad: 0, bad: 0,
good: 0, good: 0,
sick: 0, sick: 0,
killer: 0,
totalNotes: 0, totalNotes: 0,
totalNotesHit: 0, totalNotesHit: 0,
maxCombo: 0, maxCombo: 0,
@ -43,7 +42,6 @@ typedef RawTallies =
var bad:Int; var bad:Int;
var good:Int; var good:Int;
var sick:Int; var sick:Int;
var killer:Int;
var maxCombo:Int; var maxCombo:Int;
var isNewHighscore:Bool; var isNewHighscore:Bool;

View file

@ -1796,6 +1796,7 @@ class PlayState extends MusicBeatSubState
{ {
if (note == null) continue; if (note == null) continue;
// TODO: Does this properly account for offsets?
var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
var hitWindowCenter = note.strumTime; var hitWindowCenter = note.strumTime;
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS; var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
@ -1864,14 +1865,30 @@ class PlayState extends MusicBeatSubState
} }
} }
// TODO: Potential penalty for dropping a hold note? if (holdNote.missedNote && !holdNote.handledMiss)
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; } {
// When the opponent drops a hold note.
holdNote.handledMiss = true;
// We dropped a hold note.
// Mute vocals and play miss animation, but don't penalize.
vocals.opponentVolume = 0;
currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true);
}
} }
// Process notes on the player's side. // Process notes on the player's side.
for (note in playerStrumline.notes.members) for (note in playerStrumline.notes.members)
{ {
if (note == null || note.hasBeenHit) continue; if (note == null) continue;
if (note.hasBeenHit)
{
note.tooEarly = false;
note.mayHit = false;
note.hasMissed = false;
continue;
}
var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
var hitWindowCenter = note.strumTime; var hitWindowCenter = note.strumTime;
@ -1934,8 +1951,15 @@ class PlayState extends MusicBeatSubState
songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed); songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed);
} }
// TODO: Potential penalty for dropping a hold note? if (holdNote.missedNote && !holdNote.handledMiss)
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; } {
// The player dropped a hold note.
holdNote.handledMiss = true;
// Mute vocals and play miss animation, but don't penalize.
vocals.playerVolume = 0;
currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
}
} }
} }
@ -2027,8 +2051,6 @@ class PlayState extends MusicBeatSubState
trace('Hit note! ${targetNote.noteData}'); trace('Hit note! ${targetNote.noteData}');
goodNoteHit(targetNote, input); goodNoteHit(targetNote, input);
targetNote.visible = false;
targetNote.kill();
notesInDirection.remove(targetNote); notesInDirection.remove(targetNote);
// Play the strumline animation. // Play the strumline animation.
@ -2060,15 +2082,8 @@ class PlayState extends MusicBeatSubState
// Calling event.cancelEvent() skips all the other logic! Neat! // Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return; if (event.eventCanceled) return;
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
popUpScore(note, input); popUpScore(note, input);
playerStrumline.hitNote(note);
if (note.isHoldNote && note.holdNoteSprite != null) if (note.isHoldNote && note.holdNoteSprite != null)
{ {
playerStrumline.playNoteHoldCover(note.holdNoteSprite); playerStrumline.playNoteHoldCover(note.holdNoteSprite);
@ -2084,8 +2099,6 @@ class PlayState extends MusicBeatSubState
function onNoteMiss(note:NoteSprite):Void function onNoteMiss(note:NoteSprite):Void
{ {
// a MISS is when you let a note scroll past you!! // a MISS is when you let a note scroll past you!!
Highscore.tallies.missed++;
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true); var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true);
dispatchEvent(event); dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat! // Calling event.cancelEvent() skips all the other logic! Neat!
@ -2133,8 +2146,11 @@ class PlayState extends MusicBeatSubState
} }
vocals.playerVolume = 0; vocals.playerVolume = 0;
Highscore.tallies.missed++;
if (Highscore.tallies.combo != 0) if (Highscore.tallies.combo != 0)
{ {
// Break the combo.
Highscore.tallies.combo = comboPopUps.displayCombo(0); Highscore.tallies.combo = comboPopUps.displayCombo(0);
} }
@ -2282,29 +2298,51 @@ class PlayState extends MusicBeatSubState
var score = Scoring.scoreNote(noteDiff, PBOT1); var score = Scoring.scoreNote(noteDiff, PBOT1);
var daRating = Scoring.judgeNote(noteDiff, PBOT1); var daRating = Scoring.judgeNote(noteDiff, PBOT1);
if (daRating == 'miss')
{
// If daRating is 'miss', that means we made a mistake and should not continue.
trace('[WARNING] popUpScore judged a note as a miss!');
// TODO: Remove this.
comboPopUps.displayRating('miss');
return;
}
var isComboBreak = false;
switch (daRating) switch (daRating)
{ {
case 'killer':
Highscore.tallies.killer += 1;
health += Constants.HEALTH_KILLER_BONUS;
case 'sick': case 'sick':
Highscore.tallies.sick += 1; Highscore.tallies.sick += 1;
health += Constants.HEALTH_SICK_BONUS; health += Constants.HEALTH_SICK_BONUS;
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
case 'good': case 'good':
Highscore.tallies.good += 1; Highscore.tallies.good += 1;
health += Constants.HEALTH_GOOD_BONUS; health += Constants.HEALTH_GOOD_BONUS;
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
case 'bad': case 'bad':
Highscore.tallies.bad += 1; Highscore.tallies.bad += 1;
health += Constants.HEALTH_BAD_BONUS; health += Constants.HEALTH_BAD_BONUS;
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
case 'shit': case 'shit':
Highscore.tallies.shit += 1; Highscore.tallies.shit += 1;
health += Constants.HEALTH_SHIT_BONUS; health += Constants.HEALTH_SHIT_BONUS;
case 'miss': isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
Highscore.tallies.missed += 1;
health -= Constants.HEALTH_MISS_PENALTY;
} }
if (daRating == "sick" || daRating == "killer") if (isComboBreak)
{
// Break the combo, but don't increment tallies.misses.
Highscore.tallies.combo = comboPopUps.displayCombo(0);
}
else
{
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
}
playerStrumline.hitNote(daNote, !isComboBreak);
if (daRating == "sick")
{ {
playerStrumline.playNoteSplash(daNote.noteData.getDirection()); playerStrumline.playNoteSplash(daNote.noteData.getDirection());
} }
@ -2440,7 +2478,6 @@ class PlayState extends MusicBeatSubState
score: songScore, score: songScore,
tallies: tallies:
{ {
killer: Highscore.tallies.killer,
sick: Highscore.tallies.sick, sick: Highscore.tallies.sick,
good: Highscore.tallies.good, good: Highscore.tallies.good,
bad: Highscore.tallies.bad, bad: Highscore.tallies.bad,
@ -2491,7 +2528,6 @@ class PlayState extends MusicBeatSubState
tallies: tallies:
{ {
// TODO: Sum up the values for the whole level! // TODO: Sum up the values for the whole level!
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,

View file

@ -4,6 +4,7 @@ import funkin.data.song.SongData.SongNoteData;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxAtlasFrames;
import flixel.FlxSprite; import flixel.FlxSprite;
import funkin.graphics.shaders.HSVShader;
class NoteSprite extends FlxSprite class NoteSprite extends FlxSprite
{ {
@ -11,6 +12,8 @@ class NoteSprite extends FlxSprite
public var holdNoteSprite:SustainTrail; public var holdNoteSprite:SustainTrail;
var hsvShader:HSVShader;
/** /**
* The time at which the note should be hit, in milliseconds. * The time at which the note should be hit, in milliseconds.
*/ */
@ -102,6 +105,8 @@ class NoteSprite extends FlxSprite
this.strumTime = strumTime; this.strumTime = strumTime;
this.direction = direction; this.direction = direction;
this.hsvShader = new HSVShader();
if (this.strumTime < 0) this.strumTime = 0; if (this.strumTime < 0) this.strumTime = 0;
setupNoteGraphic(noteStyle); setupNoteGraphic(noteStyle);
@ -116,16 +121,57 @@ class NoteSprite extends FlxSprite
setGraphicSize(Strumline.STRUMLINE_SIZE); setGraphicSize(Strumline.STRUMLINE_SIZE);
updateHitbox(); updateHitbox();
this.shader = hsvShader;
}
#if FLX_DEBUG
/**
* Call this to override how debug bounding boxes are drawn for this sprite.
*/
public override function drawDebugOnCamera(camera:flixel.FlxCamera):Void
{
if (!camera.visible || !camera.exists || !isOnScreen(camera)) return;
var gfx = beginDrawDebug(camera);
var rect = getBoundingBox(camera);
trace('note sprite bounding box: ' + rect.x + ', ' + rect.y + ', ' + rect.width + ', ' + rect.height);
gfx.lineStyle(2, 0xFFFF66FF, 0.5); // thickness, color, alpha
gfx.drawRect(rect.x, rect.y, rect.width, rect.height);
gfx.lineStyle(2, 0xFFFFFF66, 0.5); // thickness, color, alpha
gfx.drawRect(rect.x, rect.y + rect.height / 2, rect.width, 1);
endDrawDebug(camera);
}
#end
public function desaturate():Void
{
this.hsvShader.saturation = 0.2;
}
public function setHue(hue:Float):Void
{
this.hsvShader.hue = hue;
} }
public override function revive():Void public override function revive():Void
{ {
super.revive(); super.revive();
this.visible = true;
this.alpha = 1.0;
this.active = false; this.active = false;
this.tooEarly = false; this.tooEarly = false;
this.hasBeenHit = false; this.hasBeenHit = false;
this.mayHit = false; this.mayHit = false;
this.hasMissed = false; this.hasMissed = false;
this.hsvShader.hue = 1.0;
this.hsvShader.saturation = 1.0;
this.hsvShader.value = 1.0;
} }
public override function kill():Void public override function kill():Void

View file

@ -316,7 +316,7 @@ class Strumline extends FlxSpriteGroup
// Update rendering of notes. // Update rendering of notes.
for (note in notes.members) for (note in notes.members)
{ {
if (note == null || !note.alive || note.hasBeenHit) continue; if (note == null || !note.alive) continue;
var vwoosh:Bool = note.holdNoteSprite == null; var vwoosh:Bool = note.holdNoteSprite == null;
// Set the note's position. // Set the note's position.
@ -343,7 +343,7 @@ class Strumline extends FlxSpriteGroup
playStatic(holdNote.noteDirection); playStatic(holdNote.noteDirection);
holdNote.missedNote = true; holdNote.missedNote = true;
holdNote.visible = true; holdNote.visible = true;
holdNote.alpha = 0.0; holdNote.alpha = 0.0; // Completely hide the dropped hold note.
} }
} }
@ -384,10 +384,6 @@ class Strumline extends FlxSpriteGroup
var yOffset:Float = (holdNote.fullSustainLength - holdNote.sustainLength) * Constants.PIXELS_PER_MS; var yOffset:Float = (holdNote.fullSustainLength - holdNote.sustainLength) * Constants.PIXELS_PER_MS;
trace('yOffset: ' + yOffset);
trace('holdNote.fullSustainLength: ' + holdNote.fullSustainLength);
trace('holdNote.sustainLength: ' + holdNote.sustainLength);
var vwoosh:Bool = false; var vwoosh:Bool = false;
if (Preferences.downscroll) if (Preferences.downscroll)
@ -531,11 +527,24 @@ class Strumline extends FlxSpriteGroup
this.noteData.insertionSort(compareNoteData.bind(FlxSort.ASCENDING)); this.noteData.insertionSort(compareNoteData.bind(FlxSort.ASCENDING));
} }
public function hitNote(note:NoteSprite):Void /**
* @param note The note to hit.
* @param removeNote True to remove the note immediately, false to make it transparent and let it move offscreen.
*/
public function hitNote(note:NoteSprite, removeNote:Bool = true):Void
{ {
playConfirm(note.direction); playConfirm(note.direction);
note.hasBeenHit = true; note.hasBeenHit = true;
killNote(note);
if (removeNote)
{
killNote(note);
}
else
{
note.alpha = 0.5;
note.desaturate();
}
if (note.holdNoteSprite != null) if (note.holdNoteSprite != null)
{ {

View file

@ -47,6 +47,11 @@ class SustainTrail extends FlxSprite
*/ */
public var missedNote:Bool = false; public var missedNote:Bool = false;
/**
* Set to `true` after handling additional logic for missing notes.
*/
public var handledMiss:Bool = false;
// maybe BlendMode.MULTIPLY if missed somehow, drawTriangles does not support! // maybe BlendMode.MULTIPLY if missed somehow, drawTriangles does not support!
/** /**
@ -321,6 +326,7 @@ class SustainTrail extends FlxSprite
hitNote = false; hitNote = false;
missedNote = false; missedNote = false;
handledMiss = false;
} }
override public function destroy():Void override public function destroy():Void

View file

@ -158,8 +158,8 @@ class Scoring
return switch (absTiming) return switch (absTiming)
{ {
case(_ < PBOT1_KILLER_THRESHOLD) => true: // case(_ < PBOT1_KILLER_THRESHOLD) => true:
'killer'; // 'killer';
case(_ < PBOT1_SICK_THRESHOLD) => true: case(_ < PBOT1_SICK_THRESHOLD) => true:
'sick'; 'sick';
case(_ < PBOT1_GOOD_THRESHOLD) => true: case(_ < PBOT1_GOOD_THRESHOLD) => true:

View file

@ -792,7 +792,6 @@ typedef SaveScoreData =
typedef SaveScoreTallyData = typedef SaveScoreTallyData =
{ {
var killer:Int;
var sick:Int; var sick:Int;
var good:Int; var good:Int;
var bad:Int; var bad:Int;

View file

@ -120,7 +120,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}-easy') ?? 0.0, accuracy: inputSaveData.songCompletion.get('${levelId}-easy') ?? 0.0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,
@ -140,7 +139,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}') ?? 0.0, accuracy: inputSaveData.songCompletion.get('${levelId}') ?? 0.0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,
@ -160,7 +158,6 @@ class SaveDataMigrator
accuracy: inputSaveData.songCompletion.get('${levelId}-hard') ?? 0.0, accuracy: inputSaveData.songCompletion.get('${levelId}-hard') ?? 0.0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,
@ -183,7 +180,6 @@ class SaveDataMigrator
accuracy: 0, accuracy: 0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,
@ -209,7 +205,6 @@ class SaveDataMigrator
accuracy: 0, accuracy: 0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,
@ -235,7 +230,6 @@ class SaveDataMigrator
accuracy: 0, accuracy: 0,
tallies: tallies:
{ {
killer: 0,
sick: 0, sick: 0,
good: 0, good: 0,
bad: 0, bad: 0,

View file

@ -363,6 +363,12 @@ class Constants
*/ */
public static final SCORE_HOLD_BONUS_PER_SECOND:Float = 250.0; public static final SCORE_HOLD_BONUS_PER_SECOND:Float = 250.0;
public static final JUDGEMENT_KILLER_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_SICK_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_GOOD_COMBO_BREAK:Bool = false;
public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true;
public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true;
/** /**
* FILE EXTENSIONS * FILE EXTENSIONS
*/ */