mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-12-02 09:07:28 +00:00
Various bug fixes for strumlines
This commit is contained in:
parent
1935373963
commit
60c6e5ee29
|
|
@ -2,16 +2,11 @@ package funkin;
|
||||||
|
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import cpp.abi.Abi;
|
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
||||||
import funkin.Conductor.BPMChangeEvent;
|
import funkin.Conductor.BPMChangeEvent;
|
||||||
import flixel.FlxGame;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
|
||||||
import flixel.addons.ui.FlxUIState;
|
import flixel.addons.ui.FlxUIState;
|
||||||
import flixel.math.FlxRect;
|
|
||||||
import flixel.util.FlxTimer;
|
|
||||||
|
|
||||||
class MusicBeatState extends FlxUIState
|
class MusicBeatState extends FlxUIState
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ import polymod.hscript.HScriptable;
|
||||||
/**
|
/**
|
||||||
* Functions annotated with @:hscript will call the relevant script.
|
* Functions annotated with @:hscript will call the relevant script.
|
||||||
* Functions annotated with @:hookable can be reassigned.
|
* Functions annotated with @:hookable can be reassigned.
|
||||||
|
* NOTE: If you receive the following error when making a function use @:hookable:
|
||||||
|
* `Cannot access this or other member field in variable initialization`
|
||||||
|
* This is because you need to perform calls and assignments using a static variable referencing the target object.
|
||||||
*/
|
*/
|
||||||
@:hscript({
|
@:hscript({
|
||||||
// ALL of these values are added to ALL scripts in the child classes.
|
// ALL of these values are added to ALL scripts in the child classes.
|
||||||
context: [FlxG, FlxSprite, Math, Paths, Std]
|
context: [FlxG, FlxSprite, Math, Paths, Std]
|
||||||
})
|
})
|
||||||
// @:autoBuild(funkin.util.macro.HookableMacro.build())
|
@:autoBuild(funkin.util.macro.HookableMacro.build())
|
||||||
interface IHook extends HScriptable {}
|
interface IHook extends HScriptable {}
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,24 @@ interface IScriptedClass
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a set of callbacks available to scripted classes that involve player input.
|
* Defines a set of callbacks available to scripted classes which can follow the game between states.
|
||||||
*/
|
*/
|
||||||
interface IInputScriptedClass extends IScriptedClass
|
interface IStateChangingScriptedClass extends IScriptedClass
|
||||||
{
|
{
|
||||||
public function onKeyDown(event:KeyboardInputScriptEvent):Void;
|
public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
|
||||||
public function onKeyUp(event:KeyboardInputScriptEvent):Void;
|
public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
|
||||||
// TODO: OnMouseDown, OnMouseUp, OnMouseMove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Developer note:
|
||||||
|
*
|
||||||
|
* I previously considered adding events for onKeyDown, onKeyUp, mouse events, etc.
|
||||||
|
* However, I realized that you can simply call something like the following within a module:
|
||||||
|
* `FlxG.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);`
|
||||||
|
* This is more efficient than adding an entire event handler for every key press.
|
||||||
|
*
|
||||||
|
* -Eric
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Defines a set of callbacks available to scripted classes that involve the lifecycle of the Play State.
|
* Defines a set of callbacks available to scripted classes that involve the lifecycle of the Play State.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,18 @@ class ScriptEvent
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_LOADED:ScriptEventType = "SONG_LOADED";
|
public static inline final SONG_LOADED:ScriptEventType = "SONG_LOADED";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game is entering the current FlxState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final STATE_ENTER:ScriptEventType = "STATE_ENTER";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game is exiting the current FlxState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* If true, the behavior associated with this event can be prevented.
|
* If true, the behavior associated with this event can be prevented.
|
||||||
* For example, cancelling COUNTDOWN_BEGIN should prevent the countdown from starting,
|
* For example, cancelling COUNTDOWN_BEGIN should prevent the countdown from starting,
|
||||||
|
|
@ -385,3 +397,19 @@ class SongLoadScriptEvent extends ScriptEvent
|
||||||
return 'SongLoadScriptEvent(notes=$noteStr, id=$id, difficulty=$difficulty)';
|
return 'SongLoadScriptEvent(notes=$noteStr, id=$id, difficulty=$difficulty)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is fired when moving out of or into an FlxState.
|
||||||
|
*/
|
||||||
|
class StateChangeScriptEvent extends ScriptEvent
|
||||||
|
{
|
||||||
|
public function new(type:ScriptEventType):Void
|
||||||
|
{
|
||||||
|
super(type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function toString():String
|
||||||
|
{
|
||||||
|
return 'StateChangeScriptEvent(type=' + type + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package funkin.modding.events;
|
package funkin.modding.events;
|
||||||
|
|
||||||
import funkin.modding.IScriptedClass;
|
import funkin.modding.IScriptedClass;
|
||||||
import funkin.modding.IScriptedClass.IInputScriptedClass;
|
|
||||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,16 +35,14 @@ class ScriptEventDispatcher
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Std.isOfType(target, IInputScriptedClass))
|
if (Std.isOfType(target, IStateChangingScriptedClass))
|
||||||
{
|
{
|
||||||
var t = cast(target, IInputScriptedClass);
|
var t = cast(target, IStateChangingScriptedClass);
|
||||||
|
var t = cast(target, IPlayStateScriptedClass);
|
||||||
switch (event.type)
|
switch (event.type)
|
||||||
{
|
{
|
||||||
case ScriptEvent.KEY_DOWN:
|
case ScriptEvent.NOTE_HIT:
|
||||||
t.onKeyDown(cast event);
|
t.onNoteHit(cast event);
|
||||||
return;
|
|
||||||
case ScriptEvent.KEY_UP:
|
|
||||||
t.onKeyUp(cast event);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ import funkin.modding.events.ScriptEvent.NoteScriptEvent;
|
||||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
||||||
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
|
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
|
||||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||||
import funkin.modding.IScriptedClass.IInputScriptedClass;
|
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A module is a scripted class which receives all events without requiring a specific context.
|
* A module is a scripted class which receives all events without requiring a specific context.
|
||||||
* You may have the module active at all times, or only when another script enables it.
|
* You may have the module active at all times, or only when another script enables it.
|
||||||
*/
|
*/
|
||||||
class Module implements IInputScriptedClass implements IPlayStateScriptedClass
|
class Module implements IPlayStateScriptedClass implements IStateChangingScriptedClass
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Whether the module is currently active.
|
* Whether the module is currently active.
|
||||||
|
|
@ -68,16 +68,20 @@ class Module implements IInputScriptedClass implements IPlayStateScriptedClass
|
||||||
|
|
||||||
public function onScriptEvent(event:ScriptEvent) {}
|
public function onScriptEvent(event:ScriptEvent) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the module is first created.
|
||||||
|
* This happens before the title screen appears!
|
||||||
|
*/
|
||||||
public function onCreate(event:ScriptEvent) {}
|
public function onCreate(event:ScriptEvent) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a module is destroyed.
|
||||||
|
* This currently only happens when reloading modules with F5.
|
||||||
|
*/
|
||||||
public function onDestroy(event:ScriptEvent) {}
|
public function onDestroy(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onUpdate(event:UpdateScriptEvent) {}
|
public function onUpdate(event:UpdateScriptEvent) {}
|
||||||
|
|
||||||
public function onKeyDown(event:KeyboardInputScriptEvent) {}
|
|
||||||
|
|
||||||
public function onKeyUp(event:KeyboardInputScriptEvent) {}
|
|
||||||
|
|
||||||
public function onPause(event:ScriptEvent) {}
|
public function onPause(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onResume(event:ScriptEvent) {}
|
public function onResume(event:ScriptEvent) {}
|
||||||
|
|
@ -107,4 +111,8 @@ class Module implements IInputScriptedClass implements IPlayStateScriptedClass
|
||||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
||||||
|
|
||||||
|
public function onStateChangeBegin(event:StateChangeScriptEvent) {}
|
||||||
|
|
||||||
|
public function onStateChangeEnd(event:StateChangeScriptEvent) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,10 +96,22 @@ class ModuleHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the module cache, forcing all modules to call shutdown events.
|
||||||
|
*/
|
||||||
public static function clearModuleCache():Void
|
public static function clearModuleCache():Void
|
||||||
{
|
{
|
||||||
if (moduleCache != null)
|
if (moduleCache != null)
|
||||||
{
|
{
|
||||||
|
var event = new ScriptEvent(ScriptEvent.DESTROY, false);
|
||||||
|
|
||||||
|
// Note: Ignore stopPropagation()
|
||||||
|
for (key => value in moduleCache)
|
||||||
|
{
|
||||||
|
ScriptEventDispatcher.callEvent(value, event);
|
||||||
|
moduleCache.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
moduleCache.clear();
|
moduleCache.clear();
|
||||||
modulePriorityOrder = [];
|
modulePriorityOrder = [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import funkin.util.Constants;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.input.actions.FlxAction.FlxActionAnalog;
|
|
||||||
import cpp.abi.Abi;
|
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import funkin.play.Strumline.StrumlineStyle;
|
import funkin.play.Strumline.StrumlineArrow;
|
||||||
import flixel.addons.effects.FlxTrail;
|
import flixel.addons.effects.FlxTrail;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
|
|
@ -24,12 +24,13 @@ import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
|
import funkin.modding.IHook;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.Note;
|
import funkin.Note;
|
||||||
import funkin.play.stage.Stage;
|
import funkin.play.stage.Stage;
|
||||||
import funkin.play.stage.StageData;
|
import funkin.play.stage.StageData;
|
||||||
|
import funkin.play.Strumline.StrumlineStyle;
|
||||||
import funkin.Section.SwagSection;
|
import funkin.Section.SwagSection;
|
||||||
import funkin.shaderslmfao.ColorSwap;
|
|
||||||
import funkin.SongLoad.SwagSong;
|
import funkin.SongLoad.SwagSong;
|
||||||
import funkin.ui.PopUpStuff;
|
import funkin.ui.PopUpStuff;
|
||||||
import funkin.ui.PreferencesMenu;
|
import funkin.ui.PreferencesMenu;
|
||||||
|
|
@ -43,7 +44,7 @@ using StringTools;
|
||||||
import Discord.DiscordClient;
|
import Discord.DiscordClient;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
class PlayState extends MusicBeatState
|
class PlayState extends MusicBeatState implements IHook
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* STATIC VARIABLES
|
* STATIC VARIABLES
|
||||||
|
|
@ -139,11 +140,6 @@ class PlayState extends MusicBeatState
|
||||||
*/
|
*/
|
||||||
private var inactiveNotes:Array<Note>;
|
private var inactiveNotes:Array<Note>;
|
||||||
|
|
||||||
/**
|
|
||||||
* An object which the strumline (and its notes) are positioned relative to.
|
|
||||||
*/
|
|
||||||
private var strumlineAnchor:FlxObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, the player is allowed to pause the game.
|
* If true, the player is allowed to pause the game.
|
||||||
* Disabled during the ending of a song.
|
* Disabled during the ending of a song.
|
||||||
|
|
@ -231,7 +227,6 @@ class PlayState extends MusicBeatState
|
||||||
private var vocals:VoicesGroup;
|
private var vocals:VoicesGroup;
|
||||||
private var vocalsFinished:Bool = false;
|
private var vocalsFinished:Bool = false;
|
||||||
|
|
||||||
private var playerStrums:FlxTypedGroup<FlxSprite>;
|
|
||||||
private var camZooming:Bool = false;
|
private var camZooming:Bool = false;
|
||||||
private var gfSpeed:Int = 1;
|
private var gfSpeed:Int = 1;
|
||||||
private var combo:Int = 0;
|
private var combo:Int = 0;
|
||||||
|
|
@ -331,8 +326,6 @@ class PlayState extends MusicBeatState
|
||||||
|
|
||||||
add(grpNoteSplashes);
|
add(grpNoteSplashes);
|
||||||
|
|
||||||
playerStrums = new FlxTypedGroup<FlxSprite>();
|
|
||||||
|
|
||||||
generateSong();
|
generateSong();
|
||||||
|
|
||||||
cameraFollowPoint = new FlxObject(0, 0, 1, 1);
|
cameraFollowPoint = new FlxObject(0, 0, 1, 1);
|
||||||
|
|
@ -407,6 +400,10 @@ class PlayState extends MusicBeatState
|
||||||
case 'guns':
|
case 'guns':
|
||||||
VanillaCutscenes.playGunsCutscene();
|
VanillaCutscenes.playGunsCutscene();
|
||||||
default:
|
default:
|
||||||
|
// VanillaCutscenes will call startCountdown later.
|
||||||
|
// TODO: Alternatively: make a song script that allows startCountdown to be called,
|
||||||
|
// then cancels the countdown, hides the strumline, plays the cutscene,
|
||||||
|
// then calls Countdown.performCountdown()
|
||||||
startCountdown();
|
startCountdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -415,8 +412,9 @@ class PlayState extends MusicBeatState
|
||||||
startCountdown();
|
startCountdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.leftWatermarkText.text = '${currentSong.song.toUpperCase()} - ${SongLoad.curDiff.toUpperCase()}';
|
#if debug
|
||||||
this.rightWatermarkText.text = Constants.VERSION;
|
this.rightWatermarkText.text = Constants.VERSION;
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -936,6 +934,7 @@ class PlayState extends MusicBeatState
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
updateHealthBar();
|
updateHealthBar();
|
||||||
|
updateScoreText();
|
||||||
|
|
||||||
if (needsReset)
|
if (needsReset)
|
||||||
{
|
{
|
||||||
|
|
@ -1173,7 +1172,7 @@ class PlayState extends MusicBeatState
|
||||||
daNote.active = true;
|
daNote.active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var strumLineMid = playerStrumline.offset.y + Note.swagWidth / 2;
|
var strumLineMid = playerStrumline.y + Note.swagWidth / 2;
|
||||||
|
|
||||||
if (daNote.followsTime)
|
if (daNote.followsTime)
|
||||||
daNote.y = (Conductor.songPosition - daNote.data.strumTime) * (0.45 * FlxMath.roundDecimal(SongLoad.getSpeed(),
|
daNote.y = (Conductor.songPosition - daNote.data.strumTime) * (0.45 * FlxMath.roundDecimal(SongLoad.getSpeed(),
|
||||||
|
|
@ -1181,7 +1180,7 @@ class PlayState extends MusicBeatState
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('downscroll'))
|
if (PreferencesMenu.getPref('downscroll'))
|
||||||
{
|
{
|
||||||
daNote.y += playerStrumline.offset.y;
|
daNote.y += playerStrumline.y;
|
||||||
if (daNote.isSustainNote)
|
if (daNote.isSustainNote)
|
||||||
{
|
{
|
||||||
if (daNote.animation.curAnim.name.endsWith("end") && daNote.prevNote != null)
|
if (daNote.animation.curAnim.name.endsWith("end") && daNote.prevNote != null)
|
||||||
|
|
@ -1199,7 +1198,7 @@ class PlayState extends MusicBeatState
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (daNote.followsTime)
|
if (daNote.followsTime)
|
||||||
daNote.y = playerStrumline.offset.y - daNote.y;
|
daNote.y = playerStrumline.y - daNote.y;
|
||||||
if (daNote.isSustainNote
|
if (daNote.isSustainNote
|
||||||
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit)))
|
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit)))
|
||||||
&& daNote.y + daNote.offset.y * daNote.scale.y <= strumLineMid)
|
&& daNote.y + daNote.offset.y * daNote.scale.y <= strumLineMid)
|
||||||
|
|
@ -1284,7 +1283,7 @@ class PlayState extends MusicBeatState
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInCutscene)
|
if (!isInCutscene)
|
||||||
keyShit();
|
keyShit(true);
|
||||||
|
|
||||||
dispatchEvent(new UpdateScriptEvent(elapsed));
|
dispatchEvent(new UpdateScriptEvent(elapsed));
|
||||||
}
|
}
|
||||||
|
|
@ -1293,7 +1292,7 @@ class PlayState extends MusicBeatState
|
||||||
{
|
{
|
||||||
// clipRect is applied to graphic itself so use frame Heights
|
// clipRect is applied to graphic itself so use frame Heights
|
||||||
var swagRect:FlxRect = new FlxRect(0, 0, daNote.frameWidth, daNote.frameHeight);
|
var swagRect:FlxRect = new FlxRect(0, 0, daNote.frameWidth, daNote.frameHeight);
|
||||||
var strumLineMid = playerStrumline.offset.y + Note.swagWidth / 2;
|
var strumLineMid = playerStrumline.y + Note.swagWidth / 2;
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('downscroll'))
|
if (PreferencesMenu.getPref('downscroll'))
|
||||||
{
|
{
|
||||||
|
|
@ -1549,7 +1548,14 @@ class PlayState extends MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function keyShit():Void
|
public var test:(PlayState) -> Void = function(instance:PlayState)
|
||||||
|
{
|
||||||
|
trace('test');
|
||||||
|
trace(instance.currentStageId);
|
||||||
|
};
|
||||||
|
|
||||||
|
@:hookable
|
||||||
|
public function keyShit(test:Bool):Void
|
||||||
{
|
{
|
||||||
// control arrays, order L D R U
|
// control arrays, order L D R U
|
||||||
var holdArray:Array<Bool> = [controls.NOTE_LEFT, controls.NOTE_DOWN, controls.NOTE_UP, controls.NOTE_RIGHT];
|
var holdArray:Array<Bool> = [controls.NOTE_LEFT, controls.NOTE_DOWN, controls.NOTE_UP, controls.NOTE_RIGHT];
|
||||||
|
|
@ -1566,27 +1572,27 @@ class PlayState extends MusicBeatState
|
||||||
controls.NOTE_RIGHT_R
|
controls.NOTE_RIGHT_R
|
||||||
];
|
];
|
||||||
// HOLDS, check for sustain notes
|
// HOLDS, check for sustain notes
|
||||||
if (holdArray.contains(true) && generatedMusic)
|
if (holdArray.contains(true) && PlayState.instance.generatedMusic)
|
||||||
{
|
{
|
||||||
activeNotes.forEachAlive(function(daNote:Note)
|
PlayState.instance.activeNotes.forEachAlive(function(daNote:Note)
|
||||||
{
|
{
|
||||||
if (daNote.isSustainNote && daNote.canBeHit && daNote.mustPress && holdArray[daNote.data.noteData])
|
if (daNote.isSustainNote && daNote.canBeHit && daNote.mustPress && holdArray[daNote.data.noteData])
|
||||||
goodNoteHit(daNote);
|
PlayState.instance.goodNoteHit(daNote);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRESSES, check for note hits
|
// PRESSES, check for note hits
|
||||||
if (pressArray.contains(true) && generatedMusic)
|
if (pressArray.contains(true) && PlayState.instance.generatedMusic)
|
||||||
{
|
{
|
||||||
Haptic.vibrate(100, 100);
|
Haptic.vibrate(100, 100);
|
||||||
|
|
||||||
currentStage.getBoyfriend().holdTimer = 0;
|
PlayState.instance.currentStage.getBoyfriend().holdTimer = 0;
|
||||||
|
|
||||||
var possibleNotes:Array<Note> = []; // notes that can be hit
|
var possibleNotes:Array<Note> = []; // notes that can be hit
|
||||||
var directionList:Array<Int> = []; // directions that can be hit
|
var directionList:Array<Int> = []; // directions that can be hit
|
||||||
var dumbNotes:Array<Note> = []; // notes to kill later
|
var dumbNotes:Array<Note> = []; // notes to kill later
|
||||||
|
|
||||||
activeNotes.forEachAlive(function(daNote:Note)
|
PlayState.instance.activeNotes.forEachAlive(function(daNote:Note)
|
||||||
{
|
{
|
||||||
if (daNote.canBeHit && daNote.mustPress && !daNote.tooLate && !daNote.wasGoodHit)
|
if (daNote.canBeHit && daNote.mustPress && !daNote.tooLate && !daNote.wasGoodHit)
|
||||||
{
|
{
|
||||||
|
|
@ -1621,63 +1627,60 @@ class PlayState extends MusicBeatState
|
||||||
{
|
{
|
||||||
FlxG.log.add("killing dumb ass note at " + note.data.strumTime);
|
FlxG.log.add("killing dumb ass note at " + note.data.strumTime);
|
||||||
note.kill();
|
note.kill();
|
||||||
activeNotes.remove(note, true);
|
PlayState.instance.activeNotes.remove(note, true);
|
||||||
note.destroy();
|
note.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleNotes.sort((a, b) -> Std.int(a.data.strumTime - b.data.strumTime));
|
possibleNotes.sort((a, b) -> Std.int(a.data.strumTime - b.data.strumTime));
|
||||||
|
|
||||||
if (perfectMode)
|
if (PlayState.instance.perfectMode)
|
||||||
goodNoteHit(possibleNotes[0]);
|
PlayState.instance.goodNoteHit(possibleNotes[0]);
|
||||||
else if (possibleNotes.length > 0)
|
else if (possibleNotes.length > 0)
|
||||||
{
|
{
|
||||||
for (shit in 0...pressArray.length)
|
for (shit in 0...pressArray.length)
|
||||||
{ // if a direction is hit that shouldn't be
|
{ // if a direction is hit that shouldn't be
|
||||||
if (pressArray[shit] && !directionList.contains(shit))
|
if (pressArray[shit] && !directionList.contains(shit))
|
||||||
noteMiss(shit);
|
PlayState.instance.noteMiss(shit);
|
||||||
}
|
}
|
||||||
for (coolNote in possibleNotes)
|
for (coolNote in possibleNotes)
|
||||||
{
|
{
|
||||||
if (pressArray[coolNote.data.noteData])
|
if (pressArray[coolNote.data.noteData])
|
||||||
goodNoteHit(coolNote);
|
PlayState.instance.goodNoteHit(coolNote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (shit in 0...pressArray.length)
|
for (shit in 0...pressArray.length)
|
||||||
if (pressArray[shit])
|
if (pressArray[shit])
|
||||||
noteMiss(shit);
|
PlayState.instance.noteMiss(shit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentStage == null)
|
if (PlayState.instance.currentStage == null)
|
||||||
return;
|
return;
|
||||||
if (currentStage.getBoyfriend().holdTimer > Conductor.stepCrochet * 4 * 0.001 && !holdArray.contains(true))
|
if (PlayState.instance.currentStage.getBoyfriend().holdTimer > Conductor.stepCrochet * 4 * 0.001 && !holdArray.contains(true))
|
||||||
{
|
{
|
||||||
if (currentStage.getBoyfriend().animation != null
|
if (PlayState.instance.currentStage.getBoyfriend().animation != null
|
||||||
&& currentStage.getBoyfriend().animation.curAnim.name.startsWith('sing')
|
&& PlayState.instance.currentStage.getBoyfriend().animation.curAnim.name.startsWith('sing')
|
||||||
&& !currentStage.getBoyfriend().animation.curAnim.name.endsWith('miss'))
|
&& !PlayState.instance.currentStage.getBoyfriend().animation.curAnim.name.endsWith('miss'))
|
||||||
{
|
{
|
||||||
currentStage.getBoyfriend().playAnim('idle');
|
PlayState.instance.currentStage.getBoyfriend().playAnim('idle');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playerStrums.forEach(function(spr:FlxSprite)
|
for (keyId => isPressed in pressArray)
|
||||||
{
|
{
|
||||||
if (pressArray[spr.ID] && spr.animation.curAnim.name != 'confirm')
|
var arrow:StrumlineArrow = PlayState.instance.playerStrumline.getArrow(keyId);
|
||||||
spr.animation.play('pressed');
|
|
||||||
if (!holdArray[spr.ID])
|
|
||||||
spr.animation.play('static');
|
|
||||||
|
|
||||||
if (spr.animation.curAnim.name == 'confirm' && !currentStageId.startsWith('school'))
|
if (isPressed && arrow.animation.curAnim.name != 'confirm')
|
||||||
{
|
{
|
||||||
spr.centerOffsets();
|
arrow.playAnimation('pressed');
|
||||||
spr.offset.x -= 13;
|
}
|
||||||
spr.offset.y -= 13;
|
if (!holdArray[keyId])
|
||||||
|
{
|
||||||
|
arrow.playAnimation('static');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
spr.centerOffsets();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function noteMiss(direction:NoteDir = 1):Void
|
function noteMiss(direction:NoteDir = 1):Void
|
||||||
|
|
@ -1707,13 +1710,7 @@ class PlayState extends MusicBeatState
|
||||||
|
|
||||||
currentStage.getBoyfriend().playAnim('sing' + note.dirNameUpper, true);
|
currentStage.getBoyfriend().playAnim('sing' + note.dirNameUpper, true);
|
||||||
|
|
||||||
playerStrums.forEach(function(spr:FlxSprite)
|
playerStrumline.getArrow(note.data.noteData).playAnimation('confirm', true);
|
||||||
{
|
|
||||||
if (Math.abs(note.data.noteData) == spr.ID)
|
|
||||||
{
|
|
||||||
spr.animation.play('confirm', true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
note.wasGoodHit = true;
|
note.wasGoodHit = true;
|
||||||
vocals.volume = 1;
|
vocals.volume = 1;
|
||||||
|
|
@ -1848,19 +1845,31 @@ class PlayState extends MusicBeatState
|
||||||
var strumlineYPos = Strumline.getYPos();
|
var strumlineYPos = Strumline.getYPos();
|
||||||
|
|
||||||
playerStrumline = new Strumline(0, strumlineStyle, 4);
|
playerStrumline = new Strumline(0, strumlineStyle, 4);
|
||||||
playerStrumline.offset = new FlxPoint(50 + FlxG.width / 2, strumlineYPos);
|
playerStrumline.x = 50 + FlxG.width / 2;
|
||||||
|
playerStrumline.y = strumlineYPos;
|
||||||
// Set the z-index so they don't appear in front of notes.
|
// Set the z-index so they don't appear in front of notes.
|
||||||
playerStrumline.zIndex = 100;
|
playerStrumline.zIndex = 100;
|
||||||
add(playerStrumline);
|
add(playerStrumline);
|
||||||
playerStrumline.cameras = [camHUD];
|
playerStrumline.cameras = [camHUD];
|
||||||
|
|
||||||
|
if (!isStoryMode)
|
||||||
|
{
|
||||||
|
playerStrumline.fadeInArrows();
|
||||||
|
}
|
||||||
|
|
||||||
enemyStrumline = new Strumline(1, strumlineStyle, 4);
|
enemyStrumline = new Strumline(1, strumlineStyle, 4);
|
||||||
enemyStrumline.offset = new FlxPoint(50, strumlineYPos);
|
enemyStrumline.x = 50;
|
||||||
|
enemyStrumline.y = strumlineYPos;
|
||||||
// Set the z-index so they don't appear in front of notes.
|
// Set the z-index so they don't appear in front of notes.
|
||||||
enemyStrumline.zIndex = 100;
|
enemyStrumline.zIndex = 100;
|
||||||
add(enemyStrumline);
|
add(enemyStrumline);
|
||||||
enemyStrumline.cameras = [camHUD];
|
enemyStrumline.cameras = [camHUD];
|
||||||
|
|
||||||
|
if (!isStoryMode)
|
||||||
|
{
|
||||||
|
enemyStrumline.fadeInArrows();
|
||||||
|
}
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1997,6 +2006,7 @@ class PlayState extends MusicBeatState
|
||||||
{
|
{
|
||||||
remove(currentStage);
|
remove(currentStage);
|
||||||
currentStage.kill();
|
currentStage.kill();
|
||||||
|
dispatchEvent(new ScriptEvent(ScriptEvent.DESTROY, false));
|
||||||
currentStage = null;
|
currentStage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,23 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import funkin.ui.PreferencesMenu;
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.math.FlxPoint;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
import funkin.Note.NoteColor;
|
import funkin.Note.NoteColor;
|
||||||
import funkin.Note.NoteDir;
|
import funkin.Note.NoteDir;
|
||||||
import funkin.Note.NoteType;
|
import funkin.Note.NoteType;
|
||||||
import flixel.tweens.FlxTween;
|
import funkin.ui.PreferencesMenu;
|
||||||
import flixel.tweens.FlxEase;
|
|
||||||
import funkin.util.Constants;
|
import funkin.util.Constants;
|
||||||
import flixel.FlxSprite;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group controlling the individual notes of the strumline for a given player.
|
* A group controlling the individual notes of the strumline for a given player.
|
||||||
|
*
|
||||||
|
* FUN FACT: Setting the X and Y of a FlxSpriteGroup will move all the sprites in the group.
|
||||||
*/
|
*/
|
||||||
class Strumline extends FlxTypedGroup<FlxSprite>
|
class Strumline extends FlxTypedSpriteGroup<StrumlineArrow>
|
||||||
{
|
{
|
||||||
public var offset(default, set):FlxPoint = new FlxPoint(0, 0);
|
|
||||||
|
|
||||||
function set_offset(value:FlxPoint):FlxPoint
|
|
||||||
{
|
|
||||||
this.offset = value;
|
|
||||||
updatePositions();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The style of the strumline.
|
* The style of the strumline.
|
||||||
* Options are normal and pixel.
|
* Options are normal and pixel.
|
||||||
|
|
@ -62,132 +55,30 @@ class Strumline extends FlxTypedGroup<FlxSprite>
|
||||||
|
|
||||||
function createStrumlineArrow(index:Int):Void
|
function createStrumlineArrow(index:Int):Void
|
||||||
{
|
{
|
||||||
var arrow:FlxSprite = new FlxSprite(0, 0);
|
var arrow:StrumlineArrow = new StrumlineArrow(index, style);
|
||||||
|
|
||||||
arrow.ID = index;
|
|
||||||
|
|
||||||
// Color changing for arrows is a WIP.
|
|
||||||
/*
|
|
||||||
var colorSwapShader:ColorSwap = new ColorSwap();
|
|
||||||
colorSwapShader.update(Note.arrowColors[i]);
|
|
||||||
arrow.shader = colorSwapShader;
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (style)
|
|
||||||
{
|
|
||||||
case NORMAL:
|
|
||||||
createNormalNote(arrow);
|
|
||||||
case PIXEL:
|
|
||||||
createPixelNote(arrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
arrow.updateHitbox();
|
|
||||||
arrow.scrollFactor.set();
|
|
||||||
|
|
||||||
arrow.animation.play('static');
|
|
||||||
|
|
||||||
applyFadeIn(arrow);
|
|
||||||
|
|
||||||
add(arrow);
|
add(arrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a small animation which moves the arrow down and fades it in.
|
* Apply a small animation which moves the arrow down and fades it in.
|
||||||
* Only plays at the start of Free Play songs I guess?
|
* Only plays at the start of Free Play songs.
|
||||||
|
*
|
||||||
|
* Note that modifying the offset of the whole strumline won't have the
|
||||||
* @param arrow The arrow to animate.
|
* @param arrow The arrow to animate.
|
||||||
* @param index The index of the arrow in the strumline.
|
* @param index The index of the arrow in the strumline.
|
||||||
*/
|
*/
|
||||||
function applyFadeIn(arrow:FlxSprite):Void
|
function fadeInArrow(arrow:FlxSprite):Void
|
||||||
{
|
|
||||||
if (!PlayState.isStoryMode)
|
|
||||||
{
|
{
|
||||||
arrow.y -= 10;
|
arrow.y -= 10;
|
||||||
arrow.alpha = 0;
|
arrow.alpha = 0;
|
||||||
FlxTween.tween(arrow, {y: arrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * arrow.ID)});
|
FlxTween.tween(arrow, {y: arrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * arrow.ID)});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public function fadeInArrows():Void
|
||||||
* Applies the default note style to an arrow.
|
|
||||||
* @param arrow The arrow to apply the style to.
|
|
||||||
* @param index The index of the arrow in the strumline.
|
|
||||||
*/
|
|
||||||
function createNormalNote(arrow:FlxSprite):Void
|
|
||||||
{
|
{
|
||||||
arrow.frames = Paths.getSparrowAtlas('NOTE_assets');
|
for (arrow in this.members)
|
||||||
|
|
||||||
arrow.animation.addByPrefix('green', 'arrowUP');
|
|
||||||
arrow.animation.addByPrefix('blue', 'arrowDOWN');
|
|
||||||
arrow.animation.addByPrefix('purple', 'arrowLEFT');
|
|
||||||
arrow.animation.addByPrefix('red', 'arrowRIGHT');
|
|
||||||
|
|
||||||
arrow.setGraphicSize(Std.int(arrow.width * 0.7));
|
|
||||||
arrow.antialiasing = true;
|
|
||||||
|
|
||||||
arrow.x += Note.swagWidth * arrow.ID;
|
|
||||||
|
|
||||||
switch (Math.abs(arrow.ID))
|
|
||||||
{
|
{
|
||||||
case 0:
|
fadeInArrow(arrow);
|
||||||
arrow.animation.addByPrefix('static', 'arrow static instance 1');
|
|
||||||
arrow.animation.addByPrefix('pressed', 'left press', 24, false);
|
|
||||||
arrow.animation.addByPrefix('confirm', 'left confirm', 24, false);
|
|
||||||
case 1:
|
|
||||||
arrow.animation.addByPrefix('static', 'arrow static instance 2');
|
|
||||||
arrow.animation.addByPrefix('pressed', 'down press', 24, false);
|
|
||||||
arrow.animation.addByPrefix('confirm', 'down confirm', 24, false);
|
|
||||||
case 2:
|
|
||||||
arrow.animation.addByPrefix('static', 'arrow static instance 4');
|
|
||||||
arrow.animation.addByPrefix('pressed', 'up press', 24, false);
|
|
||||||
arrow.animation.addByPrefix('confirm', 'up confirm', 24, false);
|
|
||||||
case 3:
|
|
||||||
arrow.animation.addByPrefix('static', 'arrow static instance 3');
|
|
||||||
arrow.animation.addByPrefix('pressed', 'right press', 24, false);
|
|
||||||
arrow.animation.addByPrefix('confirm', 'right confirm', 24, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the pixel note style to an arrow.
|
|
||||||
* @param arrow The arrow to apply the style to.
|
|
||||||
* @param index The index of the arrow in the strumline.
|
|
||||||
*/
|
|
||||||
function createPixelNote(arrow:FlxSprite):Void
|
|
||||||
{
|
|
||||||
arrow.loadGraphic(Paths.image('weeb/pixelUI/arrows-pixels'), true, 17, 17);
|
|
||||||
|
|
||||||
arrow.animation.add('purplel', [4]);
|
|
||||||
arrow.animation.add('blue', [5]);
|
|
||||||
arrow.animation.add('green', [6]);
|
|
||||||
arrow.animation.add('red', [7]);
|
|
||||||
|
|
||||||
arrow.setGraphicSize(Std.int(arrow.width * Constants.PIXEL_ART_SCALE));
|
|
||||||
arrow.updateHitbox();
|
|
||||||
|
|
||||||
// Forcibly disable anti-aliasing on pixel graphics to stop blur.
|
|
||||||
arrow.antialiasing = false;
|
|
||||||
|
|
||||||
arrow.x += Note.swagWidth * arrow.ID;
|
|
||||||
|
|
||||||
// TODO: Seems weird that these are hardcoded like this... no XML?
|
|
||||||
switch (Math.abs(arrow.ID))
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
arrow.animation.add('static', [0]);
|
|
||||||
arrow.animation.add('pressed', [4, 8], 12, false);
|
|
||||||
arrow.animation.add('confirm', [12, 16], 24, false);
|
|
||||||
case 1:
|
|
||||||
arrow.animation.add('static', [1]);
|
|
||||||
arrow.animation.add('pressed', [5, 9], 12, false);
|
|
||||||
arrow.animation.add('confirm', [13, 17], 24, false);
|
|
||||||
case 2:
|
|
||||||
arrow.animation.add('static', [2]);
|
|
||||||
arrow.animation.add('pressed', [6, 10], 12, false);
|
|
||||||
arrow.animation.add('confirm', [14, 18], 12, false);
|
|
||||||
case 3:
|
|
||||||
arrow.animation.add('static', [3]);
|
|
||||||
arrow.animation.add('pressed', [7, 11], 12, false);
|
|
||||||
arrow.animation.add('confirm', [15, 19], 24, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,33 +99,150 @@ class Strumline extends FlxTypedGroup<FlxSprite>
|
||||||
* @param index The index to retrieve.
|
* @param index The index to retrieve.
|
||||||
* @return The corresponding FlxSprite.
|
* @return The corresponding FlxSprite.
|
||||||
*/
|
*/
|
||||||
public inline function getArrow(value:Int):FlxSprite
|
public inline function getArrow(value:Int):StrumlineArrow
|
||||||
{
|
{
|
||||||
// members maintains the order that the arrows were added.
|
// members maintains the order that the arrows were added.
|
||||||
return this.members[value];
|
return this.members[value];
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function getArrowByNoteType(value:NoteType):FlxSprite
|
public inline function getArrowByNoteType(value:NoteType):StrumlineArrow
|
||||||
{
|
{
|
||||||
return getArrow(value.int);
|
return getArrow(value.int);
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function getArrowByNoteDir(value:NoteDir):FlxSprite
|
public inline function getArrowByNoteDir(value:NoteDir):StrumlineArrow
|
||||||
{
|
{
|
||||||
return getArrow(value.int);
|
return getArrow(value.int);
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function getArrowByNoteColor(value:NoteColor):FlxSprite
|
public inline function getArrowByNoteColor(value:NoteColor):StrumlineArrow
|
||||||
{
|
{
|
||||||
return getArrow(value.int);
|
return getArrow(value.int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default Y offset of the strumline.
|
||||||
|
* @return Int
|
||||||
|
*/
|
||||||
public static inline function getYPos():Int
|
public static inline function getYPos():Int
|
||||||
{
|
{
|
||||||
return PreferencesMenu.getPref('downscroll') ? (FlxG.height - 150) : 50;
|
return PreferencesMenu.getPref('downscroll') ? (FlxG.height - 150) : 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StrumlineArrow extends FlxSprite
|
||||||
|
{
|
||||||
|
var style:StrumlineStyle;
|
||||||
|
|
||||||
|
public function new(id:Int, style:StrumlineStyle)
|
||||||
|
{
|
||||||
|
super(0, 0);
|
||||||
|
|
||||||
|
this.ID = id;
|
||||||
|
this.style = style;
|
||||||
|
|
||||||
|
// TODO: Unhardcode this. Maybe use a note style system>
|
||||||
|
switch (style)
|
||||||
|
{
|
||||||
|
case PIXEL:
|
||||||
|
buildPixelGraphic();
|
||||||
|
case NORMAL:
|
||||||
|
buildNormalGraphic();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateHitbox();
|
||||||
|
scrollFactor.set(0, 0);
|
||||||
|
animation.play('static');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function playAnimation(anim:String, force:Bool = false)
|
||||||
|
{
|
||||||
|
animation.play(anim, force);
|
||||||
|
centerOffsets();
|
||||||
|
centerOrigin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the default note style to an arrow.
|
||||||
|
*/
|
||||||
|
function buildNormalGraphic():Void
|
||||||
|
{
|
||||||
|
this.frames = Paths.getSparrowAtlas('NOTE_assets');
|
||||||
|
|
||||||
|
this.animation.addByPrefix('green', 'arrowUP');
|
||||||
|
this.animation.addByPrefix('blue', 'arrowDOWN');
|
||||||
|
this.animation.addByPrefix('purple', 'arrowLEFT');
|
||||||
|
this.animation.addByPrefix('red', 'arrowRIGHT');
|
||||||
|
|
||||||
|
this.setGraphicSize(Std.int(this.width * 0.7));
|
||||||
|
this.antialiasing = true;
|
||||||
|
|
||||||
|
this.x += Note.swagWidth * this.ID;
|
||||||
|
|
||||||
|
switch (Math.abs(this.ID))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
this.animation.addByPrefix('static', 'arrow static instance 1');
|
||||||
|
this.animation.addByPrefix('pressed', 'left press', 24, false);
|
||||||
|
this.animation.addByPrefix('confirm', 'left confirm', 24, false);
|
||||||
|
case 1:
|
||||||
|
this.animation.addByPrefix('static', 'arrow static instance 2');
|
||||||
|
this.animation.addByPrefix('pressed', 'down press', 24, false);
|
||||||
|
this.animation.addByPrefix('confirm', 'down confirm', 24, false);
|
||||||
|
case 2:
|
||||||
|
this.animation.addByPrefix('static', 'arrow static instance 4');
|
||||||
|
this.animation.addByPrefix('pressed', 'up press', 24, false);
|
||||||
|
this.animation.addByPrefix('confirm', 'up confirm', 24, false);
|
||||||
|
case 3:
|
||||||
|
this.animation.addByPrefix('static', 'arrow static instance 3');
|
||||||
|
this.animation.addByPrefix('pressed', 'right press', 24, false);
|
||||||
|
this.animation.addByPrefix('confirm', 'right confirm', 24, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the pixel note style to an arrow.
|
||||||
|
*/
|
||||||
|
function buildPixelGraphic():Void
|
||||||
|
{
|
||||||
|
this.loadGraphic(Paths.image('weeb/pixelUI/arrows-pixels'), true, 17, 17);
|
||||||
|
|
||||||
|
this.animation.add('purplel', [4]);
|
||||||
|
this.animation.add('blue', [5]);
|
||||||
|
this.animation.add('green', [6]);
|
||||||
|
this.animation.add('red', [7]);
|
||||||
|
|
||||||
|
this.setGraphicSize(Std.int(this.width * Constants.PIXEL_ART_SCALE));
|
||||||
|
this.updateHitbox();
|
||||||
|
|
||||||
|
// Forcibly disable anti-aliasing on pixel graphics to stop blur.
|
||||||
|
this.antialiasing = false;
|
||||||
|
|
||||||
|
this.x += Note.swagWidth * this.ID;
|
||||||
|
|
||||||
|
// TODO: Seems weird that these are hardcoded like this... no XML?
|
||||||
|
switch (Math.abs(this.ID))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
this.animation.add('static', [0]);
|
||||||
|
this.animation.add('pressed', [4, 8], 12, false);
|
||||||
|
this.animation.add('confirm', [12, 16], 24, false);
|
||||||
|
case 1:
|
||||||
|
this.animation.add('static', [1]);
|
||||||
|
this.animation.add('pressed', [5, 9], 12, false);
|
||||||
|
this.animation.add('confirm', [13, 17], 24, false);
|
||||||
|
case 2:
|
||||||
|
this.animation.add('static', [2]);
|
||||||
|
this.animation.add('pressed', [6, 10], 12, false);
|
||||||
|
this.animation.add('confirm', [14, 18], 12, false);
|
||||||
|
case 3:
|
||||||
|
this.animation.add('static', [3]);
|
||||||
|
this.animation.add('pressed', [7, 11], 12, false);
|
||||||
|
this.animation.add('confirm', [15, 19], 24, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Unhardcode this and make it part of the note style system.
|
* TODO: Unhardcode this and make it part of the note style system.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ class VanillaCutscenes
|
||||||
blackScreen = null;
|
blackScreen = null;
|
||||||
|
|
||||||
FlxTween.tween(FlxG.camera, {zoom: PlayState.defaultCameraZoom}, (Conductor.crochet / 1000) * 5, {ease: FlxEase.quadInOut});
|
FlxTween.tween(FlxG.camera, {zoom: PlayState.defaultCameraZoom}, (Conductor.crochet / 1000) * 5, {ease: FlxEase.quadInOut});
|
||||||
Countdown.performCountdown(false);
|
@:privateAccess
|
||||||
|
PlayState.instance.startCountdown();
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
PlayState.instance.cameraMovement();
|
PlayState.instance.cameraMovement();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import funkin.util.SortUtil;
|
||||||
*
|
*
|
||||||
* A Stage is comprised of one or more props, each of which is a FlxSprite.
|
* A Stage is comprised of one or more props, each of which is a FlxSprite.
|
||||||
*/
|
*/
|
||||||
class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScriptedClass implements IInputScriptedClass
|
class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScriptedClass
|
||||||
{
|
{
|
||||||
public final stageId:String;
|
public final stageId:String;
|
||||||
public final stageName:String;
|
public final stageName:String;
|
||||||
|
|
@ -312,7 +312,8 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform cleanup for when you are leaving the level.
|
* onDestroy gets called when the player is leaving the PlayState,
|
||||||
|
* and is used to clean up any objects that need to be destroyed.
|
||||||
*/
|
*/
|
||||||
public function onDestroy(event:ScriptEvent):Void
|
public function onDestroy(event:ScriptEvent):Void
|
||||||
{
|
{
|
||||||
|
|
@ -322,24 +323,32 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
|
|
||||||
for (prop in this.namedProps)
|
for (prop in this.namedProps)
|
||||||
{
|
{
|
||||||
|
remove(prop);
|
||||||
|
prop.kill();
|
||||||
prop.destroy();
|
prop.destroy();
|
||||||
}
|
}
|
||||||
namedProps.clear();
|
namedProps.clear();
|
||||||
|
|
||||||
for (char in this.characters)
|
for (char in this.characters)
|
||||||
{
|
{
|
||||||
|
remove(char);
|
||||||
|
char.kill();
|
||||||
char.destroy();
|
char.destroy();
|
||||||
}
|
}
|
||||||
characters.clear();
|
characters.clear();
|
||||||
|
|
||||||
for (bopper in boppers)
|
for (bopper in boppers)
|
||||||
{
|
{
|
||||||
|
remove(bopper);
|
||||||
|
bopper.kill();
|
||||||
bopper.destroy();
|
bopper.destroy();
|
||||||
}
|
}
|
||||||
boppers = [];
|
boppers = [];
|
||||||
|
|
||||||
for (sprite in this.group)
|
for (sprite in this.group)
|
||||||
{
|
{
|
||||||
|
remove(sprite);
|
||||||
|
sprite.kill();
|
||||||
sprite.destroy();
|
sprite.destroy();
|
||||||
}
|
}
|
||||||
group.clear();
|
group.clear();
|
||||||
|
|
@ -391,10 +400,6 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
|
|
||||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onKeyDown(event:KeyboardInputScriptEvent) {}
|
|
||||||
|
|
||||||
public function onKeyUp(event:KeyboardInputScriptEvent) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that should get called every frame.
|
* A function that should get called every frame.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,17 @@ class Constants
|
||||||
public static final VERSION_SUFFIX = ' PROTOTYPE';
|
public static final VERSION_SUFFIX = ' PROTOTYPE';
|
||||||
public static var VERSION(get, null):String;
|
public static var VERSION(get, null):String;
|
||||||
|
|
||||||
|
#if debug
|
||||||
|
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
||||||
|
|
||||||
|
static function get_VERSION():String
|
||||||
|
{
|
||||||
|
return 'v${Application.current.meta.get('version')} (${GIT_HASH})' + VERSION_SUFFIX;
|
||||||
|
}
|
||||||
|
#else
|
||||||
static function get_VERSION():String
|
static function get_VERSION():String
|
||||||
{
|
{
|
||||||
return 'v${Application.current.meta.get('version')}' + VERSION_SUFFIX;
|
return 'v${Application.current.meta.get('version')}' + VERSION_SUFFIX;
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
source/funkin/util/macro/GitCommit.hx
Normal file
35
source/funkin/util/macro/GitCommit.hx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
package funkin.util.macro;
|
||||||
|
|
||||||
|
#if debug
|
||||||
|
class GitCommit
|
||||||
|
{
|
||||||
|
public static macro function getGitCommitHash():haxe.macro.Expr.ExprOf<String>
|
||||||
|
{
|
||||||
|
#if !display
|
||||||
|
// Get the current line number.
|
||||||
|
var pos = haxe.macro.Context.currentPos();
|
||||||
|
|
||||||
|
var process = new sys.io.Process('git', ['rev-parse', 'HEAD']);
|
||||||
|
if (process.exitCode() != 0)
|
||||||
|
{
|
||||||
|
var message = process.stderr.readAll().toString();
|
||||||
|
haxe.macro.Context.info('[WARN] Could not determine current git commit; is this a proper Git repository?', pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the output of the process
|
||||||
|
var commitHash:String = process.stdout.readLine();
|
||||||
|
var commitHashSplice:String = commitHash.substr(0, 7);
|
||||||
|
|
||||||
|
trace('Git Commit ID ${commitHashSplice}');
|
||||||
|
|
||||||
|
// Generates a string expression
|
||||||
|
return macro $v{commitHashSplice};
|
||||||
|
#else
|
||||||
|
// `#if display` is used for code completion. In this case returning an
|
||||||
|
// empty string is good enough; We don't want to call git on every hint.
|
||||||
|
var commitHash:String = "";
|
||||||
|
return macro $v{commitHashSplice};
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
@ -1,3 +1,70 @@
|
||||||
package funkin.util.macro;
|
package funkin.util.macro;
|
||||||
|
|
||||||
class HookableMacro {}
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
class HookableMacro
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The @:hookable annotation replaces a given function with a variable that contains a function.
|
||||||
|
* It's still callable, like normal, but now you can also replace the value! Neat!
|
||||||
|
*
|
||||||
|
* NOTE: If you receive the following error when making a function use @:hookable:
|
||||||
|
* `Cannot access this or other member field in variable initialization`
|
||||||
|
* This is because you need to perform calls and assignments using a static variable referencing the target object.
|
||||||
|
*/
|
||||||
|
public static macro function build():Array<Field>
|
||||||
|
{
|
||||||
|
Context.info('Running HookableMacro...', Context.currentPos());
|
||||||
|
|
||||||
|
var cls:haxe.macro.Type.ClassType = Context.getLocalClass().get();
|
||||||
|
var fields:Array<Field> = Context.getBuildFields();
|
||||||
|
// Find all fields with @:hookable metadata
|
||||||
|
for (field in fields)
|
||||||
|
{
|
||||||
|
if (field.meta == null)
|
||||||
|
continue;
|
||||||
|
var scriptable_meta = field.meta.find(function(m) return m.name == ':hookable');
|
||||||
|
if (scriptable_meta != null)
|
||||||
|
{
|
||||||
|
Context.info(' @:hookable annotation found on field ${field.name}', Context.currentPos());
|
||||||
|
switch (field.kind)
|
||||||
|
{
|
||||||
|
case FFun(originalFunc):
|
||||||
|
// This is the type of the function, like (Int, Int) -> Int
|
||||||
|
var replFieldTypeRet:ComplexType = originalFunc.ret == null ? Context.toComplexType(Context.getType('Void')) : originalFunc.ret;
|
||||||
|
var replFieldType:ComplexType = TFunction([for (arg in originalFunc.args) arg.type], replFieldTypeRet);
|
||||||
|
// This is the expression of the function, i.e. the function body.
|
||||||
|
|
||||||
|
var replFieldExpr:ExprDef = EFunction(FAnonymous, {
|
||||||
|
ret: originalFunc.ret,
|
||||||
|
params: originalFunc.params,
|
||||||
|
args: originalFunc.args,
|
||||||
|
expr: originalFunc.expr
|
||||||
|
});
|
||||||
|
|
||||||
|
var replField:Field = {
|
||||||
|
name: field.name,
|
||||||
|
doc: field.doc,
|
||||||
|
access: field.access,
|
||||||
|
pos: field.pos,
|
||||||
|
meta: field.meta,
|
||||||
|
kind: FVar(replFieldType, {
|
||||||
|
expr: replFieldExpr,
|
||||||
|
pos: field.pos
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replace the original field with the new field
|
||||||
|
fields[fields.indexOf(field)] = replField;
|
||||||
|
default:
|
||||||
|
Context.error('@:hookable can only be used on functions', field.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue