From 0294ea0b7923f182d99be8a3906354d3b6394c70 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 1 Mar 2024 08:13:06 -0500 Subject: [PATCH] Implemented FlxAnimate characters into Blazin'. --- assets | 2 +- source/funkin/data/BaseRegistry.hx | 1 + source/funkin/data/stage/StageData.hx | 11 ++ .../graphics/adobeanimate/FlxAtlasSprite.hx | 11 +- source/funkin/modding/events/ScriptEvent.hx | 33 +++++- source/funkin/play/GameOverSubState.hx | 11 ++ source/funkin/play/PauseSubState.hx | 1 - source/funkin/play/PlayState.hx | 103 ++++++++++-------- .../play/character/AnimateAtlasCharacter.hx | 36 +++++- source/funkin/play/character/BaseCharacter.hx | 15 ++- source/funkin/play/character/CharacterData.hx | 7 ++ .../funkin/play/cutscene/dialogue/Speaker.hx | 8 +- source/funkin/play/stage/Bopper.hx | 8 +- source/funkin/play/stage/Stage.hx | 31 +++++- 14 files changed, 205 insertions(+), 73 deletions(-) diff --git a/assets b/assets index 8b914574f..874f7de39 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8b914574fc4724c5fe483f4f9d81081bb1518c12 +Subproject commit 874f7de39ee2dfe2ffe4c02edf701d36f2a393fd diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 62a7eb0f7..2df0c18f0 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -240,6 +240,7 @@ abstract class BaseRegistry & Constructible { + // We enforce that T is Constructible to ensure this is valid. return new T(id); } diff --git a/source/funkin/data/stage/StageData.hx b/source/funkin/data/stage/StageData.hx index cb914007f..22b883c75 100644 --- a/source/funkin/data/stage/StageData.hx +++ b/source/funkin/data/stage/StageData.hx @@ -32,18 +32,21 @@ class StageData bf: { zIndex: 0, + scale: 1, position: [0, 0], cameraOffsets: [-100, -100] }, dad: { zIndex: 0, + scale: 1, position: [0, 0], cameraOffsets: [100, -100] }, gf: { zIndex: 0, + scale: 1, position: [0, 0], cameraOffsets: [0, 0] } @@ -114,6 +117,7 @@ typedef StageDataProp = @:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats) @:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats) @:optional + @:default(Left(1.0)) var scale:haxe.ds.Either>; /** @@ -190,6 +194,13 @@ typedef StageDataCharacter = @:default([0, 0]) var position:Array; + /** + * The scale to render the character at. + */ + @:optional + @:default(1) + var scale:Float; + /** * The camera offsets to apply when focusing on the character on this stage. * @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 2329a2791..90965847e 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -82,6 +82,8 @@ class FlxAtlasSprite extends FlxAnimate * @param id A string ID of the animation to play. * @param restart Whether to restart the animation if it is already playing. * @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing + * @param loop Whether to loop the animation + * NOTE: `loop` and `ignoreOther` are not compatible with each other! */ public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, ?loop:Bool = false):Void { @@ -116,9 +118,14 @@ class FlxAtlasSprite extends FlxAnimate anim.callback = function(_, frame:Int) { if (frame == (anim.getFrameLabel(id).duration - 1) + anim.getFrameLabel(id).index) { - if (loop) playAnimation(id, true, false, true); + if (loop) + { + playAnimation(id, true, false, true); + } else + { onAnimationFinish.dispatch(id); + } } }; @@ -177,6 +184,6 @@ class FlxAtlasSprite extends FlxAnimate { canPlayOtherAnims = true; this.currentAnimation = null; - this.anim.stop(); + this.anim.pause(); } } diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index 5d522e3ae..0d424a281 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -107,18 +107,18 @@ class NoteScriptEvent extends ScriptEvent public var playSound(default, default):Bool; /** - * A multiplier to the health gained or lost from this note. + * The health gained or lost from this note. * This affects both hits and misses. Remember that max health is 2.00. */ - public var healthMulti:Float; + public var healthChange:Float; - public function new(type:ScriptEventType, note:NoteSprite, comboCount:Int = 0, cancelable:Bool = false):Void + public function new(type:ScriptEventType, note:NoteSprite, healthChange:Float, comboCount:Int = 0, cancelable:Bool = false):Void { super(type, cancelable); this.note = note; this.comboCount = comboCount; this.playSound = true; - this.healthMulti = 1.0; + this.healthChange = healthChange; } public override function toString():String @@ -127,6 +127,31 @@ class NoteScriptEvent extends ScriptEvent } } +class HitNoteScriptEvent extends NoteScriptEvent +{ + /** + * The judgement the player received for hitting the note. + */ + public var judgement:String; + + /** + * The score the player received for hitting the note. + */ + public var score:Int; + + public function new(note:NoteSprite, healthChange:Float, score:Int, judgement:String, comboCount:Int = 0):Void + { + super(NOTE_HIT, note, healthChange, comboCount, true); + this.score = score; + this.judgement = judgement; + } + + public override function toString():String + { + return 'HitNoteScriptEvent(note=' + note + ', comboCount=' + comboCount + ', judgement=' + judgement + ', score=' + score + ')'; + } +} + /** * An event that is fired when you press a key with no note present. */ diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index b7e92d10f..d5132e160 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -12,6 +12,7 @@ import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.BaseCharacter; import funkin.play.PlayState; +import funkin.util.MathUtil; import funkin.ui.freeplay.FreeplayState; import funkin.ui.MusicBeatSubState; import funkin.ui.story.StoryMenuState; @@ -82,6 +83,9 @@ class GameOverSubState extends MusicBeatSubState var transparent:Bool; + final CAMERA_ZOOM_DURATION:Float = 0.5; + var targetCameraZoom:Float = 1.0; + public function new(params:GameOverParams) { super(); @@ -142,6 +146,7 @@ class GameOverSubState extends MusicBeatSubState FlxG.camera.target = null; FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01); + targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom(); // // Set up the audio @@ -177,6 +182,9 @@ class GameOverSubState extends MusicBeatSubState } } + // Smoothly lerp the camera + FlxG.camera.zoom = MathUtil.smoothLerp(FlxG.camera.zoom, targetCameraZoom, elapsed, CAMERA_ZOOM_DURATION); + // // Handle user inputs. // @@ -286,6 +294,9 @@ class GameOverSubState extends MusicBeatSubState remove(boyfriend); PlayState.instance.currentStage.addCharacter(boyfriend, BF); + // Snap reset the camera which may have changed because of the player character data. + resetCameraZoom(); + // Close the substate. close(); }); diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 2b705ea9e..03681ce13 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -386,7 +386,6 @@ class PauseSubState extends MusicBeatSubState // Set the position. var targetX = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 20 + 90; var targetY = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 120 + (FlxG.height * 0.48); - trace(targetY); FlxTween.globalManager.cancelTweensOf(text); FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut}); } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f6bb463e7..5cb2e20f3 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -927,7 +927,7 @@ class PlayState extends MusicBeatSubState camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95); } - if (currentStage != null) + if (currentStage != null && currentStage.getBoyfriend() != null) { FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation()); } @@ -1498,17 +1498,17 @@ class PlayState extends MusicBeatSubState if (dad != null) { dad.characterType = CharacterType.DAD; - } - // - // OPPONENT HEALTH ICON - // - iconP2 = new HealthIcon('dad', 1); - iconP2.y = healthBar.y - (iconP2.height / 2); - dad.initHealthIcon(true); // Apply the character ID here - iconP2.zIndex = 850; - add(iconP2); - iconP2.cameras = [camHUD]; + // + // OPPONENT HEALTH ICON + // + iconP2 = new HealthIcon('dad', 1); + iconP2.y = healthBar.y - (iconP2.height / 2); + dad.initHealthIcon(true); // Apply the character ID here + iconP2.zIndex = 850; + add(iconP2); + iconP2.cameras = [camHUD]; + } // // BOYFRIEND @@ -1518,17 +1518,17 @@ class PlayState extends MusicBeatSubState if (boyfriend != null) { boyfriend.characterType = CharacterType.BF; - } - // - // PLAYER HEALTH ICON - // - iconP1 = new HealthIcon('bf', 0); - iconP1.y = healthBar.y - (iconP1.height / 2); - boyfriend.initHealthIcon(false); // Apply the character ID here - iconP1.zIndex = 850; - add(iconP1); - iconP1.cameras = [camHUD]; + // + // PLAYER HEALTH ICON + // + iconP1 = new HealthIcon('bf', 0); + iconP1.y = healthBar.y - (iconP1.height / 2); + boyfriend.initHealthIcon(false); // Apply the character ID here + iconP1.zIndex = 850; + add(iconP1); + iconP1.cameras = [camHUD]; + } // // ADD CHARACTERS TO SCENE @@ -2016,7 +2016,7 @@ class PlayState extends MusicBeatSubState { // Call an event to allow canceling the note miss. // NOTE: This is what handles the character animations! - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, 0, true); + var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, -Constants.HEALTH_MISS_PENALTY, 0, true); dispatchEvent(event); // Calling event.cancelEvent() skips all the other logic! Neat! @@ -2025,7 +2025,7 @@ class PlayState extends MusicBeatSubState // Judge the miss. // NOTE: This is what handles the scoring. trace('Missed note! ${note.noteData}'); - onNoteMiss(note, event.playSound, event.healthMulti); + onNoteMiss(note, event.playSound, event.healthChange); note.handledMiss = true; } @@ -2171,13 +2171,41 @@ class PlayState extends MusicBeatSubState function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void { - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, Highscore.tallies.combo + 1, true); + // Calculate the input latency (do this as late as possible). + // trace('Compare: ${PreciseInputManager.getCurrentTimestamp()} - ${input.timestamp}'); + var inputLatencyNs:Int64 = PreciseInputManager.getCurrentTimestamp() - input.timestamp; + var inputLatencyMs:Float = inputLatencyNs.toFloat() / Constants.NS_PER_MS; + // trace('Input: ${daNote.noteData.getDirectionName()} pressed ${inputLatencyMs}ms ago!'); + + // Get the offset and compensate for input latency. + // Round inward (trim remainder) for consistency. + var noteDiff:Int = Std.int(Conductor.instance.songPosition - note.noteData.time - inputLatencyMs); + + var score = Scoring.scoreNote(noteDiff, PBOT1); + var daRating = Scoring.judgeNote(noteDiff, PBOT1); + + var healthChange = 0.0; + switch (daRating) + { + case 'sick': + healthChange = Constants.HEALTH_SICK_BONUS; + case 'good': + healthChange = Constants.HEALTH_GOOD_BONUS; + case 'bad': + healthChange = Constants.HEALTH_BAD_BONUS; + case 'shit': + healthChange = Constants.HEALTH_SHIT_BONUS; + } + + // Send the note hit event. + var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, Highscore.tallies.combo + 1); dispatchEvent(event); // Calling event.cancelEvent() skips all the other logic! Neat! if (event.eventCanceled) return; - popUpScore(note, input, event.healthMulti); + // Display the combo meter and add the calculation to the score. + popUpScore(note, event.score, event.judgement, event.healthChange); if (note.isHoldNote && note.holdNoteSprite != null) { @@ -2191,11 +2219,11 @@ class PlayState extends MusicBeatSubState * Called when a note leaves the screen and is considered missed by the player. * @param note */ - function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthLossMulti:Float = 1.0):Void + function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthChange:Float):Void { // If we are here, we already CALLED the onNoteMiss script hook! - health -= Constants.HEALTH_MISS_PENALTY * healthLossMulti; + health += healthChange; songScore -= 10; if (!isPracticeMode) @@ -2367,23 +2395,10 @@ class PlayState extends MusicBeatSubState /** * Handles health, score, and rating popups when a note is hit. */ - function popUpScore(daNote:NoteSprite, input:PreciseInputEvent, healthGainMulti:Float = 1.0):Void + function popUpScore(daNote:NoteSprite, score:Int, daRating:String, healthChange:Float):Void { vocals.playerVolume = 1; - // Calculate the input latency (do this as late as possible). - // trace('Compare: ${PreciseInputManager.getCurrentTimestamp()} - ${input.timestamp}'); - var inputLatencyNs:Int64 = PreciseInputManager.getCurrentTimestamp() - input.timestamp; - var inputLatencyMs:Float = inputLatencyNs.toFloat() / Constants.NS_PER_MS; - // trace('Input: ${daNote.noteData.getDirectionName()} pressed ${inputLatencyMs}ms ago!'); - - // Get the offset and compensate for input latency. - // Round inward (trim remainder) for consistency. - var noteDiff:Int = Std.int(Conductor.instance.songPosition - daNote.noteData.time - inputLatencyMs); - - var score = Scoring.scoreNote(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. @@ -2398,22 +2413,20 @@ class PlayState extends MusicBeatSubState { case 'sick': Highscore.tallies.sick += 1; - health += Constants.HEALTH_SICK_BONUS * healthGainMulti; isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK; case 'good': Highscore.tallies.good += 1; - health += Constants.HEALTH_GOOD_BONUS * healthGainMulti; isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK; case 'bad': Highscore.tallies.bad += 1; - health += Constants.HEALTH_BAD_BONUS * healthGainMulti; isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK; case 'shit': Highscore.tallies.shit += 1; - health += Constants.HEALTH_SHIT_BONUS * healthGainMulti; isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK; } + health += healthChange; + if (isComboBreak) { // Break the combo, but don't increment tallies.misses. diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index 418982bef..f180bd457 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -77,6 +77,7 @@ class AnimateAtlasCharacter extends BaseCharacter var atlasSprite:FlxAtlasSprite = loadAtlasSprite(); setSprite(atlasSprite); + loadAnimations(); super.onCreate(event); @@ -86,10 +87,21 @@ class AnimateAtlasCharacter extends BaseCharacter { if ((!canPlayOtherAnims && !ignoreOther)) return; - currentAnimation = name; - var prefix:String = getAnimationData(name).prefix; - if (prefix == null) prefix = name; - this.mainSprite.playAnimation(prefix, restart, ignoreOther); + var correctName = correctAnimationName(name); + if (correctName == null) return; + + var animData = getAnimationData(correctName); + currentAnimation = correctName; + var prefix:String = animData.prefix; + if (prefix == null) prefix = correctName; + var loop:Bool = animData.looped; + + this.mainSprite.playAnimation(prefix, restart, ignoreOther, loop); + } + + public override function hasAnimation(name:String):Bool + { + return getAnimationData(name) != null; } function loadAtlasSprite():FlxAtlasSprite @@ -114,7 +126,11 @@ class AnimateAtlasCharacter extends BaseCharacter } else { + // Make the game hold on the last frame. this.mainSprite.cleanupAnimation(prefix); + + // Fallback to idle! + // playAnimation('idle', true, false); } } @@ -140,14 +156,24 @@ class AnimateAtlasCharacter extends BaseCharacter function loadAnimations():Void { - trace('[ATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}'); + trace('[ATLASCHAR] Attempting to load ${_data.animations.length} animations for ${characterId}'); var animData:Array = cast _data.animations; for (anim in animData) { + // Validate the animation before adding. + var prefix = anim.prefix; + if (!this.mainSprite.hasAnimation(prefix)) + { + FlxG.log.warn('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}'); + continue; + } animations.set(anim.name, anim); + trace('[ATLASCHAR] - Successfully loaded animation ${anim.name} to ${characterId}'); } + + trace('[ATLASCHAR] Loaded ${animations.size()} animations for ${characterId}'); } public override function getCurrentAnimation():String diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 390864148..bfba3715a 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -60,7 +60,7 @@ class BaseCharacter extends Bopper @:allow(funkin.ui.debug.anim.DebugBoundingState) final _data:CharacterData; - final singTimeSec:Float; + final singTimeSteps:Float; /** * The offset between the corner of the sprite and the origin of the sprite (at the character's feet). @@ -180,7 +180,7 @@ class BaseCharacter extends Bopper { this.characterName = _data.name; this.name = _data.name; - this.singTimeSec = _data.singTime; + this.singTimeSteps = _data.singTime; this.globalOffsets = _data.offsets; this.flipX = _data.flipX; } @@ -193,6 +193,11 @@ class BaseCharacter extends Bopper return _data.death?.cameraOffsets ?? [0.0, 0.0]; } + public function getDeathCameraZoom():Float + { + return _data.death?.cameraZoom ?? 1.0; + } + /** * Gets the value of flipX from the character data. * `!getFlipX()` is the direction Boyfriend should face. @@ -367,9 +372,9 @@ class BaseCharacter extends Bopper // This lets you add frames to the end of the sing animation to ease back into the idle! holdTimer += event.elapsed; - var singTimeSec:Float = singTimeSec * (Conductor.instance.beatLengthMs * 0.001); // x beats, to ms. + var singTimeSec:Float = singTimeSteps * (Conductor.instance.stepLengthMs / Constants.MS_PER_SEC); // x beats, to ms. - if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss + if (getCurrentAnimation().endsWith('miss')) singTimeSec *= 2; // makes it feel more awkward when you miss??? // Without this check here, the player character would only play the `sing` animation // for one beat, as opposed to holding it as long as the player is holding the button. @@ -378,7 +383,7 @@ class BaseCharacter extends Bopper FlxG.watch.addQuick('singTimeSec-${characterId}', singTimeSec); if (holdTimer > singTimeSec && shouldStopSinging) { - // trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation'); + trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation'); holdTimer = 0; dance(true); } diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index f3c7d7613..23710274c 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -744,4 +744,11 @@ typedef DeathData = * @default [0, 0] */ var ?cameraOffsets:Array; + + /** + * The amount to zoom the camera by while focusing on this character as they die. + * Value is a multiplier of the default camera zoom for the stage. + * @default 1.0 + */ + var ?cameraZoom:Float; } diff --git a/source/funkin/play/cutscene/dialogue/Speaker.hx b/source/funkin/play/cutscene/dialogue/Speaker.hx index f848d79c8..0d29481c4 100644 --- a/source/funkin/play/cutscene/dialogue/Speaker.hx +++ b/source/funkin/play/cutscene/dialogue/Speaker.hx @@ -218,25 +218,25 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe // If the animation exists, we're good. if (hasAnimation(name)) return name; - trace('[BOPPER] Animation "$name" does not exist!'); + FlxG.log.notice('Speaker tried to play animation "$name" that does not exist, stripping suffixes...'); // Attempt to strip a `-alt` suffix, if it exists. if (name.lastIndexOf('-') != -1) { var correctName = name.substring(0, name.lastIndexOf('-')); - trace('[BOPPER] Attempting to fallback to "$correctName"'); + FlxG.log.notice('Speaker tried to play animation "$name" that does not exist, stripping suffixes...'); return correctAnimationName(correctName); } else { if (name != 'idle') { - trace('[BOPPER] Attempting to fallback to "idle"'); + FlxG.log.warn('Speaker tried to play animation "$name" that does not exist, fallback to idle...'); return correctAnimationName('idle'); } else { - trace('[BOPPER] Failing animation playback.'); + FlxG.log.error('Speaker tried to play animation "idle" that does not exist! This is bad!'); return null; } } diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 1bc0632f9..7974900d8 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -236,25 +236,25 @@ class Bopper extends StageProp implements IPlayStateScriptedClass // If the animation exists, we're good. if (hasAnimation(name)) return name; - trace('[BOPPER] Animation "$name" does not exist!'); + FlxG.log.notice('Bopper tried to play animation "$name" that does not exist, stripping suffixes...'); // Attempt to strip a `-alt` suffix, if it exists. if (name.lastIndexOf('-') != -1) { var correctName = name.substring(0, name.lastIndexOf('-')); - trace('[BOPPER] Attempting to fallback to "$correctName"'); + FlxG.log.notice('Bopper tried to play animation "$name" that does not exist, stripping suffixes...'); return correctAnimationName(correctName); } else { if (name != 'idle') { - trace('[BOPPER] Attempting to fallback to "idle"'); + FlxG.log.warn('Bopper tried to play animation "$name" that does not exist, fallback to idle...'); return correctAnimationName('idle'); } else { - trace('[BOPPER] Failing animation playback.'); + FlxG.log.error('Bopper tried to play animation "idle" that does not exist! This is bad!'); return null; } } diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index c20202245..32c0509a5 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -110,6 +110,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements getBoyfriend().resetCharacter(true); // Reapply the camera offsets. var charData = _data.characters.bf; + getBoyfriend().scale.set(charData.scale, charData.scale); getBoyfriend().cameraFocusPoint.x += charData.cameraOffsets[0]; getBoyfriend().cameraFocusPoint.y += charData.cameraOffsets[1]; } @@ -122,6 +123,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements getGirlfriend().resetCharacter(true); // Reapply the camera offsets. var charData = _data.characters.gf; + getGirlfriend().scale.set(charData.scale, charData.scale); getGirlfriend().cameraFocusPoint.x += charData.cameraOffsets[0]; getGirlfriend().cameraFocusPoint.y += charData.cameraOffsets[1]; } @@ -130,6 +132,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements getDad().resetCharacter(true); // Reapply the camera offsets. var charData = _data.characters.dad; + getDad().scale.set(charData.scale, charData.scale); getDad().cameraFocusPoint.x += charData.cameraOffsets[0]; getDad().cameraFocusPoint.y += charData.cameraOffsets[1]; } @@ -226,7 +229,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements switch (dataProp.scale) { case Left(value): - propSprite.scale.set(value); + propSprite.scale.set(value, value); case Right(values): propSprite.scale.set(values[0], values[1]); @@ -435,6 +438,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements character.originalPosition.y = character.y + character.animOffsets[1]; } + character.scale.set(charData.scale, charData.scale); character.cameraFocusPoint.x += charData.cameraOffsets[0]; character.cameraFocusPoint.y += charData.cameraOffsets[1]; @@ -637,7 +641,30 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements */ public function dispatchToCharacters(event:ScriptEvent):Void { - for (characterId in characters.keys()) + var charList = this.characters.keys().array(); + + // Dad, then BF, then GF, in that order. + + if (charList.contains('dad')) + { + dispatchToCharacter('dad', event); + charList.remove('dad'); + } + + if (charList.contains('bf')) + { + dispatchToCharacter('bf', event); + charList.remove('bf'); + } + + if (charList.contains('gf')) + { + dispatchToCharacter('gf', event); + charList.remove('gf'); + } + + // Then the rest of the characters, if any. + for (characterId in charList) { dispatchToCharacter(characterId, event); }