mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-01-08 05:07:10 +00:00
Implemented FlxAnimate characters into Blazin'.
This commit is contained in:
parent
684eb919b5
commit
0294ea0b79
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 8b914574fc4724c5fe483f4f9d81081bb1518c12
|
Subproject commit 874f7de39ee2dfe2ffe4c02edf701d36f2a393fd
|
|
@ -240,6 +240,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
||||||
*/
|
*/
|
||||||
function createEntry(id:String):Null<T>
|
function createEntry(id:String):Null<T>
|
||||||
{
|
{
|
||||||
|
// We enforce that T is Constructible to ensure this is valid.
|
||||||
return new T(id);
|
return new T(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,18 +32,21 @@ class StageData
|
||||||
bf:
|
bf:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [-100, -100]
|
cameraOffsets: [-100, -100]
|
||||||
},
|
},
|
||||||
dad:
|
dad:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [100, -100]
|
cameraOffsets: [100, -100]
|
||||||
},
|
},
|
||||||
gf:
|
gf:
|
||||||
{
|
{
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
|
scale: 1,
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
cameraOffsets: [0, 0]
|
cameraOffsets: [0, 0]
|
||||||
}
|
}
|
||||||
|
@ -114,6 +117,7 @@ typedef StageDataProp =
|
||||||
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
||||||
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
||||||
@:optional
|
@:optional
|
||||||
|
@:default(Left(1.0))
|
||||||
var scale:haxe.ds.Either<Float, Array<Float>>;
|
var scale:haxe.ds.Either<Float, Array<Float>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,6 +194,13 @@ typedef StageDataCharacter =
|
||||||
@:default([0, 0])
|
@:default([0, 0])
|
||||||
var position:Array<Float>;
|
var position:Array<Float>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* 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
|
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
|
||||||
|
|
|
@ -82,6 +82,8 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
* @param id A string ID of the animation to play.
|
* @param id A string ID of the animation to play.
|
||||||
* @param restart Whether to restart the animation if it is already playing.
|
* @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 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
|
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) {
|
anim.callback = function(_, frame:Int) {
|
||||||
if (frame == (anim.getFrameLabel(id).duration - 1) + anim.getFrameLabel(id).index)
|
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
|
else
|
||||||
|
{
|
||||||
onAnimationFinish.dispatch(id);
|
onAnimationFinish.dispatch(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,6 +184,6 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
{
|
{
|
||||||
canPlayOtherAnims = true;
|
canPlayOtherAnims = true;
|
||||||
this.currentAnimation = null;
|
this.currentAnimation = null;
|
||||||
this.anim.stop();
|
this.anim.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,18 +107,18 @@ class NoteScriptEvent extends ScriptEvent
|
||||||
public var playSound(default, default):Bool;
|
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.
|
* 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);
|
super(type, cancelable);
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.comboCount = comboCount;
|
this.comboCount = comboCount;
|
||||||
this.playSound = true;
|
this.playSound = true;
|
||||||
this.healthMulti = 1.0;
|
this.healthChange = healthChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function toString():String
|
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.
|
* An event that is fired when you press a key with no note present.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,6 +12,7 @@ import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.character.BaseCharacter;
|
import funkin.play.character.BaseCharacter;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
|
import funkin.util.MathUtil;
|
||||||
import funkin.ui.freeplay.FreeplayState;
|
import funkin.ui.freeplay.FreeplayState;
|
||||||
import funkin.ui.MusicBeatSubState;
|
import funkin.ui.MusicBeatSubState;
|
||||||
import funkin.ui.story.StoryMenuState;
|
import funkin.ui.story.StoryMenuState;
|
||||||
|
@ -82,6 +83,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
var transparent:Bool;
|
var transparent:Bool;
|
||||||
|
|
||||||
|
final CAMERA_ZOOM_DURATION:Float = 0.5;
|
||||||
|
var targetCameraZoom:Float = 1.0;
|
||||||
|
|
||||||
public function new(params:GameOverParams)
|
public function new(params:GameOverParams)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -142,6 +146,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
FlxG.camera.target = null;
|
FlxG.camera.target = null;
|
||||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01);
|
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01);
|
||||||
|
targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Set up the audio
|
// 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.
|
// Handle user inputs.
|
||||||
//
|
//
|
||||||
|
@ -286,6 +294,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
remove(boyfriend);
|
remove(boyfriend);
|
||||||
PlayState.instance.currentStage.addCharacter(boyfriend, BF);
|
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 the substate.
|
||||||
close();
|
close();
|
||||||
});
|
});
|
||||||
|
|
|
@ -386,7 +386,6 @@ class PauseSubState extends MusicBeatSubState
|
||||||
// Set the position.
|
// Set the position.
|
||||||
var targetX = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 20 + 90;
|
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);
|
var targetY = FlxMath.remapToRange((entryIndex - currentEntry), 0, 1, 0, 1.3) * 120 + (FlxG.height * 0.48);
|
||||||
trace(targetY);
|
|
||||||
FlxTween.globalManager.cancelTweensOf(text);
|
FlxTween.globalManager.cancelTweensOf(text);
|
||||||
FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut});
|
FlxTween.tween(text, {x: targetX, y: targetY}, 0.33, {ease: FlxEase.quartOut});
|
||||||
}
|
}
|
||||||
|
|
|
@ -927,7 +927,7 @@ class PlayState extends MusicBeatSubState
|
||||||
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
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());
|
FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation());
|
||||||
}
|
}
|
||||||
|
@ -1498,17 +1498,17 @@ class PlayState extends MusicBeatSubState
|
||||||
if (dad != null)
|
if (dad != null)
|
||||||
{
|
{
|
||||||
dad.characterType = CharacterType.DAD;
|
dad.characterType = CharacterType.DAD;
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// OPPONENT HEALTH ICON
|
// OPPONENT HEALTH ICON
|
||||||
//
|
//
|
||||||
iconP2 = new HealthIcon('dad', 1);
|
iconP2 = new HealthIcon('dad', 1);
|
||||||
iconP2.y = healthBar.y - (iconP2.height / 2);
|
iconP2.y = healthBar.y - (iconP2.height / 2);
|
||||||
dad.initHealthIcon(true); // Apply the character ID here
|
dad.initHealthIcon(true); // Apply the character ID here
|
||||||
iconP2.zIndex = 850;
|
iconP2.zIndex = 850;
|
||||||
add(iconP2);
|
add(iconP2);
|
||||||
iconP2.cameras = [camHUD];
|
iconP2.cameras = [camHUD];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// BOYFRIEND
|
// BOYFRIEND
|
||||||
|
@ -1518,17 +1518,17 @@ class PlayState extends MusicBeatSubState
|
||||||
if (boyfriend != null)
|
if (boyfriend != null)
|
||||||
{
|
{
|
||||||
boyfriend.characterType = CharacterType.BF;
|
boyfriend.characterType = CharacterType.BF;
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// PLAYER HEALTH ICON
|
// PLAYER HEALTH ICON
|
||||||
//
|
//
|
||||||
iconP1 = new HealthIcon('bf', 0);
|
iconP1 = new HealthIcon('bf', 0);
|
||||||
iconP1.y = healthBar.y - (iconP1.height / 2);
|
iconP1.y = healthBar.y - (iconP1.height / 2);
|
||||||
boyfriend.initHealthIcon(false); // Apply the character ID here
|
boyfriend.initHealthIcon(false); // Apply the character ID here
|
||||||
iconP1.zIndex = 850;
|
iconP1.zIndex = 850;
|
||||||
add(iconP1);
|
add(iconP1);
|
||||||
iconP1.cameras = [camHUD];
|
iconP1.cameras = [camHUD];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// ADD CHARACTERS TO SCENE
|
// ADD CHARACTERS TO SCENE
|
||||||
|
@ -2016,7 +2016,7 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
// Call an event to allow canceling the note miss.
|
// Call an event to allow canceling the note miss.
|
||||||
// NOTE: This is what handles the character animations!
|
// 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);
|
dispatchEvent(event);
|
||||||
|
|
||||||
// Calling event.cancelEvent() skips all the other logic! Neat!
|
// Calling event.cancelEvent() skips all the other logic! Neat!
|
||||||
|
@ -2025,7 +2025,7 @@ class PlayState extends MusicBeatSubState
|
||||||
// Judge the miss.
|
// Judge the miss.
|
||||||
// NOTE: This is what handles the scoring.
|
// NOTE: This is what handles the scoring.
|
||||||
trace('Missed note! ${note.noteData}');
|
trace('Missed note! ${note.noteData}');
|
||||||
onNoteMiss(note, event.playSound, event.healthMulti);
|
onNoteMiss(note, event.playSound, event.healthChange);
|
||||||
|
|
||||||
note.handledMiss = true;
|
note.handledMiss = true;
|
||||||
}
|
}
|
||||||
|
@ -2171,13 +2171,41 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
|
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);
|
dispatchEvent(event);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
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)
|
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.
|
* Called when a note leaves the screen and is considered missed by the player.
|
||||||
* @param note
|
* @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!
|
// If we are here, we already CALLED the onNoteMiss script hook!
|
||||||
|
|
||||||
health -= Constants.HEALTH_MISS_PENALTY * healthLossMulti;
|
health += healthChange;
|
||||||
songScore -= 10;
|
songScore -= 10;
|
||||||
|
|
||||||
if (!isPracticeMode)
|
if (!isPracticeMode)
|
||||||
|
@ -2367,23 +2395,10 @@ class PlayState extends MusicBeatSubState
|
||||||
/**
|
/**
|
||||||
* Handles health, score, and rating popups when a note is hit.
|
* 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;
|
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 == 'miss')
|
||||||
{
|
{
|
||||||
// If daRating is 'miss', that means we made a mistake and should not continue.
|
// If daRating is 'miss', that means we made a mistake and should not continue.
|
||||||
|
@ -2398,22 +2413,20 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
case 'sick':
|
case 'sick':
|
||||||
Highscore.tallies.sick += 1;
|
Highscore.tallies.sick += 1;
|
||||||
health += Constants.HEALTH_SICK_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK;
|
||||||
case 'good':
|
case 'good':
|
||||||
Highscore.tallies.good += 1;
|
Highscore.tallies.good += 1;
|
||||||
health += Constants.HEALTH_GOOD_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK;
|
||||||
case 'bad':
|
case 'bad':
|
||||||
Highscore.tallies.bad += 1;
|
Highscore.tallies.bad += 1;
|
||||||
health += Constants.HEALTH_BAD_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK;
|
||||||
case 'shit':
|
case 'shit':
|
||||||
Highscore.tallies.shit += 1;
|
Highscore.tallies.shit += 1;
|
||||||
health += Constants.HEALTH_SHIT_BONUS * healthGainMulti;
|
|
||||||
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
health += healthChange;
|
||||||
|
|
||||||
if (isComboBreak)
|
if (isComboBreak)
|
||||||
{
|
{
|
||||||
// Break the combo, but don't increment tallies.misses.
|
// Break the combo, but don't increment tallies.misses.
|
||||||
|
|
|
@ -77,6 +77,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
|
|
||||||
var atlasSprite:FlxAtlasSprite = loadAtlasSprite();
|
var atlasSprite:FlxAtlasSprite = loadAtlasSprite();
|
||||||
setSprite(atlasSprite);
|
setSprite(atlasSprite);
|
||||||
|
|
||||||
loadAnimations();
|
loadAnimations();
|
||||||
|
|
||||||
super.onCreate(event);
|
super.onCreate(event);
|
||||||
|
@ -86,10 +87,21 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
{
|
{
|
||||||
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
if ((!canPlayOtherAnims && !ignoreOther)) return;
|
||||||
|
|
||||||
currentAnimation = name;
|
var correctName = correctAnimationName(name);
|
||||||
var prefix:String = getAnimationData(name).prefix;
|
if (correctName == null) return;
|
||||||
if (prefix == null) prefix = name;
|
|
||||||
this.mainSprite.playAnimation(prefix, restart, ignoreOther);
|
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
|
function loadAtlasSprite():FlxAtlasSprite
|
||||||
|
@ -114,7 +126,11 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Make the game hold on the last frame.
|
||||||
this.mainSprite.cleanupAnimation(prefix);
|
this.mainSprite.cleanupAnimation(prefix);
|
||||||
|
|
||||||
|
// Fallback to idle!
|
||||||
|
// playAnimation('idle', true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,14 +156,24 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||||
|
|
||||||
function loadAnimations():Void
|
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<AnimateAtlasAnimation> = cast _data.animations;
|
var animData:Array<AnimateAtlasAnimation> = cast _data.animations;
|
||||||
|
|
||||||
for (anim in animData)
|
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);
|
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
|
public override function getCurrentAnimation():String
|
||||||
|
|
|
@ -60,7 +60,7 @@ class BaseCharacter extends Bopper
|
||||||
|
|
||||||
@:allow(funkin.ui.debug.anim.DebugBoundingState)
|
@:allow(funkin.ui.debug.anim.DebugBoundingState)
|
||||||
final _data:CharacterData;
|
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).
|
* 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.characterName = _data.name;
|
||||||
this.name = _data.name;
|
this.name = _data.name;
|
||||||
this.singTimeSec = _data.singTime;
|
this.singTimeSteps = _data.singTime;
|
||||||
this.globalOffsets = _data.offsets;
|
this.globalOffsets = _data.offsets;
|
||||||
this.flipX = _data.flipX;
|
this.flipX = _data.flipX;
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,11 @@ class BaseCharacter extends Bopper
|
||||||
return _data.death?.cameraOffsets ?? [0.0, 0.0];
|
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.
|
* Gets the value of flipX from the character data.
|
||||||
* `!getFlipX()` is the direction Boyfriend should face.
|
* `!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!
|
// This lets you add frames to the end of the sing animation to ease back into the idle!
|
||||||
|
|
||||||
holdTimer += event.elapsed;
|
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
|
// 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.
|
// 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);
|
FlxG.watch.addQuick('singTimeSec-${characterId}', singTimeSec);
|
||||||
if (holdTimer > singTimeSec && shouldStopSinging)
|
if (holdTimer > singTimeSec && shouldStopSinging)
|
||||||
{
|
{
|
||||||
// trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation');
|
trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation');
|
||||||
holdTimer = 0;
|
holdTimer = 0;
|
||||||
dance(true);
|
dance(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -744,4 +744,11 @@ typedef DeathData =
|
||||||
* @default [0, 0]
|
* @default [0, 0]
|
||||||
*/
|
*/
|
||||||
var ?cameraOffsets:Array<Float>;
|
var ?cameraOffsets:Array<Float>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,25 +218,25 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||||
// If the animation exists, we're good.
|
// If the animation exists, we're good.
|
||||||
if (hasAnimation(name)) return name;
|
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.
|
// Attempt to strip a `-alt` suffix, if it exists.
|
||||||
if (name.lastIndexOf('-') != -1)
|
if (name.lastIndexOf('-') != -1)
|
||||||
{
|
{
|
||||||
var correctName = name.substring(0, name.lastIndexOf('-'));
|
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);
|
return correctAnimationName(correctName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (name != 'idle')
|
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');
|
return correctAnimationName('idle');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Failing animation playback.');
|
FlxG.log.error('Speaker tried to play animation "idle" that does not exist! This is bad!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,25 +236,25 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
||||||
// If the animation exists, we're good.
|
// If the animation exists, we're good.
|
||||||
if (hasAnimation(name)) return name;
|
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.
|
// Attempt to strip a `-alt` suffix, if it exists.
|
||||||
if (name.lastIndexOf('-') != -1)
|
if (name.lastIndexOf('-') != -1)
|
||||||
{
|
{
|
||||||
var correctName = name.substring(0, name.lastIndexOf('-'));
|
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);
|
return correctAnimationName(correctName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (name != 'idle')
|
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');
|
return correctAnimationName('idle');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[BOPPER] Failing animation playback.');
|
FlxG.log.error('Bopper tried to play animation "idle" that does not exist! This is bad!');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getBoyfriend().resetCharacter(true);
|
getBoyfriend().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.bf;
|
var charData = _data.characters.bf;
|
||||||
|
getBoyfriend().scale.set(charData.scale, charData.scale);
|
||||||
getBoyfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getBoyfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getBoyfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getBoyfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -122,6 +123,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getGirlfriend().resetCharacter(true);
|
getGirlfriend().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.gf;
|
var charData = _data.characters.gf;
|
||||||
|
getGirlfriend().scale.set(charData.scale, charData.scale);
|
||||||
getGirlfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getGirlfriend().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getGirlfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getGirlfriend().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -130,6 +132,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
getDad().resetCharacter(true);
|
getDad().resetCharacter(true);
|
||||||
// Reapply the camera offsets.
|
// Reapply the camera offsets.
|
||||||
var charData = _data.characters.dad;
|
var charData = _data.characters.dad;
|
||||||
|
getDad().scale.set(charData.scale, charData.scale);
|
||||||
getDad().cameraFocusPoint.x += charData.cameraOffsets[0];
|
getDad().cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
getDad().cameraFocusPoint.y += charData.cameraOffsets[1];
|
getDad().cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
}
|
}
|
||||||
|
@ -226,7 +229,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
switch (dataProp.scale)
|
switch (dataProp.scale)
|
||||||
{
|
{
|
||||||
case Left(value):
|
case Left(value):
|
||||||
propSprite.scale.set(value);
|
propSprite.scale.set(value, value);
|
||||||
|
|
||||||
case Right(values):
|
case Right(values):
|
||||||
propSprite.scale.set(values[0], values[1]);
|
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.originalPosition.y = character.y + character.animOffsets[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
character.scale.set(charData.scale, charData.scale);
|
||||||
character.cameraFocusPoint.x += charData.cameraOffsets[0];
|
character.cameraFocusPoint.x += charData.cameraOffsets[0];
|
||||||
character.cameraFocusPoint.y += charData.cameraOffsets[1];
|
character.cameraFocusPoint.y += charData.cameraOffsets[1];
|
||||||
|
|
||||||
|
@ -637,7 +641,30 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
*/
|
*/
|
||||||
public function dispatchToCharacters(event:ScriptEvent):Void
|
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);
|
dispatchToCharacter(characterId, event);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue