Hold note covers are in and properly positioned

This commit is contained in:
EliteMasterEric 2023-07-08 01:03:46 -04:00
parent 8e071221ff
commit 796a51325f
8 changed files with 193 additions and 90 deletions

View file

@ -60,6 +60,11 @@ class MusicBeatState extends FlxUIState implements IEventHandler
{
super.update(elapsed);
// Rebindable volume keys.
if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
// Emergency exit button.
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());

View file

@ -1367,13 +1367,14 @@ class PlayState extends MusicBeatState
add(playerStrumline);
add(opponentStrumline);
// Position the player strumline on the right
playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET;
// Position the player strumline on the right half of the screen
playerStrumline.x = FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET; // Classic style
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
playerStrumline.y = PreferencesMenu.getPref('downscroll') ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
playerStrumline.zIndex = 200;
playerStrumline.cameras = [camHUD];
// Position the opponent strumline on the left
// Position the opponent strumline on the left half of the screen
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
opponentStrumline.y = PreferencesMenu.getPref('downscroll') ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
opponentStrumline.zIndex = 100;
@ -1642,13 +1643,18 @@ class PlayState extends MusicBeatState
if (Conductor.songPosition > hitWindowEnd)
{
if (note.hasMissed) continue;
note.tooEarly = false;
note.mayHit = false;
note.hasMissed = true;
if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true;
}
else if (Conductor.songPosition > hitWindowCenter)
{
if (note.hasBeenHit) continue;
// Call an event to allow canceling the note hit.
// NOTE: This is what handles the character animations!
var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, 0, true);
@ -1668,6 +1674,8 @@ class PlayState extends MusicBeatState
}
else if (Conductor.songPosition > hitWindowStart)
{
if (note.hasBeenHit || note.hasMissed) continue;
note.tooEarly = false;
note.mayHit = true;
note.hasMissed = false;
@ -1682,6 +1690,25 @@ class PlayState extends MusicBeatState
}
}
// Process hold notes on the opponent's side.
for (holdNote in opponentStrumline.holdNotes.members)
{
if (holdNote == null || !holdNote.alive) continue;
// While the hold note is being hit, and there is length on the hold note...
if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0)
{
// Make sure the opponent keeps singing while the note is held.
if (currentStage != null && currentStage.getDad() != null && currentStage.getDad().isSinging())
{
currentStage.getDad().holdTimer = 0;
}
}
// TODO: Potential penalty for dropping a hold note?
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; }
}
// Process notes on the player's side.
for (note in playerStrumline.notes.members)
{
@ -1743,11 +1770,11 @@ class PlayState extends MusicBeatState
if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0)
{
// Grant the player health.
trace(holdNote);
trace(holdNote.noteData);
trace(holdNote.sustainLength);
health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed;
}
// TODO: Potential penalty for dropping a hold note?
// if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; }
}
}
@ -1821,6 +1848,7 @@ class PlayState extends MusicBeatState
targetNote.visible = false;
targetNote.kill();
notesInDirection.remove(targetNote);
// Play the strumline animation.
playerStrumline.playConfirm(input.noteDirection);
@ -1942,33 +1970,30 @@ class PlayState extends MusicBeatState
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
{
if (!note.hasBeenHit)
var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, Highscore.tallies.combo + 1, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return;
if (!note.isHoldNote)
{
var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, Highscore.tallies.combo + 1, true);
dispatchEvent(event);
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
if (!note.isHoldNote)
{
Highscore.tallies.combo++;
Highscore.tallies.totalNotesHit++;
if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo;
popUpScore(note, input);
}
playerStrumline.hitNote(note);
if (note.holdNoteSprite != null)
{
playerStrumline.playNoteHoldCover(note.holdNoteSprite);
}
vocals.playerVolume = 1;
popUpScore(note, input);
}
playerStrumline.hitNote(note);
if (note.holdNoteSprite != null)
{
playerStrumline.playNoteHoldCover(note.holdNoteSprite);
}
vocals.playerVolume = 1;
}
/**

View file

@ -359,7 +359,7 @@ class BaseCharacter extends Bopper
}
// Handle character note hold time.
if (getCurrentAnimation().startsWith('sing'))
if (isSinging())
{
// TODO: Rework this code (and all character animations ugh)
// such that the hold time is handled by padding frames,
@ -405,6 +405,11 @@ class BaseCharacter extends Bopper
}
}
public function isSinging():Bool
{
return getCurrentAnimation().startsWith('sing');
}
override function dance(force:Bool = false):Void
{
// Prevent default dancing behavior.
@ -412,13 +417,13 @@ class BaseCharacter extends Bopper
if (!force)
{
if (getCurrentAnimation().startsWith('sing')) return;
if (isSinging()) return;
if (['hey', 'cheer'].contains(getCurrentAnimation()) && !isAnimationFinished()) return;
}
// Prevent dancing while another animation is playing.
if (!force && getCurrentAnimation().startsWith('sing')) return;
if (!force && isSinging()) return;
// Otherwise, fallback to the super dance() method, which handles playing the idle animation.
super.dance();

View file

@ -47,7 +47,7 @@ class NoteHoldCover extends FlxTypedSpriteGroup<FlxSprite>
glow.animation.finishCallback = this.onAnimationFinished;
if (glow.animation.getAnimationList().length < 2)
if (glow.animation.getAnimationList().length < 3)
{
trace('WARNING: NoteHoldCover failed to initialize all animations.');
}
@ -56,35 +56,54 @@ class NoteHoldCover extends FlxTypedSpriteGroup<FlxSprite>
public override function update(elapsed):Void
{
super.update(elapsed);
if (!holdNote.alive && !glow.animation.curAnim.name.startsWith('holdCoverEnd'))
if ((!holdNote.alive || holdNote.missedNote) && !glow.animation.curAnim.name.startsWith('holdCoverEnd'))
{
this.visible = false;
this.kill();
}
else
{
this.visible = true;
// If alive is false, the hold note was held to completion.
// If missedNote is true, the hold note was "dropped".
playEnd();
}
}
public function playStart():Void
{
// glow.animation.play('holdCoverStart${noteDirection.colorName.toTitleCase()}');\
// glow.animation.play('holdCoverStart${noteDirection.colorName.toTitleCase()}');
glow.animation.play('holdCoverStartRed');
}
public function playContinue():Void
{
// glow.animation.play('holdCover${noteDirection.colorName.toTitleCase()}');\
// glow.animation.play('holdCover${noteDirection.colorName.toTitleCase()}');
glow.animation.play('holdCoverRed');
}
public function playEnd():Void
{
// glow.animation.play('holdCoverEnd${noteDirection.colorName.toTitleCase()}');\
// glow.animation.play('holdCoverEnd${noteDirection.colorName.toTitleCase()}');
glow.animation.play('holdCoverEndRed');
}
public override function kill():Void
{
super.kill();
this.visible = false;
if (glow != null) glow.visible = false;
if (sparks != null) sparks.visible = false;
}
public override function revive():Void
{
super.revive();
this.visible = true;
this.alpha = 1.0;
if (glow != null) glow.visible = true;
if (sparks != null) sparks.visible = true;
}
public function onAnimationFinished(animationName:String):Void
{
if (animationName.startsWith('holdCoverStart'))

View file

@ -127,7 +127,7 @@ class NoteSprite extends FlxSprite
if (noteFrames != null && !force) return noteFrames;
noteFrames = Paths.getSparrowAtlas('NOTE_assets');
noteFrames = Paths.getSparrowAtlas('notes');
noteFrames.parent.persist = true;
@ -138,20 +138,10 @@ class NoteSprite extends FlxSprite
{
this.frames = buildNoteFrames();
animation.addByPrefix('greenScroll', 'green instance');
animation.addByPrefix('redScroll', 'red instance');
animation.addByPrefix('blueScroll', 'blue instance');
animation.addByPrefix('purpleScroll', 'purple instance');
animation.addByPrefix('purpleholdend', 'pruple end hold');
animation.addByPrefix('greenholdend', 'green hold end');
animation.addByPrefix('redholdend', 'red hold end');
animation.addByPrefix('blueholdend', 'blue hold end');
animation.addByPrefix('purplehold', 'purple hold piece');
animation.addByPrefix('greenhold', 'green hold piece');
animation.addByPrefix('redhold', 'red hold piece');
animation.addByPrefix('bluehold', 'blue hold piece');
animation.addByPrefix('greenScroll', 'noteUp');
animation.addByPrefix('redScroll', 'noteRight');
animation.addByPrefix('blueScroll', 'noteDown');
animation.addByPrefix('purpleScroll', 'noteLeft');
setGraphicSize(Strumline.STRUMLINE_SIZE);
updateHitbox();

View file

@ -20,7 +20,7 @@ import funkin.util.SortUtil;
class Strumline extends FlxSpriteGroup
{
public static final DIRECTIONS:Array<NoteDirection> = [NoteDirection.LEFT, NoteDirection.DOWN, NoteDirection.UP, NoteDirection.RIGHT];
public static final STRUMLINE_SIZE:Int = 112;
public static final STRUMLINE_SIZE:Int = 104;
public static final NOTE_SPACING:Int = STRUMLINE_SIZE + 8;
// Positional fixes for new strumline graphics.
@ -84,6 +84,8 @@ class Strumline extends FlxSpriteGroup
this.noteSplashes.zIndex = 50;
this.add(this.noteSplashes);
this.refresh();
for (i in 0...KEY_COUNT)
{
var child:StrumlineNote = new StrumlineNote(isPlayer, DIRECTIONS[i]);
@ -102,6 +104,11 @@ class Strumline extends FlxSpriteGroup
this.active = true;
}
public function refresh():Void
{
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
}
override function get_width():Float
{
return KEY_COUNT * Strumline.NOTE_SPACING;
@ -112,8 +119,25 @@ class Strumline extends FlxSpriteGroup
super.update(elapsed);
updateNotes();
#if debug
if (!isPlayer)
{
FlxG.watch.addQuick("strumlineAnim", strumlineNotes.members[3]?.animation?.curAnim?.name);
var curFrame = strumlineNotes.members[3]?.animation?.curAnim?.curFrame;
frameMax = (curFrame > frameMax) ? curFrame : frameMax;
FlxG.watch.addQuick("strumlineFrame", strumlineNotes.members[3]?.animation?.curAnim?.curFrame);
FlxG.watch.addQuick("strumlineFrameMax", frameMax);
animFinishedEver = animFinishedEver || strumlineNotes.members[3]?.animation?.curAnim?.finished;
FlxG.watch.addQuick("strumlineFinished", strumlineNotes.members[3]?.animation?.curAnim?.finished);
FlxG.watch.addQuick("strumlineFinishedEver", animFinishedEver);
}
#end
}
var frameMax:Int;
var animFinishedEver:Bool;
/**
* Get a list of notes within + or - the given strumtime.
* @param strumTime The current time.
@ -253,7 +277,8 @@ class Strumline extends FlxSpriteGroup
// Stopped pressing the hold note.
playStatic(holdNote.noteDirection);
holdNote.missedNote = true;
holdNote.alpha = 0.6;
holdNote.visible = true;
holdNote.alpha = 0.0;
}
}
@ -347,6 +372,15 @@ class Strumline extends FlxSpriteGroup
}
}
}
// Update rendering of pressed keys.
for (dir in DIRECTIONS)
{
if (isKeyHeld(dir) && getByDirection(dir).getCurrentAnimation() == "static")
{
playPress(dir);
}
}
}
public function onBeatHit():Void
@ -405,7 +439,7 @@ class Strumline extends FlxSpriteGroup
if (note.holdNoteSprite != null)
{
note.holdNoteSprite.missedNote = true;
note.holdNoteSprite.alpha = 0.6;
note.holdNoteSprite.visible = false;
}
}
@ -483,12 +517,12 @@ class Strumline extends FlxSpriteGroup
cover.x += getXPos(holdNote.noteDirection);
cover.x += STRUMLINE_SIZE / 2;
cover.x -= cover.width / 2;
// cover.x += INITIAL_OFFSET * 2;
cover.x += -12; // Manual tweaking because fuck.
cover.y = this.y;
cover.y += INITIAL_OFFSET;
cover.y += STRUMLINE_SIZE / 2;
// cover.y -= cover.height / 2;
// cover.y += STRUMLINE_SIZE / 2;
cover.y += -96; // Manual tweaking because fuck.
}
}
@ -525,7 +559,7 @@ class Strumline extends FlxSpriteGroup
holdNoteSprite.sustainLength = note.length;
holdNoteSprite.missedNote = false;
holdNoteSprite.hitNote = false;
holdNoteSprite.visible = true;
holdNoteSprite.alpha = 1.0;
holdNoteSprite.x = this.x;

View file

@ -13,6 +13,10 @@ class StrumlineNote extends FlxSprite
public var direction(default, set):NoteDirection;
var confirmHoldTimer:Float = -1;
static final CONFIRM_HOLD_TIME:Float = 0.1;
public function updatePosition(parentNote:NoteSprite)
{
this.x = parentNote.x;
@ -49,9 +53,12 @@ class StrumlineNote extends FlxSprite
function onAnimationFinished(name:String):Void
{
if (!isPlayer && name.startsWith('confirm'))
// Run a timer before we stop playing the confirm animation.
// On opponent, this prevent issues with hold notes.
// On player, this allows holding the confirm key to fall back to press.
if (name == 'confirm')
{
playStatic();
confirmHoldTimer = 0;
}
}
@ -60,37 +67,49 @@ class StrumlineNote extends FlxSprite
super.update(elapsed);
centerOrigin();
if (confirmHoldTimer >= 0)
{
confirmHoldTimer += elapsed;
// Ensure the opponent stops holding the key after a certain amount of time.
if (confirmHoldTimer >= CONFIRM_HOLD_TIME)
{
confirmHoldTimer = -1;
playStatic();
}
}
}
function setup():Void
{
this.frames = Paths.getSparrowAtlas('StrumlineNotes');
this.frames = Paths.getSparrowAtlas('noteStrumline');
switch (this.direction)
{
case NoteDirection.LEFT:
this.animation.addByIndices('static', 'left confirm', [6, 7], '', 24, false, false, false);
this.animation.addByPrefix('press', 'left press', 24, false, false, false);
this.animation.addByIndices('confirm', 'left confirm', [0, 1, 2, 3], '', 24, false, false, false);
this.animation.addByIndices('confirm-hold', 'left confirm', [2, 3, 4, 5], '', 24, true, false, false);
this.animation.addByPrefix('static', 'staticLeft0', 24, false, false, false);
this.animation.addByPrefix('press', 'pressLeft0', 24, false, false, false);
this.animation.addByPrefix('confirm', 'confirmLeft0', 24, false, false, false);
this.animation.addByPrefix('confirm-hold', 'confirmHoldLeft0', 24, true, false, false);
case NoteDirection.DOWN:
this.animation.addByIndices('static', 'down confirm', [6, 7], '', 24, false, false, false);
this.animation.addByPrefix('press', 'down press', 24, false, false, false);
this.animation.addByIndices('confirm', 'down confirm', [0, 1, 2, 3], '', 24, false, false, false);
this.animation.addByIndices('confirm-hold', 'down confirm', [2, 3, 4, 5], '', 24, true, false, false);
this.animation.addByPrefix('static', 'staticDown0', 24, false, false, false);
this.animation.addByPrefix('press', 'pressDown0', 24, false, false, false);
this.animation.addByPrefix('confirm', 'confirmDown0', 24, false, false, false);
this.animation.addByPrefix('confirm-hold', 'confirmHoldDown0', 24, true, false, false);
case NoteDirection.UP:
this.animation.addByIndices('static', 'up confirm', [6, 7], '', 24, false, false, false);
this.animation.addByPrefix('press', 'up press', 24, false, false, false);
this.animation.addByIndices('confirm', 'up confirm', [0, 1, 2, 3], '', 24, false, false, false);
this.animation.addByIndices('confirm-hold', 'up confirm', [2, 3, 4, 5], '', 24, true, false, false);
this.animation.addByPrefix('static', 'staticUp0', 24, false, false, false);
this.animation.addByPrefix('press', 'pressUp0', 24, false, false, false);
this.animation.addByPrefix('confirm', 'confirmUp0', 24, false, false, false);
this.animation.addByPrefix('confirm-hold', 'confirmHoldUp0', 24, true, false, false);
case NoteDirection.RIGHT:
this.animation.addByIndices('static', 'right confirm', [6, 7], '', 24, false, false, false);
this.animation.addByPrefix('press', 'right press', 24, false, false, false);
this.animation.addByIndices('confirm', 'right confirm', [0, 1, 2, 3], '', 24, false, false, false);
this.animation.addByIndices('confirm-hold', 'right confirm', [2, 3, 4, 5], '', 24, true, false, false);
this.animation.addByPrefix('static', 'staticRight0', 24, false, false, false);
this.animation.addByPrefix('press', 'pressRight0', 24, false, false, false);
this.animation.addByPrefix('confirm', 'confirmRight0', 24, false, false, false);
this.animation.addByPrefix('confirm-hold', 'confirmHoldRight0', 24, true, false, false);
}
this.setGraphicSize(Std.int(Strumline.STRUMLINE_SIZE * 1.55));
@ -133,16 +152,22 @@ class StrumlineNote extends FlxSprite
{
this.active = true;
if (getCurrentAnimation() == "confirm-hold") return;
if (getCurrentAnimation() == "confirm")
if (getCurrentAnimation() == "confirm-hold")
{
return;
}
else if (getCurrentAnimation() == "confirm")
{
if (isAnimationFinished())
{
this.playAnimation('confirm-hold', true, false);
this.confirmHoldTimer = -1;
this.playAnimation('confirm-hold', false, false);
}
return;
}
this.playAnimation('confirm', false, false);
else
{
this.playAnimation('confirm', false, false);
}
}
/**

View file

@ -199,7 +199,7 @@ class Constants
* If true, the player will not receive the ghost miss penalty if there are no notes within the hit window.
* This is the thing people have been begging for forever lolol.
*/
public static final GHOST_TAPPING:Bool = true;
public static final GHOST_TAPPING:Bool = false;
/**
* OTHER