continuinggg (very cool improvements)

asynced video loadings, classes rewrites, bugfixes and a lot more
This commit is contained in:
⍚~Nex
2024-08-31 04:37:00 +02:00
parent 562f1d8fc6
commit 8e81d17fb0
22 changed files with 411 additions and 205 deletions
+23 -11
View File
@@ -1,7 +1,9 @@
import funkin.system.FunkinSprite;
var tankman:FunkinSprite;
var tankTalk1, distorto:FlxSound;
var tankTalk, distorto:FlxSound;
var timers:Array<FlxTimer> = [];
var destroyDistorto:Bool = true;
function create() {
FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut});
@@ -28,29 +30,39 @@ function create() {
tankman.playAnim('tank');
tankTalk.play();
new FlxTimer().start(4.1, function(ugly:FlxTimer)
timer(4.1, function()
{
FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.4}, 0.4, {ease: FlxEase.quadOut});
FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.3}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0.45});
game.gf.playAnim('sad', false, "DANCE");
});
new FlxTimer().start(11, function(tmr:FlxTimer)
{
game.remove(tankman);
tankman.destroy();
game.camHUD.visible = true;
game.dad.visible = true;
timer(11, function() {
destroyDistorto = false;
distorto.fadeOut((Conductor.crochet / 1000) * 5, 0);
FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0});
close();
});
}
function timer(duration:Float, callBack:Void->Void) {
timers.push(new FlxTimer().start(duration, function(timer) {
timers.remove(timer);
callBack();
}));
}
function focusOn(char) {
var camPos = char.getCameraPosition();
game.camFollow.setPosition(camPos.x, camPos.y);
camPos.put();
}
function destroy() {
game.remove(tankman);
game.camHUD.visible = true;
game.dad.visible = true;
for(timer in timers) timer.cancel();
for(thing in [tankman, tankTalk]) thing.destroy();
if(destroyDistorto) distorto.destroy();
FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0});
}
+10 -7
View File
@@ -27,11 +27,6 @@ function create() {
stressCutscene.play();
stressCutscene.onComplete = function() {
for(spr in [gf, tankman, pico]) {
spr.destroy();
game.remove(spr);
}
game.camHUD.visible = game.dad.visible = game.gf.visible = true;
close();
};
@@ -59,8 +54,6 @@ function create() {
}
function update(elapsed) {
if (FlxG.keys.justPressed.F5)
FlxG.resetState();
switch(step) {
case 0:
lipSync(tankman, 0, 16750);
@@ -134,3 +127,13 @@ function focusOn(char, snap:Bool = false) {
if(snap) FlxG.camera.snapToTarget();
camPos.put();
}
function destroy() {
game.boyfriend.animation.finishCallback = null;
game.camHUD.visible = game.boyfriend.visible = game.dad.visible = game.gf.visible = true;
for(thing in [bf, gf, tankman, pico, stressCutscene]) {
if(thing == null) continue;
if(thing != stressCutscene) game.remove(thing);
thing.destroy();
}
}
+27 -14
View File
@@ -2,6 +2,8 @@ import funkin.system.FunkinSprite;
var tankman:FunkinSprite;
var tankTalk1, tankTalk2, bfBeep, distorto:FlxSound;
var timers:Array<FlxTimer> = [];
var destroyDistorto:Bool = true;
function create() {
FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut});
@@ -33,31 +35,33 @@ function create() {
tankman.playAnim('1');
tankTalk1.play();
new FlxTimer().start(3, function(tmr:FlxTimer) {
timer(3, function() {
focusOn(game.boyfriend);
new FlxTimer().start(1.5, function(t) {
timer(1.5, function() {
game.boyfriend.playAnim("singUP");
bfBeep.play();
new FlxTimer().start(1.5, function(t) {
timer(1.5, function() {
focusOn(game.dad);
tankTalk2.play();
tankman.playAnim('2');
new FlxTimer().start(6.1, function(t) {
game.remove(tankman);
tankman.destroy();
game.camHUD.visible = true;
game.dad.visible = true;
timer(6.1, function() {
destroyDistorto = false;
distorto.fadeOut((Conductor.crochet / 1000) * 5, 0);
close();
}, 1);
}, 1);
}, 1);
}, 1);
});
});
});
});
}
function timer(duration:Float, callBack:Void->Void) {
timers.push(new FlxTimer().start(duration, function(timer) {
timers.remove(timer);
callBack();
}));
}
function focusOn(char) {
@@ -65,3 +69,12 @@ function focusOn(char) {
game.camFollow.setPosition(camPos.x, camPos.y);
camPos.put();
}
function destroy() {
game.remove(tankman);
game.camHUD.visible = true;
game.dad.visible = true;
for(timer in timers) timer.cancel();
for(thing in [tankTalk1, tankTalk2, bfBeep, tankman]) thing.destroy();
if(destroyDistorto) distorto.destroy();
}
+9
View File
@@ -43,6 +43,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public var debugMode:Bool = false;
public var animDatas:Map<String, AnimData> = [];
public var globalCurFrame(get, set):Int;
/**
* ODD interval -> asynced; EVEN interval -> synced
*/
@@ -374,4 +376,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return beatInterval = v;
}
@:noCompletion private inline function get_globalCurFrame() {
return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0);
}
@:noCompletion private inline function set_globalCurFrame(val:Int) {
return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val);
}
}
+3 -5
View File
@@ -228,7 +228,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver
public override function openSubState(subState:FlxSubState) {
var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState));
if (!e.cancelled)
super.openSubState(subState);
super.openSubState(e.substate is FlxSubState ? cast e.substate : subState);
}
public override function onResize(w:Int, h:Int) {
@@ -256,11 +256,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver
if (e.cancelled)
return false;
if (skipTransOut)
if (skipTransOut || (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null))
return true;
if (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null)
return true;
openSubState(new MusicBeatTransition(nextState));
openSubState(new MusicBeatTransition(e.substate));
persistentUpdate = false;
return false;
}
+2 -2
View File
@@ -217,7 +217,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
public override function openSubState(subState:FlxSubState) {
var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState));
if (!e.cancelled)
super.openSubState(subState);
super.openSubState(e.substate is FlxSubState ? cast e.substate : subState);
}
public override function onResize(w:Int, h:Int) {
@@ -235,7 +235,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState));
if (e.cancelled)
return false;
return super.switchTo(nextState);
return super.switchTo(e.substate);
}
public override function onFocus() {
@@ -4,7 +4,7 @@ import flixel.FlxState;
final class StateEvent extends CancellableEvent {
/**
* Substate that is about to be opened/closed
* Substate or State that is about to be opened/closed
*/
public var substate:FlxState;
public var substate:FlxState; // WHY is it named substate :sob: - Nex
}
+1 -1
View File
@@ -18,8 +18,8 @@ class MainState extends FlxState {
public override function create() {
super.create();
if (!initiated) {
Main.loadGameSettings();
Constants.init();
Main.loadGameSettings();
}
initiated = true;
+26 -9
View File
@@ -9,10 +9,12 @@ import funkin.backend.FunkinSprite;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.DanceEvent;
import funkin.backend.scripting.events.CancellableEvent;
import funkin.backend.scripting.events.DirectionAnimEvent;
import funkin.backend.scripting.events.PlayAnimContext;
import funkin.backend.scripting.events.PlayAnimEvent;
import funkin.backend.scripting.events.PointEvent;
import funkin.backend.scripting.events.DrawEvent;
import funkin.backend.system.Conductor;
import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.system.interfaces.IOffsetCompatible;
@@ -147,6 +149,11 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
public function tryDance() {
var event = new CancellableEvent();
script.call("onTryDance", [event]);
if (event.cancelled)
return;
switch (lastAnimContext) {
case SING | MISS:
if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition)
@@ -173,6 +180,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
tryDance();
}
public override function measureHit(curMeasure:Int)
script.call("measureHit", [curMeasure]);
public override function stepHit(curStep:Int)
script.call("stepHit", [curStep]);
@@ -196,17 +206,24 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
return (isPlayer != playerOffsets) != (flipX != __baseFlipped);
public override function draw() {
if (isFlippedOffsets()) {
__reverseDrawProcedure = true;
flipX = !flipX;
scale.x *= -1;
var e = EventManager.get(DrawEvent).recycle();
script.call("draw", [e]);
super.draw();
if(!e.cancelled) {
if (isFlippedOffsets()) {
__reverseDrawProcedure = true;
flipX = !flipX;
scale.x *= -1;
flipX = !flipX;
scale.x *= -1;
__reverseDrawProcedure = false;
} else super.draw();
super.draw();
flipX = !flipX;
scale.x *= -1;
__reverseDrawProcedure = false;
} else super.draw();
}
script.call("postDraw", [e]);
}
public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"];
+1 -1
View File
@@ -41,8 +41,8 @@ class GameOverSubstate extends MusicBeatSubstate
super();
this.x = x;
this.y = y;
this.characterName = character != null ? character : Constants.DEFAULT_GAMEOVER_CHARACTER;
this.player = player;
this.characterName = character != null ? character : Constants.DEFAULT_GAMEOVER_CHARACTER;
this.gameOverSong = gameOverSong != null ? gameOverSong : Constants.DEFAULT_GAMEOVER_SONG;
this.lossSFXName = lossSFX != null ? lossSFXName : Constants.DEFAULT_GAMEOVER_LOSS_SFX;
this.retrySFX = retrySFX != null ? retrySFX : Constants.DEFAULT_GAMEOVER_RETRY_SFX;
+2 -2
View File
@@ -130,7 +130,7 @@ class Note extends FlxSprite
(this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * Constants.DEFAULT_NOTE_SCALE, animSuffix);
if (PlayState.instance != null)
event = PlayState.instance.scripts.event("onNoteCreation", event);
event = PlayState.instance.gameAndCharsEvent("onNoteCreation", event);
this.animSuffix = event.animSuffix;
if (!event.cancelled) {
@@ -184,7 +184,7 @@ class Note extends FlxSprite
if (PlayState.instance != null) {
PlayState.instance.splashHandler.getSplashGroup(splash);
PlayState.instance.scripts.event("onPostNoteCreation", event);
PlayState.instance.gameAndCharsEvent("onPostNoteCreation", event);
}
}
+47 -26
View File
@@ -511,7 +511,7 @@ class PlayState extends MusicBeatState
if (e.percent <= acc && (rating == null || rating.percent < e.percent))
rating = e;
var event = scripts.event("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating));
var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating));
if (!event.cancelled)
curRating = event.rating;
}
@@ -525,6 +525,24 @@ class PlayState extends MusicBeatState
return this.maxHealth = v;
}
public inline function callOnCharacters(func:String, ?parameters:Array<Dynamic>) {
if(strumLines != null) strumLines.forEachAlive(function (strLine:StrumLine) {
if (strLine.characters != null) for (character in strLine.characters)
if (character != null) character.script.call(func, parameters);
});
}
public inline function gameAndCharsCall(func:String, ?parameters:Array<Dynamic>, ?charsFunc:String) {
scripts.call(func, parameters);
callOnCharacters(charsFunc != null ? charsFunc : func, parameters);
}
public inline function gameAndCharsEvent<T:CancellableEvent>(func:String, ?event:T, ?charsFunc:String):T {
scripts.event(func, event);
callOnCharacters(charsFunc != null ? charsFunc : func, [event]);
return event;
}
@:dox(hide) override public function create()
{
Note.__customNoteTypeExists = [];
@@ -685,9 +703,9 @@ class PlayState extends MusicBeatState
// HUD INITIALIZATION & CAMERA INITIALIZATION
#if REGION
var event = EventManager.get(AmountEvent).recycle(Constants.DEFAULT_STRUM_AMOUNT);
if (!scripts.event("onPreGenerateStrums", event).cancelled) {
if (!gameAndCharsEvent("onPreGenerateStrums", event).cancelled) {
generateStrums(event.amount);
scripts.event("onPostGenerateStrums", event);
gameAndCharsEvent("onPostGenerateStrums", event);
}
for(str in strumLines)
@@ -787,7 +805,7 @@ class PlayState extends MusicBeatState
__updateNote_event = EventManager.get(NoteUpdateEvent);
scripts.call("postCreate");
gameAndCharsCall("postCreate", null, "gamePostCreate");
}
/**
@@ -851,7 +869,7 @@ class PlayState extends MusicBeatState
_startCountdownCalled = true;
inCutscene = false;
if (scripts.event("onStartCountdown", new CancellableEvent()).cancelled) return;
if (gameAndCharsEvent("onStartCountdown", new CancellableEvent()).cancelled) return;
}
startedCountdown = true;
@@ -864,14 +882,14 @@ class PlayState extends MusicBeatState
{
countdown(swagCounter++);
}, introLength);
scripts.call("onPostStartCountdown");
gameAndCharsCall("onPostStartCountdown");
}
/**
* Creates a fake countdown.
*/
public function countdown(swagCounter:Int) {
var event:CountdownEvent = scripts.event("onCountdown", EventManager.get(CountdownEvent).recycle(
var event:CountdownEvent = gameAndCharsEvent("onCountdown", EventManager.get(CountdownEvent).recycle(
swagCounter,
1,
introSounds[swagCounter],
@@ -914,12 +932,12 @@ class PlayState extends MusicBeatState
event.spriteTween = tween;
event.cancelled = false;
scripts.event("onPostCountdown", event);
gameAndCharsEvent("onPostCountdown", event);
}
@:dox(hide) function startSong():Void
{
scripts.call("onSongStart");
gameAndCharsCall("onSongStart");
startingSong = false;
inst.onComplete = endSong;
@@ -946,7 +964,7 @@ class PlayState extends MusicBeatState
updateDiscordPresence();
scripts.call("onStartSong");
gameAndCharsCall("onStartSong");
}
public override function destroy() {
@@ -1024,7 +1042,7 @@ class PlayState extends MusicBeatState
@:dox(hide)
override function openSubState(SubState:FlxSubState)
{
var event = scripts.event("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState));
var event = gameAndCharsEvent("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState));
if (!postCreated)
MusicBeatState.skipTransIn = true;
@@ -1044,13 +1062,13 @@ class PlayState extends MusicBeatState
startTimer.active = false;
}
super.openSubState(SubState);
super.openSubState(event.substate is FlxSubState ? cast event.substate : SubState);
}
@:dox(hide)
override function closeSubState()
{
var event = scripts.event("onSubstateClose", EventManager.get(StateEvent).recycle(subState));
var event = gameAndCharsEvent("onSubstateClose", EventManager.get(StateEvent).recycle(subState));
if (event.cancelled) return;
if (paused)
@@ -1084,7 +1102,7 @@ class PlayState extends MusicBeatState
inst.resume();
vocals.resume();
}
scripts.call("onFocus");
gameAndCharsCall("onFocus");
updateDiscordPresence();
super.onFocus();
}
@@ -1097,7 +1115,7 @@ class PlayState extends MusicBeatState
inst.pause();
vocals.pause();
}
scripts.call("onFocusLost");
gameAndCharsCall("onFocusLost");
updateDiscordPresence();
super.onFocusLost();
}
@@ -1116,14 +1134,14 @@ class PlayState extends MusicBeatState
strumLine.vocals.play();
}
vocals.play();
scripts.call("onVocalsResync");
gameAndCharsCall("onVocalsResync");
}
/**
* Pauses the game.
*/
public function pauseGame() {
var e = scripts.event("onGamePause", new CancellableEvent());
var e = gameAndCharsEvent("onGamePause", new CancellableEvent());
if (e.cancelled) return;
persistentUpdate = false;
@@ -1291,7 +1309,7 @@ class PlayState extends MusicBeatState
pos.x /= r;
pos.y /= r;
var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r));
var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r));
if (!event.cancelled)
camFollow.setPosition(pos.x, pos.y);
}
@@ -1339,7 +1357,10 @@ class PlayState extends MusicBeatState
if (event == null) return;
if (event.params == null) return;
if (scripts.event("onEvent", EventManager.get(EventGameEvent).recycle(event)).cancelled) return;
var e = EventManager.get(EventGameEvent).recycle(event);
gameAndCharsEvent("onEvent", e);
if (e.cancelled) return;
var event = e.event;
switch(event.name) {
case "HScript Call":
@@ -1399,7 +1420,7 @@ class PlayState extends MusicBeatState
*/
public function gameOver(?character:Character, ?deathCharID:String, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String) {
var charToUse:Character = character.getDefault(opponentMode ? dad : boyfriend); // Imma still make it check null later just in case dad or bf are also null for some weird scripts - Nex
var event:GameOverEvent = scripts.event("onGameOver", EventManager.get(GameOverEvent).recycle(
var event:GameOverEvent = gameAndCharsEvent("onGameOver", EventManager.get(GameOverEvent).recycle(
charToUse == null ? 0 : charToUse.x,
charToUse == null ? 0 : charToUse.y,
charToUse,
@@ -1428,7 +1449,7 @@ class PlayState extends MusicBeatState
openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX));
scripts.event("onPostGameOver", event);
gameAndCharsEvent("onPostGameOver", event);
}
/**
@@ -1436,7 +1457,7 @@ class PlayState extends MusicBeatState
*/
public function endSong():Void
{
scripts.call("onSongEnd");
gameAndCharsCall("onSongEnd");
canPause = false;
inst.volume = 0;
vocals.volume = 0;
@@ -1557,7 +1578,7 @@ class PlayState extends MusicBeatState
var directionID:Null<Int> = note == null ? direction : note.strumID;
if (playerID == null || directionID == null || playerID == -1) return;
var event:NoteMissEvent = scripts.event("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0));
var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0));
strumLine.onMiss.dispatch(event);
if (event.cancelled) return;
@@ -1641,11 +1662,11 @@ class PlayState extends MusicBeatState
var event:NoteHitEvent;
if (strumLine != null && !strumLine.cpu)
event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick"));
event = gameAndCharsEvent("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick"));
else
event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false));
event = gameAndCharsEvent("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false));
strumLine.onHit.dispatch(event);
scripts.event("onNoteHit", event);
gameAndCharsEvent("onNoteHit", event);
if (!event.cancelled) {
if (!note.isSustainNote) {
+1 -1
View File
@@ -41,7 +41,7 @@ class SplashHandler extends FlxTypedGroup<FunkinSprite> {
public function showSplash(name:String, strum:Strum) {
__grp = getSplashGroup(name);
var event = funkin.backend.scripting.EventManager.get(funkin.backend.scripting.events.DynamicEvent).recycle(__grp.showOnStrum(strum), strum);
event = PlayState.instance.scripts.event("onSplashShown", event);
event = PlayState.instance.gameAndCharsEvent("onSplashShown", event);
if (!event.cancelled)
add(event.value1);
+2 -2
View File
@@ -68,7 +68,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
}
if (PlayState.instance != null) {
var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, stageXML, elems));
var event = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, stageXML, elems));
elems = event.elems;
}
@@ -138,7 +138,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
}
if(PlayState.instance != null) {
sprite = PlayState.instance.scripts.event("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite;
sprite = PlayState.instance.gameAndCharsEvent("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite;
}
if (sprite != null) {
+4 -4
View File
@@ -247,7 +247,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
__justReleased.push(s.__getJustReleased(this));
}
var event = PlayState.instance.scripts.event("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id));
var event = PlayState.instance.gameAndCharsEvent("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id));
if (event.cancelled) return;
__pressed = CoolUtil.getDefault(event.pressed, []);
@@ -282,7 +282,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
forEach(function(str:Strum) {
str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this));
});
PlayState.instance.scripts.call("onPostInputUpdate");
PlayState.instance.gameAndCharsCall("onPostInputUpdate");
}
public inline function addHealth(health:Float)
@@ -317,7 +317,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix);
event.__doAnimation = !MusicBeatState.skipTransIn;
event = PlayState.instance.scripts.event("onStrumCreation", event);
event = PlayState.instance.gameAndCharsEvent("onStrumCreation", event);
if (!event.cancelled) {
babyArrow.frames = Paths.getFrames(event.sprite);
@@ -348,7 +348,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
insert(i, babyArrow);
PlayState.instance.scripts.event("onPostStrumCreation", event);
PlayState.instance.gameAndCharsEvent("onPostStrumCreation", event);
return babyArrow;
}
+58 -1
View File
@@ -1,18 +1,75 @@
package funkin.game.cutscenes;
import funkin.backend.scripting.events.NameEvent;
import funkin.menus.PauseSubState;
/**
* Substate made for cutscenes.
*/
class Cutscene extends MusicBeatSubstate {
var __callback:Void->Void;
var __pausable:Bool;
var game:PlayState = PlayState.instance;
public function new(callback:Void->Void) {
public var paused:Bool = false;
public var pauseItems:Array<String>;
public var skippable(default, set):Bool;
public function set_skippable(val:Bool):Bool {
if(!val) pauseItems.remove('Skip Cutscene');
else if(!pauseItems.contains('Skip Cutscene')) pauseItems.insert(1, 'Skip Cutscene');
return skippable = val;
}
public function new(callback:Void->Void, allowPause:Bool = true, canSkip:Bool = true) {
super();
__callback = callback;
__pausable = allowPause;
pauseItems = Constants.DEFAULT_CUTSCENE_PAUSE_ITEMS;
skippable = canSkip;
}
public override function update(elapsed:Float) {
super.update(elapsed);
if (pauseCheck()) pauseCutscene();
}
public function pauseCheck():Bool
return controls.PAUSE && __pausable;
var _before:Array<Bool> = [false, true];
public function pauseCutscene() {
_before = [game.persistentUpdate, game.persistentDraw];
game.persistentUpdate = persistentUpdate = false;
game.persistentDraw = persistentDraw = true;
paused = true;
openSubState(new PauseSubState(pauseItems, selectPauseOption));
}
public override function closeSubState() {
if (paused) {
game.persistentUpdate = _before[0];
game.persistentDraw = _before[1];
paused = false;
}
super.closeSubState();
}
public function onSkipCutscene(event:NameEvent) close();
public function onRestartCutscene(event:NameEvent) event.name = "Restart Song"; // Making the whole PlayState restart just for precaution - Nex
public function onResumeCutscene(event:NameEvent) event.name = "Resume";
public function selectPauseOption(event:NameEvent) {
switch(event.name) {
case 'Skip Cutscene': onSkipCutscene(event);
case 'Restart Cutscene': onRestartCutscene(event);
case 'Resume Cutscene': onResumeCutscene(event);
}
return true;
}
public override function close() {
@@ -13,7 +13,7 @@ import haxe.xml.Access;
/**
* Substate made for dialogue cutscenes. To use it in a scripted cutscene, call `startDialogue`.
*/
class DialogueCutscene extends Cutscene {
class DialogueCutscene extends ScriptedCutscene {
public var dialoguePath:String;
public var dialogueData:Access;
@@ -28,23 +28,29 @@ class DialogueCutscene extends Cutscene {
public var curMusic:FlxSound = null;
public static var cutscene:DialogueCutscene;
public var dialogueScript:Script;
public var dialogueScript(get, set):Script;
public function set_curLine(val:DialogueLine) {
lastLine = curLine;
return curLine = val;
}
// Backwards comapt funcs - Nex
public function set_dialogueScript(val:Script) return script = val;
public function get_dialogueScript() return script;
public function new(dialoguePath:String, callback:Void->Void) {
super(callback);
this.dialoguePath = dialoguePath;
super(this.dialoguePath = dialoguePath, callback);
camera = dialogueCamera = new FlxCamera();
dialogueCamera.bgColor = 0;
FlxG.cameras.add(dialogueCamera, false);
dialogueScript = Script.create(Paths.script(Path.withoutExtension(dialoguePath), null, dialoguePath.startsWith('assets')));
dialogueScript.setParent(cutscene = this);
dialogueScript.load();
cutscene = this;
}
public override function onErrorScriptLoading() {
Logs.trace('Could not find script for dialogue cutscene at "${scriptPath}"', WARNING, YELLOW);
}
var parentDisabler:FunkinParentDisabler;
@@ -110,22 +116,21 @@ class DialogueCutscene extends Cutscene {
next(true);
} catch(e) {
Logs.trace('Error while loading dialogue at ${dialoguePath}: ${e.toString()}', ERROR);
trace(CoolUtil.getLastExceptionStack());
var message:String = e.toString();
Logs.trace('Error while loading dialogue at ${dialoguePath}: $message', ERROR, RED);
dialogueScript.call("loadingError", [message]);
close();
}
dialogueScript.call("postCreate");
}
public override function beatHit(curBeat:Int) {
super.beatHit(curBeat);
dialogueScript.call("beatHit", [curBeat]);
public override function pauseCheck():Bool {
return super.pauseCheck() && !controls.ACCEPT; // Avoids next() and pause() being called at the same time - Nex
}
public override function update(elapsed:Float) {
super.update(elapsed);
dialogueScript.call("update", [elapsed]);
if(controls.ACCEPT) {
if(dialogueBox.dialogueEnded) next();
else dialogueBox.text.skip();
@@ -193,8 +198,6 @@ class DialogueCutscene extends Cutscene {
public override function destroy() {
if(curMusic != null && !curMusic.persist) curMusic.destroy();
dialogueScript.call("destroy");
dialogueScript.destroy();
super.destroy();
cutscene = null;
@@ -1,20 +1,25 @@
package funkin.game.cutscenes;
import haxe.io.Path;
import flixel.FlxSubState;
import funkin.backend.scripting.DummyScript;
import funkin.backend.scripting.Script;
import funkin.backend.scripting.events.CancellableEvent;
import funkin.backend.scripting.events.NameEvent;
import funkin.backend.scripting.events.StateEvent;
/**
* Substate made for scripted cutscenes.
* To add cutscenes to your songs, add a `cutscene.hx` file in your song's directory (ex: `songs/song/cutscene.hx`)
*/
class ScriptedCutscene extends Cutscene {
var scriptPath:String;
var script:Script;
var scriptPath:String;
public function new(scriptPath:String, callback:Void->Void) {
super(callback);
script = Script.create(this.scriptPath = scriptPath);
script = Script.create(this.scriptPath = Paths.script(Path.withoutExtension(scriptPath), null, scriptPath.startsWith('assets')));
script.setPublicMap(PlayState.instance.scripts.publicVariables);
script.setParent(this);
script.load();
@@ -22,32 +27,91 @@ class ScriptedCutscene extends Cutscene {
public override function create() {
super.create();
trace("fuck you");
script.call("create");
if (Std.isOfType(script, DummyScript)) {
Logs.trace('Could not find script for scripted cutscene at ${scriptPath}', ERROR, RED);
close();
}
if(Std.isOfType(script, DummyScript))
onErrorScriptLoading();
}
public override function update(elapsed:Float) {
public function onErrorScriptLoading() {
Logs.trace('Could not find script for scripted cutscene at "${scriptPath}"', ERROR, RED);
close();
}
public override function pauseCheck():Bool
{
var shouldClose = super.pauseCheck();
script.call("pauseCheck", [shouldClose]);
return shouldClose;
}
public override function update(elapsed:Float)
{
script.call("update", [elapsed]);
super.update(elapsed);
script.call("postUpdate", [elapsed]);
}
public override function beatHit(curBeat:Int) {
public override function pauseCutscene()
{
var event = new CancellableEvent();
script.call("pauseCutscene", [event]);
if(!event.cancelled) super.pauseCutscene();
}
public override function onSkipCutscene(event:NameEvent)
{
script.call("onSkipCutscene", [event]);
if(!event.cancelled) super.onSkipCutscene(event);
}
public override function onRestartCutscene(event:NameEvent)
{
script.call("onRestartCutscene", [event]);
if(!event.cancelled) super.onRestartCutscene(event);
}
public override function onResumeCutscene(event:NameEvent)
{
script.call("onResumeCutscene", [event]);
if(!event.cancelled) super.onResumeCutscene(event);
}
public override function measureHit(curMeasure:Int)
{
super.measureHit(curMeasure);
script.call("measureHit", [curMeasure]);
}
public override function beatHit(curBeat:Int)
{
super.beatHit(curBeat);
script.call("beatHit", [curBeat]);
}
public override function stepHit(curStep:Int) {
public override function stepHit(curStep:Int)
{
super.stepHit(curStep);
script.call("stepHit", [curStep]);
}
public override function openSubState(sub:FlxSubState)
{
var event = EventManager.get(StateEvent).recycle(sub);
script.call("onSubstateClose", [event]);
if(!event.cancelled) super.openSubState(event.substate is FlxSubState ? cast event.substate : sub);
}
public override function closeSubState()
{
var event = EventManager.get(StateEvent).recycle(subState);
script.call("onSubstateOpen", [event]);
if(!event.cancelled) super.closeSubState();
}
public override function destroy() {
script.call("destroy");
script.destroy();
super.destroy();
}
+87 -83
View File
@@ -1,5 +1,6 @@
package funkin.game.cutscenes;
import flixel.tweens.FlxTween;
import flixel.addons.display.FlxBackdrop;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
@@ -12,7 +13,7 @@ import haxe.xml.Access;
import sys.io.File;
#end
#if VIDEO_CUTSCENES
import hxvlc.flixel.FlxVideo;
import hxvlc.flixel.FlxVideoSprite;
#end
/**
@@ -24,22 +25,24 @@ class VideoCutscene extends Cutscene {
var localPath:String;
#if VIDEO_CUTSCENES
var video:FlxVideo;
public var skippable:Bool = true;
#end
var video:FlxVideoSprite;
final mutex = new sys.thread.Mutex();
var cutsceneCamera:FlxCamera;
var text:FunkinText;
var loadingBackdrop:FlxBackdrop;
var videoReady:Bool = false;
private var __loaded:Bool = false;
var bg:FlxSprite;
var subtitle:FunkinText;
public var subtitles:Array<CutsceneSubtitle> = [];
var curSubtitle:Int = 0;
#end
public function new(path:String, callback:Void->Void) {
super(callback);
super(callback, false);
localPath = Assets.getPath(this.path = path);
}
@@ -48,21 +51,17 @@ class VideoCutscene extends Cutscene {
// TODO: get vlc to stop autoloading those goddamn subtitles (use different file ext??)
#if VIDEO_CUTSCENES
cutsceneCamera = new FlxCamera();
cutsceneCamera.bgColor = 0;
FlxG.cameras.add(cutsceneCamera, false);
#if VIDEO_CUTSCENES
parseSubtitles();
video = new FlxVideo();
video.onEndReached.add(function()
{
video.dispose();
FlxG.removeChild(video);
});
video.onEndReached.add(close);
FlxG.addChildBelowMouse(video);
add(video = new FlxVideoSprite());
video.antialiasing = true;
video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex
video.bitmap.onEndReached.add(close);
//cover = new FlxSprite(0, FlxG.height * 0.85).makeSolid(FlxG.width + 50, FlxG.height + 50, 0xFF000000);
//cover.scrollFactor.set(0, 0);
@@ -77,41 +76,44 @@ class VideoCutscene extends Cutscene {
subtitle.alignment = CENTER;
subtitle.visible = false;
if (localPath.startsWith("[ZIP]")) {
text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video...");
text.cameras = [cutsceneCamera];
text.visible = false;
@:privateAccess
text.regenGraphic();
add(text);
text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video...");
text.cameras = [cutsceneCamera];
text.visible = false;
@:privateAccess
text.regenGraphic();
add(text);
loadingBackdrop = new FlxBackdrop(text.graphic, X);
loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height;
loadingBackdrop.cameras = [cutsceneCamera];
add(loadingBackdrop);
loadingBackdrop = new FlxBackdrop(text.graphic, X);
loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height;
loadingBackdrop.velocity.x = 70;
loadingBackdrop.cameras = [cutsceneCamera];
add(loadingBackdrop);
// ZIP PATH: EXPORT
// TODO: this but better and more ram friendly
localPath = './.temp/video-${curVideo++}.${Path.extension(path)}';
Main.execAsync(function() {
loadingBackdrop.alpha = 0;
FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut});
Main.execAsync(function() {
if (localPath.startsWith("[ZIP]")) {
// ZIP PATH: EXPORT
// TODO: this but better and more ram friendly
localPath = './.temp/video-${curVideo++}.mp4';
File.saveBytes(localPath, Assets.getBytes(path));
videoReady = true;
});
} else {
if (video.load(localPath))
new FlxTimer().start(0.001, function(tmr:FlxTimer) {
video.play();
});
else
close();
}
}
if(video.load(localPath)) new FlxTimer().start(0.001, function(_) {mutex.acquire(); onReady(); mutex.release();});
else { mutex.acquire(); close(); mutex.release(); }
});
add(bg);
add(subtitle);
#end
cameras = [cutsceneCamera];
#else
close();
#end
}
#if VIDEO_CUTSCENES
public function parseSubtitles() {
var subtitlesPath = '${Path.withoutExtension(path)}.srt';
@@ -141,22 +143,18 @@ class VideoCutscene extends Cutscene {
}
if (subtitleText.length <= 0) continue; // empty subtitle, skipping
var lastSub = subtitles.last();
if (lastSub != null && lastSub.subtitle == "" && lastSub.time > beginTime)
if (lastSub != null && lastSub.subtitle == "" && lastSub.time >= beginTime)
subtitles.pop(); // remove last subtitle auto reset to prevent bugs
subtitles.push({
subtitle: subtitleText.join("."),
time: beginTime * 1000,
color: 0xFFFFFFFF // todo
subtitle: subtitleText.join(""),
time: beginTime * 1000
});
subtitles.push({
subtitle: "",
time: endTime * 1000,
color: 0xFFFFFFFF
time: endTime * 1000
});
}
}
trace(subtitles);
}
public static function splitTime(str:String):Float {
@@ -173,50 +171,56 @@ class VideoCutscene extends Cutscene {
return time;
}
public inline function onReady() {
FlxTween.cancelTweensOf(loadingBackdrop);
FlxTween.tween(loadingBackdrop, {alpha: 0}, 0.7, {ease: FlxEase.sineInOut, onComplete: function(_) {
loadingBackdrop.destroy();
text.destroy();
}});
__pausable = __loaded = true;
video.play();
}
public override function update(elapsed:Float) {
super.update(elapsed);
#if VIDEO_CUTSCENES
if (videoReady) {
videoReady = false;
if (video.load(localPath))
new FlxTimer().start(0.001, function(tmr:FlxTimer) {
video.play();
});
else
{
close();
if (loadingBackdrop != null)
loadingBackdrop.visible = false;
return;
}
if (loadingBackdrop != null)
loadingBackdrop.visible = false;
if(loadingBackdrop == null && curSubtitle < subtitles.length && haxe.Int64.toInt(video.bitmap.time) >= subtitles[curSubtitle].time) {
setSubtitle(subtitles[curSubtitle]);
curSubtitle++;
}
@:privateAccess
var time:Int64 = video.time;
}
while (subtitles.length > 0 && subtitles[0].time < Math.round(FPHelper.i64ToDouble(time.low, time.high)))
setSubtitle(subtitles.shift());
@:dox(hide) override public function onFocus() {
if(FlxG.autoPause && !paused) video.resume();
super.onFocus();
}
if (loadingBackdrop != null) {
loadingBackdrop.x -= elapsed * FlxG.width * 0.5;
}
if (skippable && video.isPlaying && controls.ACCEPT) {
video.onEndReached.dispatch();
}
#else
close();
#end
@:dox(hide) override public function onFocusLost() {
if(FlxG.autoPause && !paused) video.pause();
super.onFocusLost();
}
public override function pauseCutscene() {
video.pause();
super.pauseCutscene();
}
public override function onResumeCutscene(event) {
video.resume();
super.onResumeCutscene(event);
}
public override function onRestartCutscene(event) {
closeSubState();
video.bitmap.position = curSubtitle = 0;
setSubtitle({time: -1, subtitle: ""});
video.resume();
}
public function setSubtitle(sub:CutsceneSubtitle) {
if (bg.visible = subtitle.visible = (sub.subtitle.length > 0)) {
subtitle.text = sub.subtitle;
subtitle.color = sub.color;
subtitle.screenCenter(X);
bg.scale.set(subtitle.width + 8, subtitle.height + 8);
bg.updateHitbox();
@@ -228,10 +232,10 @@ class VideoCutscene extends Cutscene {
FlxG.cameras.remove(cutsceneCamera, true);
super.destroy();
}
#end
}
typedef CutsceneSubtitle = {
var time:Float; // time in ms
var subtitle:String; // subtitle text
var color:FlxColor;
}
@@ -87,8 +87,11 @@ class DialogueBox extends FunkinSprite {
}
} catch(e) {
active = false;
Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR);
var message:String = e.toString();
Logs.trace('Couldn\'t load dialogue box "$name": $message', ERROR, RED);
dialogueBoxScript.call("loadingError", [message]);
}
defaultTextTypeSFX = [FlxG.sound.load(textTypeSFX)];
FlxG.sound.cache(nextSFX);
dialogueBoxScript.call("postCreate");
@@ -54,9 +54,10 @@ class DialogueCharacter extends FunkinSprite {
x = 0; y = 0;
} catch(e) {
var message:String = e.toString();
Logs.trace('Failed to load dialogue character $name: ${message}', ERROR);
Logs.trace('Failed to load dialogue character $name: ${message}', ERROR, RED);
dialogueCharScript.call("loadingError", [message]);
}
visible = false;
dialogueCharScript.call("postCreate");
}
+7 -6
View File
@@ -20,19 +20,22 @@ class PauseSubState extends MusicBeatSubstate
var grpMenuShit:FlxTypedGroup<Alphabet>;
var menuItems:Array<String> = Constants.DEFAULT_PAUSE_ITEMS;
var menuItems:Array<String>;
var curSelected:Int = 0;
var pauseMusic:FlxSound;
public var pauseScript:Script;
public var selectCall:NameEvent->Void; // Mainly for extern stuff that aren't scripts - Nex
public var game:PlayState = PlayState.instance; // shortcut
private var __cancelDefault:Bool = false;
public function new(x:Float = 0, y:Float = 0) {
public function new(?items:Array<String>, ?selectCall:NameEvent->Void) {
super();
menuItems = items != null ? items : Constants.DEFAULT_PAUSE_ITEMS;
this.selectCall = selectCall;
}
var parentDisabler:FunkinParentDisabler;
@@ -134,13 +137,11 @@ class PauseSubState extends MusicBeatSubstate
public function selectOption() {
var event = EventManager.get(NameEvent).recycle(menuItems[curSelected]);
if (selectCall != null) selectCall(event);
pauseScript.call("onSelectOption", [event]);
if (event.cancelled) return;
var daSelected:String = event.name;
switch (daSelected)
switch (event.name)
{
case "Resume":
close();