mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-29 19:15:55 -05:00
Fixed Week 4 boppers and car.
This commit is contained in:
parent
1485d27b07
commit
b6da0b5e20
37 changed files with 1107 additions and 633 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -2,7 +2,4 @@ export/
|
||||||
.vscode/
|
.vscode/
|
||||||
APIStuff.hx
|
APIStuff.hx
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
RECOVER_*.fla
|
||||||
example_mods/enaSkin/
|
|
||||||
|
|
||||||
example_mods/tricky/
|
|
17
.vscode/launch.json
vendored
17
.vscode/launch.json
vendored
|
@ -12,6 +12,21 @@
|
||||||
"name": "Haxe Eval",
|
"name": "Haxe Eval",
|
||||||
"type": "haxe-eval",
|
"type": "haxe-eval",
|
||||||
"request": "launch"
|
"request": "launch"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"name": "HTML5 Debug",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://127.0.0.1:3001",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"webRoot": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "debug: html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mac (Debug)",
|
||||||
|
"type": "hxcpp",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/export/debug/macos/bin/Funkin.app/Contents/MacOS/Funkin",
|
||||||
|
"preLaunchTask": "debug: mac"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,13 @@
|
||||||
|
|
||||||
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
|
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
AUTOMATICALLY MOVING EXAMPLE MODS INTO THE BUILD CAUSES ISSUES
|
||||||
|
Currently, this line will add the mod files to the library manifest,
|
||||||
|
which causes issues if the mod is not enabled.
|
||||||
|
If we can exclude the `mods` folder from the manifest, we can re-enable this line.
|
||||||
<assets path='example_mods' rename='mods' embed='false' exclude="*.md" />
|
<assets path='example_mods' rename='mods' embed='false' exclude="*.md" />
|
||||||
|
-->
|
||||||
<assets path='art/readme.txt' rename='do NOT readme.txt' />
|
<assets path='art/readme.txt' rename='do NOT readme.txt' />
|
||||||
|
|
||||||
<assets path="CHANGELOG.md" rename='changelog.txt' />
|
<assets path="CHANGELOG.md" rename='changelog.txt' />
|
||||||
|
@ -122,7 +128,7 @@
|
||||||
<!--haxelib name="newgrounds" unless="switch"/> -->
|
<!--haxelib name="newgrounds" unless="switch"/> -->
|
||||||
<haxelib name="faxe" if='switch' />
|
<haxelib name="faxe" if='switch' />
|
||||||
<haxelib name="polymod" />
|
<haxelib name="polymod" />
|
||||||
<haxelib name="firetongue" />
|
<haxelib name="thx.semver" />
|
||||||
|
|
||||||
<!-- <haxelib name="colyseus"/> -->
|
<!-- <haxelib name="colyseus"/> -->
|
||||||
<!-- <haxelib name="colyseus-websocket" /> -->
|
<!-- <haxelib name="colyseus-websocket" /> -->
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
{
|
{
|
||||||
"title": "Intro Mod",
|
"title": "Intro Mod",
|
||||||
"description": "An introductory mod.",
|
"description": "An introductory mod.",
|
||||||
"author": "MasterEric",
|
"contributors": [{
|
||||||
|
"name": "MasterEric"
|
||||||
|
}],
|
||||||
"api_version": "0.1.0",
|
"api_version": "0.1.0",
|
||||||
"mod_version": "1.0.0",
|
"mod_version": "1.0.0",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
{
|
{
|
||||||
"title": "Testing123",
|
"title": "Testing123",
|
||||||
"description": "Newgrounds? More like OLDGROUNDS lol.",
|
"description": "Newgrounds? More like OLDGROUNDS lol.",
|
||||||
"author": "MasterEric",
|
"contributors": [{
|
||||||
|
"name": "MasterEric"
|
||||||
|
}],
|
||||||
"api_version": "0.1.0",
|
"api_version": "0.1.0",
|
||||||
"mod_version": "1.0.0",
|
"mod_version": "1.0.0",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package;
|
package;
|
||||||
|
|
||||||
import funkin.InitState;
|
|
||||||
import funkin.MemoryCounter;
|
|
||||||
import flixel.FlxGame;
|
import flixel.FlxGame;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
|
import funkin.InitState;
|
||||||
|
import funkin.MemoryCounter;
|
||||||
import openfl.Lib;
|
import openfl.Lib;
|
||||||
import openfl.display.FPS;
|
import openfl.display.FPS;
|
||||||
import openfl.display.Sprite;
|
import openfl.display.Sprite;
|
||||||
|
@ -37,17 +37,9 @@ class Main extends Sprite
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// TODO: Ideally this should change to utilize a user interface.
|
// TODO: Replace this with loadEnabledMods().
|
||||||
// 1. Call PolymodHandler.getAllMods(). This gives you an array of ModMetadata items,
|
|
||||||
// each of which contains information about the mod including an icon.
|
|
||||||
// 2. Provide an interface to enable, disable, and reorder enabled mods.
|
|
||||||
// A design similar to that of Minecraft resource packs would be intuitive.
|
|
||||||
// 3. The interface should save (to the save file) and output an ordered array of mod IDs.
|
|
||||||
// 4. Replace the call to PolymodHandler.loadAllMods() with a call to PolymodHandler.loadModsById(ids:Array<String>).
|
|
||||||
funkin.modding.PolymodHandler.loadAllMods();
|
funkin.modding.PolymodHandler.loadAllMods();
|
||||||
|
|
||||||
funkin.i18n.FireTongueHandler.init();
|
|
||||||
|
|
||||||
if (stage != null)
|
if (stage != null)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -15,9 +15,31 @@ typedef BPMChangeEvent =
|
||||||
|
|
||||||
class Conductor
|
class Conductor
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Beats per minute of the song.
|
||||||
|
*/
|
||||||
public static var bpm:Float = 100;
|
public static var bpm:Float = 100;
|
||||||
public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds
|
|
||||||
public static var stepCrochet:Float = crochet / 4; // steps in milliseconds
|
/**
|
||||||
|
* Duration of a beat in millisecond.
|
||||||
|
*/
|
||||||
|
public static var crochet(get, null):Float;
|
||||||
|
|
||||||
|
static function get_crochet():Float
|
||||||
|
{
|
||||||
|
return ((60 / bpm) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of a step in milliseconds.
|
||||||
|
*/
|
||||||
|
public static var stepCrochet(get, null):Float;
|
||||||
|
|
||||||
|
static function get_stepCrochet():Float
|
||||||
|
{
|
||||||
|
return crochet / 4;
|
||||||
|
}
|
||||||
|
|
||||||
public static var songPosition:Float;
|
public static var songPosition:Float;
|
||||||
public static var lastSongPos:Float;
|
public static var lastSongPos:Float;
|
||||||
public static var offset:Float = 0;
|
public static var offset:Float = 0;
|
||||||
|
@ -52,12 +74,4 @@ class Conductor
|
||||||
}
|
}
|
||||||
trace("new BPM map BUDDY " + bpmChangeMap);
|
trace("new BPM map BUDDY " + bpmChangeMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function changeBPM(newBpm:Float)
|
|
||||||
{
|
|
||||||
bpm = newBpm;
|
|
||||||
|
|
||||||
crochet = ((60 / bpm) * 1000);
|
|
||||||
stepCrochet = crochet / 4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSubState;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import flixel.system.FlxSound;
|
import flixel.system.FlxSound;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
import haxe.display.Display;
|
|
||||||
import funkin.ui.PreferencesMenu;
|
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
|
import funkin.ui.PreferencesMenu;
|
||||||
|
|
||||||
class GameOverSubstate extends MusicBeatSubstate
|
class GameOverSubstate extends MusicBeatSubstate
|
||||||
{
|
{
|
||||||
|
@ -57,7 +54,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||||
add(camFollow);
|
add(camFollow);
|
||||||
|
|
||||||
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + stageSuffix));
|
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + stageSuffix));
|
||||||
// Conductor.changeBPM(100);
|
// Conductor.bpm = 100;
|
||||||
|
|
||||||
switch (PlayState.currentSong.player1)
|
switch (PlayState.currentSong.player1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,7 +31,7 @@ class LatencyState extends FlxState
|
||||||
strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5);
|
strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5);
|
||||||
add(strumLine);
|
add(strumLine);
|
||||||
|
|
||||||
Conductor.changeBPM(120);
|
Conductor.bpm = 120;
|
||||||
|
|
||||||
super.create();
|
super.create();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package funkin;
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
import flixel.graphics.frames.FlxAtlasFrames;
|
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
|
import funkin.play.PlayState;
|
||||||
import haxe.io.Path;
|
import haxe.io.Path;
|
||||||
import lime.app.Future;
|
import lime.app.Future;
|
||||||
import lime.app.Promise;
|
import lime.app.Promise;
|
||||||
|
@ -12,7 +12,6 @@ import lime.utils.AssetLibrary;
|
||||||
import lime.utils.AssetManifest;
|
import lime.utils.AssetManifest;
|
||||||
import lime.utils.Assets as LimeAssets;
|
import lime.utils.Assets as LimeAssets;
|
||||||
import openfl.utils.Assets;
|
import openfl.utils.Assets;
|
||||||
import funkin.play.PlayState;
|
|
||||||
|
|
||||||
class LoadingState extends MusicBeatState
|
class LoadingState extends MusicBeatState
|
||||||
{
|
{
|
||||||
|
@ -21,7 +20,6 @@ class LoadingState extends MusicBeatState
|
||||||
var target:FlxState;
|
var target:FlxState;
|
||||||
var stopMusic = false;
|
var stopMusic = false;
|
||||||
var callbacks:MultiCallback;
|
var callbacks:MultiCallback;
|
||||||
|
|
||||||
var danceLeft = false;
|
var danceLeft = false;
|
||||||
|
|
||||||
var loadBar:FlxSprite;
|
var loadBar:FlxSprite;
|
||||||
|
@ -117,17 +115,15 @@ class LoadingState extends MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function beatHit()
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
super.beatHit();
|
// super.beatHit() returns false if a module cancelled the event.
|
||||||
|
if (!super.beatHit())
|
||||||
|
return false;
|
||||||
|
|
||||||
// logo.animation.play('bump');
|
|
||||||
danceLeft = !danceLeft;
|
danceLeft = !danceLeft;
|
||||||
/*
|
|
||||||
if (danceLeft)
|
return true;
|
||||||
gfDance.animation.play('danceRight');
|
|
||||||
else
|
|
||||||
gfDance.animation.play('danceLeft'); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetShit:Float = 0;
|
var targetShit:Float = 0;
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
import flixel.util.FlxColor;
|
import flixel.FlxState;
|
||||||
|
import flixel.FlxSubState;
|
||||||
|
import flixel.addons.ui.FlxUIState;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import flixel.util.FlxSort;
|
||||||
|
import funkin.Conductor.BPMChangeEvent;
|
||||||
|
import funkin.modding.PolymodHandler;
|
||||||
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.util.SortUtil;
|
||||||
import funkin.Conductor.BPMChangeEvent;
|
|
||||||
import flixel.addons.ui.FlxUIState;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MusicBeatState actually represents the core utility FlxState of the game.
|
||||||
|
* It includes functionality for event handling, as well as maintaining BPM-based update events.
|
||||||
|
*/
|
||||||
class MusicBeatState extends FlxUIState
|
class MusicBeatState extends FlxUIState
|
||||||
{
|
{
|
||||||
private var curStep:Int = 0;
|
private var curStep:Int = 0;
|
||||||
|
@ -21,13 +29,23 @@ class MusicBeatState extends FlxUIState
|
||||||
public var leftWatermarkText:FlxText = null;
|
public var leftWatermarkText:FlxText = null;
|
||||||
public var rightWatermarkText:FlxText = null;
|
public var rightWatermarkText:FlxText = null;
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
initCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCallbacks()
|
||||||
|
{
|
||||||
|
subStateOpened.add(onOpenSubstateComplete);
|
||||||
|
subStateClosed.add(onCloseSubstateComplete);
|
||||||
|
}
|
||||||
|
|
||||||
override function create()
|
override function create()
|
||||||
{
|
{
|
||||||
super.create();
|
super.create();
|
||||||
|
|
||||||
if (transIn != null)
|
|
||||||
trace('reg ' + transIn.region);
|
|
||||||
|
|
||||||
createWatermarkText();
|
createWatermarkText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +53,10 @@ class MusicBeatState extends FlxUIState
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
// This can now be used in EVERY STATE YAY!
|
||||||
|
if (FlxG.keys.justPressed.F5)
|
||||||
|
debug_refreshModules();
|
||||||
|
|
||||||
// everyStep();
|
// everyStep();
|
||||||
var oldStep:Int = curStep;
|
var oldStep:Int = curStep;
|
||||||
|
|
||||||
|
@ -44,6 +66,8 @@ class MusicBeatState extends FlxUIState
|
||||||
if (oldStep != curStep && curStep >= 0)
|
if (oldStep != curStep && curStep >= 0)
|
||||||
stepHit();
|
stepHit();
|
||||||
|
|
||||||
|
FlxG.watch.addQuick("songPos", Conductor.songPosition);
|
||||||
|
|
||||||
dispatchEvent(new UpdateScriptEvent(elapsed));
|
dispatchEvent(new UpdateScriptEvent(elapsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +95,14 @@ class MusicBeatState extends FlxUIState
|
||||||
ModuleHandler.callEvent(event);
|
ModuleHandler.callEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function debug_refreshModules()
|
||||||
|
{
|
||||||
|
PolymodHandler.forceReloadAssets();
|
||||||
|
|
||||||
|
// Restart the current state, so old data is cleared.
|
||||||
|
FlxG.resetState();
|
||||||
|
}
|
||||||
|
|
||||||
private function updateBeat():Void
|
private function updateBeat():Void
|
||||||
{
|
{
|
||||||
curBeat = Math.floor(curStep / 4);
|
curBeat = Math.floor(curStep / 4);
|
||||||
|
@ -92,15 +124,97 @@ class MusicBeatState extends FlxUIState
|
||||||
curStep = lastChange.stepTime + Math.floor((Conductor.songPosition - lastChange.songTime) / Conductor.stepCrochet);
|
curStep = lastChange.stepTime + Math.floor((Conductor.songPosition - lastChange.songTime) / Conductor.stepCrochet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stepHit():Void
|
public function stepHit():Bool
|
||||||
{
|
{
|
||||||
if (curStep % 4 == 0)
|
var event = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep);
|
||||||
beatHit();
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function beatHit():Void
|
if (curStep % 4 == 0)
|
||||||
|
beatHit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beatHit():Bool
|
||||||
{
|
{
|
||||||
|
var event = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, curBeat, curStep);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lastBeatHitTime = Conductor.songPosition;
|
lastBeatHitTime = Conductor.songPosition;
|
||||||
// do literally nothing dumbass
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the state, by redoing the render order of all sprites.
|
||||||
|
* It does this based on the `zIndex` of each prop.
|
||||||
|
*/
|
||||||
|
public function refresh()
|
||||||
|
{
|
||||||
|
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function switchTo(nextState:FlxState):Bool
|
||||||
|
{
|
||||||
|
var event = new StateChangeScriptEvent(ScriptEvent.STATE_CHANGE_BEGIN, nextState, true);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.switchTo(nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function openSubState(targetSubstate:FlxSubState):Void
|
||||||
|
{
|
||||||
|
var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubstate, true);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.openSubState(targetSubstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenSubstateComplete(targetState:FlxSubState):Void
|
||||||
|
{
|
||||||
|
dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_END, targetState, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function closeSubState():Void
|
||||||
|
{
|
||||||
|
var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_BEGIN, this.subState, true);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.closeSubState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCloseSubstateComplete(targetState:FlxSubState):Void
|
||||||
|
{
|
||||||
|
dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_END, targetState, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
import funkin.Conductor.BPMChangeEvent;
|
|
||||||
import flixel.FlxSubState;
|
import flixel.FlxSubState;
|
||||||
|
import funkin.Conductor.BPMChangeEvent;
|
||||||
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
import funkin.modding.module.ModuleHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MusicBeatSubstate reincorporates the functionality of MusicBeatState into an FlxSubState.
|
||||||
|
*/
|
||||||
class MusicBeatSubstate extends FlxSubState
|
class MusicBeatSubstate extends FlxSubState
|
||||||
{
|
{
|
||||||
public function new()
|
public function new()
|
||||||
|
@ -53,6 +58,11 @@ class MusicBeatSubstate extends FlxSubState
|
||||||
beatHit();
|
beatHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dispatchEvent(event:ScriptEvent)
|
||||||
|
{
|
||||||
|
ModuleHandler.callEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
public function beatHit():Void
|
public function beatHit():Void
|
||||||
{
|
{
|
||||||
// do literally nothing dumbass
|
// do literally nothing dumbass
|
||||||
|
|
|
@ -4,13 +4,7 @@ import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.input.android.FlxAndroidKey;
|
|
||||||
import flixel.input.android.FlxAndroidKeys;
|
|
||||||
import flixel.input.gamepad.FlxGamepad;
|
import flixel.input.gamepad.FlxGamepad;
|
||||||
import flixel.input.gamepad.id.SwitchJoyconLeftID;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import flixel.math.FlxRect;
|
|
||||||
import flixel.text.FlxText;
|
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
|
@ -19,29 +13,20 @@ import funkin.audiovis.SpectogramSprite;
|
||||||
import funkin.shaderslmfao.BuildingShaders;
|
import funkin.shaderslmfao.BuildingShaders;
|
||||||
import funkin.shaderslmfao.ColorSwap;
|
import funkin.shaderslmfao.ColorSwap;
|
||||||
import funkin.shaderslmfao.TitleOutline;
|
import funkin.shaderslmfao.TitleOutline;
|
||||||
import funkin.ui.PreferencesMenu;
|
import funkin.ui.AtlasText;
|
||||||
import lime.app.Application;
|
import funkin.util.Constants;
|
||||||
import lime.graphics.Image;
|
|
||||||
import lime.media.AudioContext;
|
|
||||||
import lime.ui.Window;
|
|
||||||
import openfl.Assets;
|
import openfl.Assets;
|
||||||
import openfl.display.Sprite;
|
import openfl.display.Sprite;
|
||||||
import openfl.events.AsyncErrorEvent;
|
import openfl.events.AsyncErrorEvent;
|
||||||
import openfl.events.Event;
|
|
||||||
import openfl.events.MouseEvent;
|
import openfl.events.MouseEvent;
|
||||||
import openfl.events.NetStatusEvent;
|
import openfl.events.NetStatusEvent;
|
||||||
import openfl.media.Video;
|
import openfl.media.Video;
|
||||||
import openfl.net.NetConnection;
|
|
||||||
import openfl.net.NetStream;
|
import openfl.net.NetStream;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
#if desktop
|
#if desktop
|
||||||
import sys.FileSystem;
|
|
||||||
import sys.io.File;
|
|
||||||
import sys.thread.Thread;
|
|
||||||
#end
|
#end
|
||||||
|
|
||||||
class TitleState extends MusicBeatState
|
class TitleState extends MusicBeatState
|
||||||
{
|
{
|
||||||
public static var initialized:Bool = false;
|
public static var initialized:Bool = false;
|
||||||
|
@ -158,9 +143,9 @@ class TitleState extends MusicBeatState
|
||||||
{
|
{
|
||||||
FlxG.sound.playMusic(Paths.music('freakyMenu'), 0);
|
FlxG.sound.playMusic(Paths.music('freakyMenu'), 0);
|
||||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||||
|
Conductor.bpm = Constants.FREAKY_MENU_BPM;
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.changeBPM(102);
|
|
||||||
persistentUpdate = true;
|
persistentUpdate = true;
|
||||||
|
|
||||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||||
|
@ -189,8 +174,6 @@ class TitleState extends MusicBeatState
|
||||||
gfDance.antialiasing = true;
|
gfDance.antialiasing = true;
|
||||||
add(gfDance);
|
add(gfDance);
|
||||||
|
|
||||||
trace('MACRO TEST: ${gfDance.zIndex}');
|
|
||||||
|
|
||||||
// alphaShader.shader.funnyShit.input = gfDance.pixels; // old shit
|
// alphaShader.shader.funnyShit.input = gfDance.pixels; // old shit
|
||||||
|
|
||||||
logoBl.shader = alphaShader.shader;
|
logoBl.shader = alphaShader.shader;
|
||||||
|
@ -220,6 +203,7 @@ class TitleState extends MusicBeatState
|
||||||
|
|
||||||
blackScreen = bg.clone();
|
blackScreen = bg.clone();
|
||||||
credGroup.add(blackScreen);
|
credGroup.add(blackScreen);
|
||||||
|
credGroup.add(textGroup);
|
||||||
|
|
||||||
// var atlasBullShit:FlxSprite = new FlxSprite();
|
// var atlasBullShit:FlxSprite = new FlxSprite();
|
||||||
// atlasBullShit.frames = CoolUtil.fromAnimate(Paths.image('money'), Paths.file('images/money.json'));
|
// atlasBullShit.frames = CoolUtil.fromAnimate(Paths.image('money'), Paths.file('images/money.json'));
|
||||||
|
@ -382,13 +366,6 @@ class TitleState extends MusicBeatState
|
||||||
pressedEnter = true;
|
pressedEnter = true;
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
// a faster intro thing lol!
|
|
||||||
if (pressedEnter && transitioning && skippedIntro)
|
|
||||||
{
|
|
||||||
FlxG.switchState(new MainMenuState());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressedEnter && !transitioning && skippedIntro)
|
if (pressedEnter && !transitioning && skippedIntro)
|
||||||
{
|
{
|
||||||
if (FlxG.sound.music != null)
|
if (FlxG.sound.music != null)
|
||||||
|
@ -434,6 +411,7 @@ class TitleState extends MusicBeatState
|
||||||
Assets.cache.clear(Paths.image('logoBumpin'));
|
Assets.cache.clear(Paths.image('logoBumpin'));
|
||||||
Assets.cache.clear(Paths.image('titleEnter'));
|
Assets.cache.clear(Paths.image('titleEnter'));
|
||||||
// ngSpr??
|
// ngSpr??
|
||||||
|
FlxG.switchState(targetState);
|
||||||
});
|
});
|
||||||
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
|
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
|
||||||
}
|
}
|
||||||
|
@ -503,39 +481,47 @@ class TitleState extends MusicBeatState
|
||||||
var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music);
|
var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music);
|
||||||
add(spec);
|
add(spec);
|
||||||
|
|
||||||
Conductor.changeBPM(190);
|
Conductor.bpm = 190;
|
||||||
FlxG.camera.flash(FlxColor.WHITE, 1);
|
FlxG.camera.flash(FlxColor.WHITE, 1);
|
||||||
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
|
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCoolText(textArray:Array<String>)
|
function createCoolText(textArray:Array<String>)
|
||||||
{
|
{
|
||||||
|
if (credGroup == null || textGroup == null)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i in 0...textArray.length)
|
for (i in 0...textArray.length)
|
||||||
{
|
{
|
||||||
var money:Alphabet = new Alphabet(0, 0, textArray[i], true, false);
|
var money:AtlasText = new AtlasText(0, 0, textArray[i], AtlasFont.BOLD);
|
||||||
money.screenCenter(X);
|
money.screenCenter(X);
|
||||||
money.y += (i * 60) + 200;
|
money.y += (i * 60) + 200;
|
||||||
credGroup.add(money);
|
// credGroup.add(money);
|
||||||
textGroup.add(money);
|
textGroup.add(money);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMoreText(text:String)
|
function addMoreText(text:String)
|
||||||
{
|
{
|
||||||
|
if (credGroup == null || textGroup == null)
|
||||||
|
return;
|
||||||
|
|
||||||
lime.ui.Haptic.vibrate(100, 100);
|
lime.ui.Haptic.vibrate(100, 100);
|
||||||
|
|
||||||
var coolText:Alphabet = new Alphabet(0, 0, text, true, false);
|
var coolText:AtlasText = new AtlasText(0, 0, text, AtlasFont.BOLD);
|
||||||
coolText.screenCenter(X);
|
coolText.screenCenter(X);
|
||||||
coolText.y += (textGroup.length * 60) + 200;
|
coolText.y += (textGroup.length * 60) + 200;
|
||||||
credGroup.add(coolText);
|
|
||||||
textGroup.add(coolText);
|
textGroup.add(coolText);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCoolText()
|
function deleteCoolText()
|
||||||
{
|
{
|
||||||
|
if (credGroup == null || textGroup == null)
|
||||||
|
return;
|
||||||
|
|
||||||
while (textGroup.members.length > 0)
|
while (textGroup.members.length > 0)
|
||||||
{
|
{
|
||||||
credGroup.remove(textGroup.members[0], true);
|
// credGroup.remove(textGroup.members[0], true);
|
||||||
textGroup.remove(textGroup.members[0], true);
|
textGroup.remove(textGroup.members[0], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -543,9 +529,11 @@ class TitleState extends MusicBeatState
|
||||||
var isRainbow:Bool = false;
|
var isRainbow:Bool = false;
|
||||||
var skippedIntro:Bool = false;
|
var skippedIntro:Bool = false;
|
||||||
|
|
||||||
override function beatHit()
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
super.beatHit();
|
// super.beatHit() returns false if a module cancelled the event.
|
||||||
|
if (!super.beatHit())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!skippedIntro)
|
if (!skippedIntro)
|
||||||
{
|
{
|
||||||
|
@ -605,6 +593,8 @@ class TitleState extends MusicBeatState
|
||||||
else
|
else
|
||||||
gfDance.animation.play('danceLeft');
|
gfDance.animation.play('danceLeft');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function skipIntro():Void
|
function skipIntro():Void
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
package funkin.charting;
|
package funkin.charting;
|
||||||
|
|
||||||
import funkin.Conductor.BPMChangeEvent;
|
|
||||||
import funkin.Note.NoteData;
|
|
||||||
import funkin.Section.SwagSection;
|
|
||||||
import funkin.SongLoad.SwagSong;
|
|
||||||
import funkin.audiovis.ABotVis;
|
|
||||||
import funkin.audiovis.PolygonSpectogram;
|
|
||||||
import funkin.audiovis.SpectogramSprite;
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.addons.display.FlxGridOverlay;
|
import flixel.addons.display.FlxGridOverlay;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
|
@ -23,14 +16,21 @@ import flixel.system.FlxSound;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.ui.FlxButton;
|
import flixel.ui.FlxButton;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
|
import funkin.Conductor.BPMChangeEvent;
|
||||||
|
import funkin.Note.NoteData;
|
||||||
|
import funkin.Section.SwagSection;
|
||||||
|
import funkin.SongLoad.SwagSong;
|
||||||
|
import funkin.audiovis.ABotVis;
|
||||||
|
import funkin.audiovis.PolygonSpectogram;
|
||||||
|
import funkin.audiovis.SpectogramSprite;
|
||||||
|
import funkin.play.PlayState;
|
||||||
|
import funkin.rendering.MeshRender;
|
||||||
import haxe.Json;
|
import haxe.Json;
|
||||||
import lime.media.AudioBuffer;
|
import lime.media.AudioBuffer;
|
||||||
import lime.utils.Assets;
|
import lime.utils.Assets;
|
||||||
import openfl.events.Event;
|
import openfl.events.Event;
|
||||||
import openfl.events.IOErrorEvent;
|
import openfl.events.IOErrorEvent;
|
||||||
import openfl.net.FileReference;
|
import openfl.net.FileReference;
|
||||||
import funkin.rendering.MeshRender;
|
|
||||||
import funkin.play.PlayState;
|
|
||||||
|
|
||||||
using Lambda;
|
using Lambda;
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
@ -144,7 +144,7 @@ class ChartingState extends MusicBeatState
|
||||||
updateGrid();
|
updateGrid();
|
||||||
|
|
||||||
loadSong(_song.song);
|
loadSong(_song.song);
|
||||||
Conductor.changeBPM(_song.bpm);
|
Conductor.bpm = _song.bpm;
|
||||||
Conductor.mapBPMChanges(_song);
|
Conductor.mapBPMChanges(_song);
|
||||||
|
|
||||||
bpmTxt = new FlxText(1000, 50, 0, "", 16);
|
bpmTxt = new FlxText(1000, 50, 0, "", 16);
|
||||||
|
@ -545,7 +545,7 @@ class ChartingState extends MusicBeatState
|
||||||
{
|
{
|
||||||
tempBpm = nums.value;
|
tempBpm = nums.value;
|
||||||
Conductor.mapBPMChanges(_song);
|
Conductor.mapBPMChanges(_song);
|
||||||
Conductor.changeBPM(nums.value);
|
Conductor.bpm = nums.value;
|
||||||
}
|
}
|
||||||
else if (wname == 'note_susLength')
|
else if (wname == 'note_susLength')
|
||||||
{
|
{
|
||||||
|
@ -975,9 +975,9 @@ class ChartingState extends MusicBeatState
|
||||||
_song.bpm = tempBpm;
|
_song.bpm = tempBpm;
|
||||||
|
|
||||||
/* if (FlxG.keys.justPressed.UP)
|
/* if (FlxG.keys.justPressed.UP)
|
||||||
Conductor.changeBPM(Conductor.bpm + 1);
|
Conductor.bpm = Conductor.bpm + 1;
|
||||||
if (FlxG.keys.justPressed.DOWN)
|
if (FlxG.keys.justPressed.DOWN)
|
||||||
Conductor.changeBPM(Conductor.bpm - 1); */
|
Conductor.bpm = Conductor.bpm - 1; */
|
||||||
|
|
||||||
var shiftThing:Int = 1;
|
var shiftThing:Int = 1;
|
||||||
if (FlxG.keys.pressed.SHIFT)
|
if (FlxG.keys.pressed.SHIFT)
|
||||||
|
@ -1213,7 +1213,7 @@ class ChartingState extends MusicBeatState
|
||||||
|
|
||||||
if (SongLoad.getSong()[curSection].changeBPM && SongLoad.getSong()[curSection].bpm > 0)
|
if (SongLoad.getSong()[curSection].changeBPM && SongLoad.getSong()[curSection].bpm > 0)
|
||||||
{
|
{
|
||||||
Conductor.changeBPM(SongLoad.getSong()[curSection].bpm);
|
Conductor.bpm = SongLoad.getSong()[curSection].bpm;
|
||||||
FlxG.log.add('CHANGED BPM!');
|
FlxG.log.add('CHANGED BPM!');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1223,7 +1223,7 @@ class ChartingState extends MusicBeatState
|
||||||
for (i in 0...curSection)
|
for (i in 0...curSection)
|
||||||
if (SongLoad.getSong()[i].changeBPM)
|
if (SongLoad.getSong()[i].changeBPM)
|
||||||
daBPM = SongLoad.getSong()[i].bpm;
|
daBPM = SongLoad.getSong()[i].bpm;
|
||||||
Conductor.changeBPM(daBPM);
|
Conductor.bpm = daBPM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* // PORT BULLSHIT, INCASE THERE'S NO SUSTAIN DATA FOR A NOTE
|
/* // PORT BULLSHIT, INCASE THERE'S NO SUSTAIN DATA FOR A NOTE
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
package funkin.i18n;
|
|
||||||
|
|
||||||
import firetongue.FireTongue;
|
|
||||||
|
|
||||||
class FireTongueHandler
|
|
||||||
{
|
|
||||||
static final DEFAULT_LOCALE = 'en-US';
|
|
||||||
// static final DEFAULT_LOCALE = 'pt-BR';
|
|
||||||
static final LOCALE_DIR = 'assets/locales/';
|
|
||||||
|
|
||||||
static var tongue:FireTongue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the FireTongue instance.
|
|
||||||
* This will automatically start with the default locale for you.
|
|
||||||
*/
|
|
||||||
public static function init():Void
|
|
||||||
{
|
|
||||||
tongue = new FireTongue(OPENFL, // Haxe framework being used.
|
|
||||||
// This should really have been a parameterized object...
|
|
||||||
null, // Function to check if a file exists. Specify null to use the one from the framework.
|
|
||||||
null, // Function to retrieve the text of a file. Specify null to use the one from the framework.
|
|
||||||
null, // Function to get a list of files in a directory. Specify null to use the one from the framework.
|
|
||||||
firetongue.FireTongue.Case.Upper);
|
|
||||||
|
|
||||||
// TODO: Make this use the language from the user's preferences.
|
|
||||||
setLanguage(DEFAULT_LOCALE);
|
|
||||||
|
|
||||||
trace('[FIRETONGUE] Initialized. Available locales: ${tongue.locales.join(', ')}');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switch the language used by FireTongue.
|
|
||||||
* @param locale The name of the locale to use, such as `en-US`.
|
|
||||||
*/
|
|
||||||
public static function setLanguage(locale:String):Void
|
|
||||||
{
|
|
||||||
tongue.initialize({
|
|
||||||
locale: locale, // The locale to load.
|
|
||||||
|
|
||||||
finishedCallback: onFinishLoad, // Function run when the locale is loaded.
|
|
||||||
directory: LOCALE_DIR, // Folder (relative to assets/) to load data from.
|
|
||||||
replaceMissing: false, // If true, missing flags fallback to the default locale.
|
|
||||||
checkMissing: true, // If true, check for and store the list of missing flags for this locale.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called when FireTongue finishes loading a language.
|
|
||||||
*/
|
|
||||||
static function onFinishLoad()
|
|
||||||
{
|
|
||||||
if (tongue == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
trace('[FIRETONGUE] Finished loading locale: ${tongue.locale}');
|
|
||||||
if (tongue.missingFlags != null)
|
|
||||||
{
|
|
||||||
if (tongue.missingFlags.get(tongue.locale) != null && tongue.missingFlags.get(tongue.locale).length != 0)
|
|
||||||
{
|
|
||||||
trace('[FIRETONGUE] Missing flags: ${tongue.missingFlags.get(tongue.locale).join(', ')}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace('[FIRETONGUE] No missing flags for this locale. (Note: Another locale has missing flags.)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace('[FIRETONGUE] No missing flags.');
|
|
||||||
}
|
|
||||||
|
|
||||||
trace('[FIRETONGUE] HELLO_WORLD = ${t("HELLO_WORLD")}');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a localized string based on the given key.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* import i18n.FiretongueHandler.t;
|
|
||||||
* trace(t('HELLO')); // Prints "Hello!"
|
|
||||||
*
|
|
||||||
* @param key The key to use to retrieve the localized string.
|
|
||||||
* @param context The data file to load the key from.
|
|
||||||
* @return The localized string.
|
|
||||||
*/
|
|
||||||
public static function t(key:String, context:String = 'data'):String
|
|
||||||
{
|
|
||||||
// The localization strings can be stored all in one file,
|
|
||||||
// or split into several contexts.
|
|
||||||
return tongue.get(key, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a localized string while replacing specific values.
|
|
||||||
* In this way, you can use the same invocation call to properly localize
|
|
||||||
* a variety of different languages with distinct grammar.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* import i18n.FiretongueHandler.f;
|
|
||||||
* trace(f('COLLECT_X_APPLES', 'data', ['<X>'], ['10']); // Prints "Collect 10 apples!"
|
|
||||||
*
|
|
||||||
* @param key The key to use to retrieve the localized string.
|
|
||||||
* @param context The data file to load the key from.
|
|
||||||
* @param flags The flags to replace in the string.
|
|
||||||
* @param values The values to replace those flags with.
|
|
||||||
* @return The localized string.
|
|
||||||
*/
|
|
||||||
public static function f(key:String, context:String = 'data', flags:Array<String> = null, values:Array<String> = null):String
|
|
||||||
{
|
|
||||||
var str = t(key, context);
|
|
||||||
return firetongue.Replace.flags(str, flags, values);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
# i18n
|
|
||||||
|
|
||||||
This package contains functions used for internationalization (i18n).
|
|
|
@ -23,6 +23,20 @@ interface IStateChangingScriptedClass extends IScriptedClass
|
||||||
{
|
{
|
||||||
public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
|
public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
|
||||||
public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
|
public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
|
||||||
|
|
||||||
|
public function onSubstateOpenBegin(event:SubStateScriptEvent):Void;
|
||||||
|
public function onSubstateOpenEnd(event:SubStateScriptEvent):Void;
|
||||||
|
public function onSubstateCloseBegin(event:SubStateScriptEvent):Void;
|
||||||
|
public function onSubstateCloseEnd(event:SubStateScriptEvent):Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a set of callbacks available to scripted classes which represent notes.
|
||||||
|
*/
|
||||||
|
interface INoteScriptedClass extends IScriptedClass
|
||||||
|
{
|
||||||
|
public function onNoteHit(event:NoteScriptEvent):Void;
|
||||||
|
public function onNoteMiss(event:NoteScriptEvent):Void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,18 +54,18 @@ interface IStateChangingScriptedClass extends IScriptedClass
|
||||||
*/
|
*/
|
||||||
interface IPlayStateScriptedClass extends IScriptedClass
|
interface IPlayStateScriptedClass extends IScriptedClass
|
||||||
{
|
{
|
||||||
public function onPause(event:ScriptEvent):Void;
|
public function onPause(event:PauseScriptEvent):Void;
|
||||||
public function onResume(event:ScriptEvent):Void;
|
public function onResume(event:ScriptEvent):Void;
|
||||||
|
|
||||||
public function onSongLoaded(eent:SongLoadScriptEvent):Void;
|
public function onSongLoaded(eent:SongLoadScriptEvent):Void;
|
||||||
public function onSongStart(event:ScriptEvent):Void;
|
public function onSongStart(event:ScriptEvent):Void;
|
||||||
public function onSongEnd(event:ScriptEvent):Void;
|
public function onSongEnd(event:ScriptEvent):Void;
|
||||||
public function onSongReset(event:ScriptEvent):Void;
|
|
||||||
public function onGameOver(event:ScriptEvent):Void;
|
public function onGameOver(event:ScriptEvent):Void;
|
||||||
public function onGameRetry(event:ScriptEvent):Void;
|
public function onSongRetry(event:ScriptEvent):Void;
|
||||||
|
|
||||||
public function onNoteHit(event:NoteScriptEvent):Void;
|
public function onNoteHit(event:NoteScriptEvent):Void;
|
||||||
public function onNoteMiss(event:NoteScriptEvent):Void;
|
public function onNoteMiss(event:NoteScriptEvent):Void;
|
||||||
|
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent):Void;
|
||||||
|
|
||||||
public function onStepHit(event:SongTimeScriptEvent):Void;
|
public function onStepHit(event:SongTimeScriptEvent):Void;
|
||||||
public function onBeatHit(event:SongTimeScriptEvent):Void;
|
public function onBeatHit(event:SongTimeScriptEvent):Void;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package funkin.modding;
|
package funkin.modding;
|
||||||
|
|
||||||
import polymod.Polymod.ModMetadata;
|
import funkin.modding.module.ModuleHandler;
|
||||||
|
import funkin.play.stage.StageData;
|
||||||
import polymod.Polymod;
|
import polymod.Polymod;
|
||||||
import polymod.backends.OpenFLBackend;
|
|
||||||
import polymod.backends.PolymodAssets.PolymodAssetType;
|
import polymod.backends.PolymodAssets.PolymodAssetType;
|
||||||
import polymod.format.ParseRules.LinesParseFormat;
|
|
||||||
import polymod.format.ParseRules.TextFileFormat;
|
import polymod.format.ParseRules.TextFileFormat;
|
||||||
|
|
||||||
class PolymodHandler
|
class PolymodHandler
|
||||||
|
@ -31,6 +30,15 @@ class PolymodHandler
|
||||||
loadModsById(getAllModIds());
|
loadModsById(getAllModIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the game with configured mods enabled with Polymod.
|
||||||
|
*/
|
||||||
|
public static function loadEnabledMods()
|
||||||
|
{
|
||||||
|
trace("Initializing Polymod (using configured mods)...");
|
||||||
|
loadModsById(getEnabledModIds());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the game without any mods enabled with Polymod.
|
* Loads the game without any mods enabled with Polymod.
|
||||||
*/
|
*/
|
||||||
|
@ -165,4 +173,61 @@ class PolymodHandler
|
||||||
var modIds = [for (i in getAllMods()) i.id];
|
var modIds = [for (i in getAllMods()) i.id];
|
||||||
return modIds;
|
return modIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function setEnabledMods(newModList:Array<String>):Void
|
||||||
|
{
|
||||||
|
FlxG.save.data.enabledMods = newModList;
|
||||||
|
// Make sure to COMMIT the changes.
|
||||||
|
FlxG.save.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of enabled mods.
|
||||||
|
* @return Array<String>
|
||||||
|
*/
|
||||||
|
public static function getEnabledModIds():Array<String>
|
||||||
|
{
|
||||||
|
if (FlxG.save.data.enabledMods == null)
|
||||||
|
{
|
||||||
|
// NOTE: If the value is null, the enabled mod list is unconfigured.
|
||||||
|
// Currently, we default to disabling newly installed mods.
|
||||||
|
// If we want to auto-enable new mods, but otherwise leave the configured list in place,
|
||||||
|
// we will need some custom logic.
|
||||||
|
FlxG.save.data.enabledMods = [];
|
||||||
|
}
|
||||||
|
return FlxG.save.data.enabledMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEnabledMods():Array<ModMetadata>
|
||||||
|
{
|
||||||
|
var modIds = getEnabledModIds();
|
||||||
|
var modMetadata = getAllMods();
|
||||||
|
var enabledMods = [];
|
||||||
|
for (item in modMetadata)
|
||||||
|
{
|
||||||
|
if (modIds.indexOf(item.id) != -1)
|
||||||
|
{
|
||||||
|
enabledMods.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enabledMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function forceReloadAssets()
|
||||||
|
{
|
||||||
|
// Forcibly clear scripts so that scripts can be edited.
|
||||||
|
ModuleHandler.clearModuleCache();
|
||||||
|
polymod.hscript.PolymodScriptClass.clearScriptClasses();
|
||||||
|
|
||||||
|
// Forcibly reload Polymod so it finds any new files.
|
||||||
|
loadEnabledMods();
|
||||||
|
|
||||||
|
// Reload scripted classes so stages and modules will update.
|
||||||
|
polymod.hscript.PolymodScriptClass.registerAllScriptClasses();
|
||||||
|
|
||||||
|
// Reload the stages in cache.
|
||||||
|
// TODO: Currently this causes lag since you're reading a lot of files, how to fix?
|
||||||
|
StageDataParser.loadStageCache();
|
||||||
|
ModuleHandler.loadModuleCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package funkin.modding.events;
|
package funkin.modding.events;
|
||||||
|
|
||||||
|
import flixel.FlxState;
|
||||||
|
import flixel.FlxSubState;
|
||||||
|
import funkin.Note.NoteDir;
|
||||||
import funkin.play.Countdown.CountdownStep;
|
import funkin.play.Countdown.CountdownStep;
|
||||||
import openfl.events.EventType;
|
import openfl.events.EventType;
|
||||||
import openfl.events.KeyboardEvent;
|
import openfl.events.KeyboardEvent;
|
||||||
|
@ -83,6 +86,15 @@ class ScriptEvent
|
||||||
*/
|
*/
|
||||||
public static inline final NOTE_MISS:ScriptEventType = "NOTE_MISS";
|
public static inline final NOTE_MISS:ScriptEventType = "NOTE_MISS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a character presses a note when there was none there, causing them to lose health.
|
||||||
|
* Important information such as direction pressed, etc. are all provided.
|
||||||
|
*
|
||||||
|
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
|
||||||
|
* avoiding lost health/score and preventing the miss animation.
|
||||||
|
*/
|
||||||
|
public static inline final NOTE_GHOST_MISS:ScriptEventType = "NOTE_GHOST_MISS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
|
* Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
|
||||||
*
|
*
|
||||||
|
@ -97,13 +109,6 @@ class ScriptEvent
|
||||||
*/
|
*/
|
||||||
public static inline final SONG_END:ScriptEventType = "SONG_END";
|
public static inline final SONG_END:ScriptEventType = "SONG_END";
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the song is reset. This can happen from the pause menu or the game over screen.
|
|
||||||
*
|
|
||||||
* This event is not cancelable.
|
|
||||||
*/
|
|
||||||
public static inline final SONG_RESET:ScriptEventType = "SONG_RESET";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the countdown begins. This occurs before the song starts.
|
* Called when the countdown begins. This occurs before the song starts.
|
||||||
*
|
*
|
||||||
|
@ -130,18 +135,19 @@ class ScriptEvent
|
||||||
public static inline final COUNTDOWN_END:ScriptEventType = "COUNTDOWN_END";
|
public static inline final COUNTDOWN_END:ScriptEventType = "COUNTDOWN_END";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game over screen triggers and the death animation plays.
|
* Called before the game over screen triggers and the death animation plays.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final GAME_OVER:ScriptEventType = "GAME_OVER";
|
public static inline final GAME_OVER:ScriptEventType = "GAME_OVER";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player presses a key to restart the game after the death animation.
|
* Called when the player presses a key to restart the game.
|
||||||
|
* This can happen from the pause menu or the game over screen.
|
||||||
*
|
*
|
||||||
* This event IS cancelable! Canceling this event will prevent the game from restarting.
|
* This event IS cancelable! Canceling this event will prevent the game from restarting.
|
||||||
*/
|
*/
|
||||||
public static inline final GAME_RETRY:ScriptEventType = "GAME_RETRY";
|
public static inline final SONG_RETRY:ScriptEventType = "SONG_RETRY";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player pushes down any key on the keyboard.
|
* Called when the player pushes down any key on the keyboard.
|
||||||
|
@ -166,11 +172,46 @@ 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.
|
* Called when the game is about to switch the current FlxState.
|
||||||
*
|
*
|
||||||
* This event is not cancelable.
|
* This event is not cancelable.
|
||||||
*/
|
*/
|
||||||
public static inline final STATE_ENTER:ScriptEventType = "STATE_ENTER";
|
public static inline final STATE_CHANGE_BEGIN:ScriptEventType = "STATE_CHANGE_BEGIN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game has finished switching the current FlxState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final STATE_CHANGE_END:ScriptEventType = "STATE_CHANGE_END";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game is about to open a new FlxSubState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = "SUBSTATE_OPEN_BEGIN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game has finished opening a new FlxSubState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final SUBSTATE_OPEN_END:ScriptEventType = "SUBSTATE_OPEN_END";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game is about to close the current FlxSubState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = "SUBSTATE_CLOSE_BEGIN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the game has finished closing the current FlxSubState.
|
||||||
|
*
|
||||||
|
* This event is not cancelable.
|
||||||
|
*/
|
||||||
|
public static inline final SUBSTATE_CLOSE_END:ScriptEventType = "SUBSTATE_CLOSE_END";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the game is exiting the current FlxState.
|
* Called when the game is exiting the current FlxState.
|
||||||
|
@ -179,7 +220,7 @@ class ScriptEvent
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* 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_START should prevent the countdown from starting,
|
||||||
* until another script restarts it, or cancelling NOTE_HIT should cause the note to be missed.
|
* until another script restarts it, or cancelling NOTE_HIT should cause the note to be missed.
|
||||||
*/
|
*/
|
||||||
public var cancelable(default, null):Bool;
|
public var cancelable(default, null):Bool;
|
||||||
|
@ -209,7 +250,7 @@ class ScriptEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this function on a cancelable event to cancel the associated behavior.
|
* Call this function on a cancelable event to cancel the associated behavior.
|
||||||
* For example, cancelling COUNTDOWN_BEGIN will prevent the countdown from starting.
|
* For example, cancelling COUNTDOWN_START will prevent the countdown from starting.
|
||||||
*/
|
*/
|
||||||
public function cancelEvent():Void
|
public function cancelEvent():Void
|
||||||
{
|
{
|
||||||
|
@ -265,6 +306,59 @@ class NoteScriptEvent extends ScriptEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is fired when you press a key with no note present.
|
||||||
|
*/
|
||||||
|
class GhostMissNoteScriptEvent extends ScriptEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The direction that was mistakenly pressed.
|
||||||
|
*/
|
||||||
|
public var dir(default, null):NoteDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether there was a note within judgement range when this ghost note was pressed.
|
||||||
|
*/
|
||||||
|
public var hasPossibleNotes(default, null):Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much health should be lost when this ghost note is pressed.
|
||||||
|
* Remember that max health is 2.00.
|
||||||
|
*/
|
||||||
|
public var healthChange(default, default):Float;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much score should be lost when this ghost note is pressed.
|
||||||
|
*/
|
||||||
|
public var scoreChange(default, default):Int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to play the record scratch sound.
|
||||||
|
*/
|
||||||
|
public var playSound(default, default):Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to play the miss animation on the player.
|
||||||
|
*/
|
||||||
|
public var playAnim(default, default):Bool;
|
||||||
|
|
||||||
|
public function new(dir:NoteDir, hasPossibleNotes:Bool, healthChange:Float, scoreChange:Int):Void
|
||||||
|
{
|
||||||
|
super(ScriptEvent.NOTE_GHOST_MISS, true);
|
||||||
|
this.dir = dir;
|
||||||
|
this.hasPossibleNotes = hasPossibleNotes;
|
||||||
|
this.healthChange = healthChange;
|
||||||
|
this.scoreChange = scoreChange;
|
||||||
|
this.playSound = true;
|
||||||
|
this.playAnim = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function toString():String
|
||||||
|
{
|
||||||
|
return 'GhostMissNoteScriptEvent(dir=' + dir + ', hasPossibleNotes=' + hasPossibleNotes + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event that is fired during the update loop.
|
* An event that is fired during the update loop.
|
||||||
*/
|
*/
|
||||||
|
@ -306,7 +400,7 @@ class SongTimeScriptEvent extends ScriptEvent
|
||||||
|
|
||||||
public function new(type:ScriptEventType, beat:Int, step:Int):Void
|
public function new(type:ScriptEventType, beat:Int, step:Int):Void
|
||||||
{
|
{
|
||||||
super(type, false);
|
super(type, true);
|
||||||
this.beat = beat;
|
this.beat = beat;
|
||||||
this.step = step;
|
this.step = step;
|
||||||
}
|
}
|
||||||
|
@ -403,13 +497,58 @@ class SongLoadScriptEvent extends ScriptEvent
|
||||||
*/
|
*/
|
||||||
class StateChangeScriptEvent extends ScriptEvent
|
class StateChangeScriptEvent extends ScriptEvent
|
||||||
{
|
{
|
||||||
public function new(type:ScriptEventType):Void
|
/**
|
||||||
|
* The state the game is moving into.
|
||||||
|
*/
|
||||||
|
public var targetState(default, null):FlxState;
|
||||||
|
|
||||||
|
public function new(type:ScriptEventType, targetState:FlxState, cancelable:Bool = false):Void
|
||||||
{
|
{
|
||||||
super(type, false);
|
super(type, cancelable);
|
||||||
|
this.targetState = targetState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function toString():String
|
public override function toString():String
|
||||||
{
|
{
|
||||||
return 'StateChangeScriptEvent(type=' + type + ')';
|
return 'StateChangeScriptEvent(type=' + type + ', targetState=' + targetState + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is fired when moving out of or into an FlxSubState.
|
||||||
|
*/
|
||||||
|
class SubStateScriptEvent extends ScriptEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The state the game is moving into.
|
||||||
|
*/
|
||||||
|
public var targetState(default, null):FlxSubState;
|
||||||
|
|
||||||
|
public function new(type:ScriptEventType, targetState:FlxSubState, cancelable:Bool = false):Void
|
||||||
|
{
|
||||||
|
super(type, cancelable);
|
||||||
|
this.targetState = targetState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function toString():String
|
||||||
|
{
|
||||||
|
return 'SubStateScriptEvent(type=' + type + ', targetState=' + targetState + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event which is called when the player attempts to pause the game.
|
||||||
|
*/
|
||||||
|
class PauseScriptEvent extends ScriptEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Whether to use the Gitaroo Man pause.
|
||||||
|
*/
|
||||||
|
public var gitaroo(default, default):Bool;
|
||||||
|
|
||||||
|
public function new(gitaroo:Bool):Void
|
||||||
|
{
|
||||||
|
super(ScriptEvent.PAUSE, true);
|
||||||
|
this.gitaroo = gitaroo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package funkin.modding.events;
|
package funkin.modding.events;
|
||||||
|
|
||||||
import funkin.modding.IScriptedClass;
|
|
||||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||||
|
import funkin.modding.IScriptedClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions to assist with handling scripted classes.
|
* Utility functions to assist with handling scripted classes.
|
||||||
|
@ -35,18 +35,6 @@ class ScriptEventDispatcher
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Std.isOfType(target, IStateChangingScriptedClass))
|
|
||||||
{
|
|
||||||
var t = cast(target, IStateChangingScriptedClass);
|
|
||||||
var t = cast(target, IPlayStateScriptedClass);
|
|
||||||
switch (event.type)
|
|
||||||
{
|
|
||||||
case ScriptEvent.NOTE_HIT:
|
|
||||||
t.onNoteHit(cast event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Std.isOfType(target, IPlayStateScriptedClass))
|
if (Std.isOfType(target, IPlayStateScriptedClass))
|
||||||
{
|
{
|
||||||
var t = cast(target, IPlayStateScriptedClass);
|
var t = cast(target, IPlayStateScriptedClass);
|
||||||
|
@ -58,6 +46,9 @@ class ScriptEventDispatcher
|
||||||
case ScriptEvent.NOTE_MISS:
|
case ScriptEvent.NOTE_MISS:
|
||||||
t.onNoteMiss(cast event);
|
t.onNoteMiss(cast event);
|
||||||
return;
|
return;
|
||||||
|
case ScriptEvent.NOTE_GHOST_MISS:
|
||||||
|
t.onNoteGhostMiss(cast event);
|
||||||
|
return;
|
||||||
case ScriptEvent.SONG_BEAT_HIT:
|
case ScriptEvent.SONG_BEAT_HIT:
|
||||||
t.onBeatHit(cast event);
|
t.onBeatHit(cast event);
|
||||||
return;
|
return;
|
||||||
|
@ -70,11 +61,14 @@ class ScriptEventDispatcher
|
||||||
case ScriptEvent.SONG_END:
|
case ScriptEvent.SONG_END:
|
||||||
t.onSongEnd(event);
|
t.onSongEnd(event);
|
||||||
return;
|
return;
|
||||||
case ScriptEvent.SONG_RESET:
|
case ScriptEvent.SONG_RETRY:
|
||||||
t.onSongReset(event);
|
t.onSongRetry(event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.GAME_OVER:
|
||||||
|
t.onGameOver(event);
|
||||||
return;
|
return;
|
||||||
case ScriptEvent.PAUSE:
|
case ScriptEvent.PAUSE:
|
||||||
t.onPause(event);
|
t.onPause(cast event);
|
||||||
return;
|
return;
|
||||||
case ScriptEvent.RESUME:
|
case ScriptEvent.RESUME:
|
||||||
t.onResume(event);
|
t.onResume(event);
|
||||||
|
@ -94,7 +88,38 @@ class ScriptEventDispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw "No helper for event type: " + event.type;
|
if (Std.isOfType(target, IStateChangingScriptedClass))
|
||||||
|
{
|
||||||
|
var t = cast(target, IStateChangingScriptedClass);
|
||||||
|
switch (event.type)
|
||||||
|
{
|
||||||
|
case ScriptEvent.STATE_CHANGE_BEGIN:
|
||||||
|
t.onStateChangeBegin(cast event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.STATE_CHANGE_END:
|
||||||
|
t.onStateChangeEnd(cast event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.SUBSTATE_OPEN_BEGIN:
|
||||||
|
t.onSubstateOpenBegin(cast event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.SUBSTATE_OPEN_END:
|
||||||
|
t.onSubstateOpenEnd(cast event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.SUBSTATE_CLOSE_BEGIN:
|
||||||
|
t.onSubstateCloseBegin(cast event);
|
||||||
|
return;
|
||||||
|
case ScriptEvent.SUBSTATE_CLOSE_END:
|
||||||
|
t.onSubstateCloseEnd(cast event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Prevent "NO HELPER error."
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw "No function called for event type: " + event.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function callEventOnAllTargets(targets:Iterator<IScriptedClass>, event:ScriptEvent):Void
|
public static function callEventOnAllTargets(targets:Iterator<IScriptedClass>, event:ScriptEvent):Void
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package funkin.modding.module;
|
package funkin.modding.module;
|
||||||
|
|
||||||
import funkin.modding.events.ScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.KeyboardInputScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.NoteScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
|
|
||||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||||
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
|
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
|
||||||
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
@ -18,7 +13,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||||
/**
|
/**
|
||||||
* Whether the module is currently active.
|
* Whether the module is currently active.
|
||||||
*/
|
*/
|
||||||
public var active(default, set):Bool = false;
|
public var active(default, set):Bool = true;
|
||||||
|
|
||||||
function set_active(value:Bool):Bool
|
function set_active(value:Bool):Bool
|
||||||
{
|
{
|
||||||
|
@ -48,14 +43,11 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||||
* Called when the module is initialized.
|
* Called when the module is initialized.
|
||||||
* It may not be safe to reference other modules here since they may not be loaded yet.
|
* It may not be safe to reference other modules here since they may not be loaded yet.
|
||||||
*
|
*
|
||||||
* @param startActive Whether to start with the module active.
|
* NOTE: To make the module start inactive, call `this.active = false` in the constructor.
|
||||||
* If false, the module will be inactive and must be enabled by another script,
|
|
||||||
* such as a stage or another module.
|
|
||||||
*/
|
*/
|
||||||
public function new(moduleId:String, active:Bool = true, priority:Int = 1000):Void
|
public function new(moduleId:String, priority:Int = 1000):Void
|
||||||
{
|
{
|
||||||
this.moduleId = moduleId;
|
this.moduleId = moduleId;
|
||||||
this.active = active;
|
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +74,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||||
|
|
||||||
public function onUpdate(event:UpdateScriptEvent) {}
|
public function onUpdate(event:UpdateScriptEvent) {}
|
||||||
|
|
||||||
public function onPause(event:ScriptEvent) {}
|
public function onPause(event:PauseScriptEvent) {}
|
||||||
|
|
||||||
public function onResume(event:ScriptEvent) {}
|
public function onResume(event:ScriptEvent) {}
|
||||||
|
|
||||||
|
@ -90,16 +82,14 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||||
|
|
||||||
public function onSongEnd(event:ScriptEvent) {}
|
public function onSongEnd(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onSongReset(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onGameOver(event:ScriptEvent) {}
|
public function onGameOver(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onGameRetry(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onNoteHit(event:NoteScriptEvent) {}
|
public function onNoteHit(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
public function onNoteMiss(event:NoteScriptEvent) {}
|
public function onNoteMiss(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
|
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
|
||||||
|
|
||||||
public function onStepHit(event:SongTimeScriptEvent) {}
|
public function onStepHit(event:SongTimeScriptEvent) {}
|
||||||
|
|
||||||
public function onBeatHit(event:SongTimeScriptEvent) {}
|
public function onBeatHit(event:SongTimeScriptEvent) {}
|
||||||
|
@ -110,9 +100,19 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
|
||||||
|
|
||||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
public function onSongLoaded(event:SongLoadScriptEvent) {}
|
||||||
|
|
||||||
public function onStateChangeBegin(event:StateChangeScriptEvent) {}
|
public function onStateChangeBegin(event:StateChangeScriptEvent) {}
|
||||||
|
|
||||||
public function onStateChangeEnd(event:StateChangeScriptEvent) {}
|
public function onStateChangeEnd(event:StateChangeScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSubstateOpenBegin(event:SubStateScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSubstateOpenEnd(event:SubStateScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSubstateCloseBegin(event:SubStateScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSubstateCloseEnd(event:SubStateScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSongRetry(event:ScriptEvent) {}
|
||||||
}
|
}
|
||||||
|
|
63
source/funkin/play/AnimationData.hx
Normal file
63
source/funkin/play/AnimationData.hx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package funkin.play;
|
||||||
|
|
||||||
|
typedef AnimationData =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name for the animation.
|
||||||
|
* This should match the animation name queried by the game;
|
||||||
|
* for example, characters need animations with names `idle`, `singDOWN`, `singUPmiss`, etc.
|
||||||
|
*/
|
||||||
|
var name:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix for the frames of the animation as defined by the XML file.
|
||||||
|
* This will may or may not differ from the `name` of the animation,
|
||||||
|
* depending on how your animator organized their FLA or whatever.
|
||||||
|
*/
|
||||||
|
var prefix:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally specify an asset path to use for this specific animation.
|
||||||
|
* ONLY for use by MultiSparrow characters.
|
||||||
|
* @default The assetPath of the parent sprite
|
||||||
|
*/
|
||||||
|
var assetPath:Null<String>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset the character's position by this amount when playing this animation.
|
||||||
|
* @default [0, 0]
|
||||||
|
*/
|
||||||
|
var offsets:Null<Array<Float>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the animation should loop when it finishes.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
var looped:Null<Bool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the animation's sprites should be flipped horizontally.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
var flipX:Null<Bool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the animation's sprites should be flipped vertically.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
var flipY:Null<Bool>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The frame rate of the animation.
|
||||||
|
* @default 24
|
||||||
|
*/
|
||||||
|
var frameRate:Null<Int>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you want this animation to use only certain frames of an animation with a given prefix,
|
||||||
|
* select them here.
|
||||||
|
* @example [0, 1, 2, 3] (use only the first four frames)
|
||||||
|
* @default [] (all frames)
|
||||||
|
*/
|
||||||
|
var frameIndices:Null<Array<Int>>;
|
||||||
|
}
|
|
@ -1,14 +1,13 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import funkin.Note.NoteData;
|
|
||||||
import funkin.audiovis.PolygonSpectogram;
|
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.addons.effects.FlxTrail;
|
import flixel.addons.effects.FlxTrail;
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxTimer;
|
import funkin.Note.NoteData;
|
||||||
|
import funkin.audiovis.PolygonSpectogram;
|
||||||
|
|
||||||
class PicoFight extends MusicBeatState
|
class PicoFight extends MusicBeatState
|
||||||
{
|
{
|
||||||
|
@ -37,7 +36,7 @@ class PicoFight extends MusicBeatState
|
||||||
FlxG.sound.playMusic(Paths.inst("blazin"));
|
FlxG.sound.playMusic(Paths.inst("blazin"));
|
||||||
|
|
||||||
SongLoad.loadFromJson('blazin', "blazin");
|
SongLoad.loadFromJson('blazin', "blazin");
|
||||||
Conductor.changeBPM(SongLoad.songData.bpm);
|
Conductor.bpm = SongLoad.songData.bpm;
|
||||||
|
|
||||||
for (dumbassSection in SongLoad.songData.noteMap['hard'])
|
for (dumbassSection in SongLoad.songData.noteMap['hard'])
|
||||||
{
|
{
|
||||||
|
@ -184,13 +183,15 @@ class PicoFight extends MusicBeatState
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function stepHit()
|
override function stepHit():Bool
|
||||||
{
|
{
|
||||||
super.stepHit();
|
return super.stepHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
override function beatHit()
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
|
if (!super.beatHit())
|
||||||
|
return false;
|
||||||
funnyWave.thickness = 10;
|
funnyWave.thickness = 10;
|
||||||
funnyWave.waveAmplitude = 300;
|
funnyWave.waveAmplitude = 300;
|
||||||
funnyWave.realtimeVisLenght = 0.1;
|
funnyWave.realtimeVisLenght = 0.1;
|
||||||
|
@ -198,7 +199,6 @@ class PicoFight extends MusicBeatState
|
||||||
picoHealth += 1;
|
picoHealth += 1;
|
||||||
|
|
||||||
makeNotes();
|
makeNotes();
|
||||||
// trace(picoHealth);
|
return true;
|
||||||
super.beatHit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import funkin.play.Strumline.StrumlineArrow;
|
|
||||||
import flixel.addons.effects.FlxTrail;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
import flixel.FlxSubState;
|
import flixel.FlxSubState;
|
||||||
|
import flixel.addons.effects.FlxTrail;
|
||||||
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
|
@ -19,19 +18,17 @@ import flixel.ui.FlxBar;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxSort;
|
import flixel.util.FlxSort;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
import funkin.charting.ChartingState;
|
|
||||||
import funkin.modding.events.ScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
|
||||||
import funkin.modding.IHook;
|
|
||||||
import funkin.modding.module.ModuleHandler;
|
|
||||||
import funkin.Note;
|
import funkin.Note;
|
||||||
import funkin.play.stage.Stage;
|
|
||||||
import funkin.play.stage.StageData;
|
|
||||||
import funkin.play.Strumline.StrumlineStyle;
|
|
||||||
import funkin.Section.SwagSection;
|
import funkin.Section.SwagSection;
|
||||||
import funkin.SongLoad.SwagSong;
|
import funkin.SongLoad.SwagSong;
|
||||||
|
import funkin.charting.ChartingState;
|
||||||
|
import funkin.modding.IHook;
|
||||||
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
|
import funkin.play.Strumline.StrumlineArrow;
|
||||||
|
import funkin.play.Strumline.StrumlineStyle;
|
||||||
|
import funkin.play.stage.Stage;
|
||||||
|
import funkin.play.stage.StageData;
|
||||||
import funkin.ui.PopUpStuff;
|
import funkin.ui.PopUpStuff;
|
||||||
import funkin.ui.PreferencesMenu;
|
import funkin.ui.PreferencesMenu;
|
||||||
import funkin.util.Constants;
|
import funkin.util.Constants;
|
||||||
|
@ -287,7 +284,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
currentSong = SongLoad.loadFromJson('tutorial');
|
currentSong = SongLoad.loadFromJson('tutorial');
|
||||||
|
|
||||||
Conductor.mapBPMChanges(currentSong);
|
Conductor.mapBPMChanges(currentSong);
|
||||||
Conductor.changeBPM(currentSong.bpm);
|
Conductor.bpm = currentSong.bpm;
|
||||||
|
|
||||||
switch (currentSong.song.toLowerCase())
|
switch (currentSong.song.toLowerCase())
|
||||||
{
|
{
|
||||||
|
@ -592,7 +589,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
*
|
*
|
||||||
* Call this by pressing F5 on a debug build.
|
* Call this by pressing F5 on a debug build.
|
||||||
*/
|
*/
|
||||||
function debug_refreshStages()
|
override function debug_refreshModules()
|
||||||
{
|
{
|
||||||
// Remove the current stage. If the stage gets deleted while it's still in use,
|
// Remove the current stage. If the stage gets deleted while it's still in use,
|
||||||
// it'll probably crash the game or something.
|
// it'll probably crash the game or something.
|
||||||
|
@ -604,18 +601,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
currentStage = null;
|
currentStage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleHandler.clearModuleCache();
|
super.debug_refreshModules();
|
||||||
|
|
||||||
// Forcibly reload scripts so that scripted stages can be edited.
|
|
||||||
polymod.hscript.PolymodScriptClass.clearScriptClasses();
|
|
||||||
polymod.hscript.PolymodScriptClass.registerAllScriptClasses();
|
|
||||||
|
|
||||||
// Reload the stages in cache. This might cause a lag spike but who cares this is a debug utility.
|
|
||||||
StageDataParser.loadStageCache();
|
|
||||||
ModuleHandler.loadModuleCache();
|
|
||||||
|
|
||||||
// Reload the level. This should use new data from the assets folder.
|
|
||||||
LoadingState.loadAndSwitchState(new PlayState());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -783,7 +769,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
{
|
{
|
||||||
// FlxG.log.add(ChartParser.parse());
|
// FlxG.log.add(ChartParser.parse());
|
||||||
|
|
||||||
Conductor.changeBPM(currentSong.bpm);
|
Conductor.bpm = currentSong.bpm;
|
||||||
|
|
||||||
currentSong.song = currentSong.song;
|
currentSong.song = currentSong.song;
|
||||||
|
|
||||||
|
@ -849,7 +835,9 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
oldNote = null;
|
oldNote = null;
|
||||||
|
|
||||||
var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote);
|
var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote);
|
||||||
swagNote.data = songNotes;
|
// swagNote.data = songNotes;
|
||||||
|
swagNote.data.sustainLength = songNotes.sustainLength;
|
||||||
|
swagNote.data.altNote = songNotes.altNote;
|
||||||
swagNote.scrollFactor.set(0, 0);
|
swagNote.scrollFactor.set(0, 0);
|
||||||
|
|
||||||
var susLength:Float = swagNote.data.sustainLength;
|
var susLength:Float = swagNote.data.sustainLength;
|
||||||
|
@ -938,6 +926,8 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
|
|
||||||
if (needsReset)
|
if (needsReset)
|
||||||
{
|
{
|
||||||
|
dispatchEvent(new ScriptEvent(ScriptEvent.SONG_RETRY));
|
||||||
|
|
||||||
resetCamera();
|
resetCamera();
|
||||||
|
|
||||||
persistentUpdate = true;
|
persistentUpdate = true;
|
||||||
|
@ -948,11 +938,10 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
vocals.pause();
|
vocals.pause();
|
||||||
|
|
||||||
var event:ScriptEvent = new ScriptEvent(ScriptEvent.SONG_RESET, false);
|
|
||||||
|
|
||||||
FlxG.sound.music.time = 0;
|
FlxG.sound.music.time = 0;
|
||||||
regenNoteData(); // loads the note data from start
|
regenNoteData(); // loads the note data from start
|
||||||
health = 1;
|
health = 1;
|
||||||
|
songScore = 0;
|
||||||
Countdown.performCountdown(currentStageId.startsWith('school'));
|
Countdown.performCountdown(currentStageId.startsWith('school'));
|
||||||
|
|
||||||
needsReset = false;
|
needsReset = false;
|
||||||
|
@ -1003,6 +992,12 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
#end
|
#end
|
||||||
|
|
||||||
if ((controls.PAUSE || androidPause) && isInCountdown && mayPauseGame)
|
if ((controls.PAUSE || androidPause) && isInCountdown && mayPauseGame)
|
||||||
|
{
|
||||||
|
var event = new PauseScriptEvent(FlxG.random.bool(1 / 1000));
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (!event.eventCanceled)
|
||||||
{
|
{
|
||||||
persistentUpdate = false;
|
persistentUpdate = false;
|
||||||
persistentDraw = true;
|
persistentDraw = true;
|
||||||
|
@ -1010,7 +1005,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
// There is a 1/1000 change to use a special pause menu.
|
// There is a 1/1000 change to use a special pause menu.
|
||||||
// This prevents the player from resuming, but that's the point.
|
// This prevents the player from resuming, but that's the point.
|
||||||
// It's a reference to Gitaroo Man, which doesn't let you pause the game.
|
// It's a reference to Gitaroo Man, which doesn't let you pause the game.
|
||||||
if (FlxG.random.bool(1 / 1000))
|
if (event.gitaroo)
|
||||||
{
|
{
|
||||||
FlxG.switchState(new GitarooPause());
|
FlxG.switchState(new GitarooPause());
|
||||||
}
|
}
|
||||||
|
@ -1027,6 +1022,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
DiscordClient.changePresence(detailsPausedText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC);
|
DiscordClient.changePresence(detailsPausedText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.SEVEN)
|
if (FlxG.keys.justPressed.SEVEN)
|
||||||
{
|
{
|
||||||
|
@ -1040,9 +1036,6 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
if (FlxG.keys.justPressed.EIGHT)
|
if (FlxG.keys.justPressed.EIGHT)
|
||||||
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
|
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.F5)
|
|
||||||
debug_refreshStages();
|
|
||||||
|
|
||||||
if (FlxG.keys.justPressed.NINE)
|
if (FlxG.keys.justPressed.NINE)
|
||||||
iconP1.swapOldIcon();
|
iconP1.swapOldIcon();
|
||||||
|
|
||||||
|
@ -1656,7 +1649,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlayState.instance.currentStage == null)
|
if (PlayState.instance == null || PlayState.instance.currentStage == null)
|
||||||
return;
|
return;
|
||||||
if (PlayState.instance.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))
|
||||||
{
|
{
|
||||||
|
@ -1724,9 +1717,12 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function stepHit()
|
override function stepHit():Bool
|
||||||
{
|
{
|
||||||
super.stepHit();
|
// super.stepHit() returns false if a module cancelled the event.
|
||||||
|
if (!super.stepHit())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 20
|
if (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 20
|
||||||
|| (currentSong.needsVoices && Math.abs(vocals.time - (Conductor.songPosition - Conductor.offset)) > 20))
|
|| (currentSong.needsVoices && Math.abs(vocals.time - (Conductor.songPosition - Conductor.offset)) > 20))
|
||||||
{
|
{
|
||||||
|
@ -1734,11 +1730,15 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep));
|
dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, curBeat, curStep));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override function beatHit()
|
override function beatHit():Bool
|
||||||
{
|
{
|
||||||
super.beatHit();
|
// super.beatHit() returns false if a module cancelled the event.
|
||||||
|
if (!super.beatHit())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (generatedMusic)
|
if (generatedMusic)
|
||||||
{
|
{
|
||||||
|
@ -1749,7 +1749,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
{
|
{
|
||||||
if (SongLoad.getSong()[Math.floor(curStep / 16)].changeBPM)
|
if (SongLoad.getSong()[Math.floor(curStep / 16)].changeBPM)
|
||||||
{
|
{
|
||||||
Conductor.changeBPM(SongLoad.getSong()[Math.floor(curStep / 16)].bpm);
|
Conductor.bpm = SongLoad.getSong()[Math.floor(curStep / 16)].bpm;
|
||||||
FlxG.log.add('CHANGED BPM!');
|
FlxG.log.add('CHANGED BPM!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1780,8 +1780,7 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
// Make the characters dance on the beat
|
// Make the characters dance on the beat
|
||||||
danceOnBeat();
|
danceOnBeat();
|
||||||
|
|
||||||
// Call any relevant event handlers.
|
return true;
|
||||||
dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, curBeat, curStep));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1897,13 +1896,6 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
Countdown.pauseCountdown();
|
Countdown.pauseCountdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
var event:ScriptEvent = new ScriptEvent(ScriptEvent.PAUSE, true);
|
|
||||||
|
|
||||||
dispatchEvent(event);
|
|
||||||
|
|
||||||
if (event.eventCanceled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
super.openSubState(subState);
|
super.openSubState(subState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1915,6 +1907,13 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
{
|
{
|
||||||
if (isGamePaused)
|
if (isGamePaused)
|
||||||
{
|
{
|
||||||
|
var event:ScriptEvent = new ScriptEvent(ScriptEvent.RESUME, true);
|
||||||
|
|
||||||
|
dispatchEvent(event);
|
||||||
|
|
||||||
|
if (event.eventCanceled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (FlxG.sound.music != null && !startingSong)
|
if (FlxG.sound.music != null && !startingSong)
|
||||||
resyncVocals();
|
resyncVocals();
|
||||||
|
|
||||||
|
@ -1930,13 +1929,6 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
var event:ScriptEvent = new ScriptEvent(ScriptEvent.RESUME, true);
|
|
||||||
|
|
||||||
dispatchEvent(event);
|
|
||||||
|
|
||||||
if (event.eventCanceled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
super.closeSubState();
|
super.closeSubState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1957,12 +1949,16 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
|
|
||||||
override function dispatchEvent(event:ScriptEvent):Void
|
override function dispatchEvent(event:ScriptEvent):Void
|
||||||
{
|
{
|
||||||
|
// ORDER: Module, Stage, Character, Song, Note
|
||||||
|
// Modules should get the first chance to cancel the event.
|
||||||
|
|
||||||
|
// super.dispatchEvent(event) dispatches event to module scripts.
|
||||||
|
super.dispatchEvent(event);
|
||||||
|
|
||||||
|
// Dispatch event to stage script.
|
||||||
ScriptEventDispatcher.callEvent(currentStage, event);
|
ScriptEventDispatcher.callEvent(currentStage, event);
|
||||||
|
|
||||||
// TODO: Dispatch event to song script
|
// TODO: Dispatch event to song script
|
||||||
// TODO: Dispatch events to character scripts
|
|
||||||
|
|
||||||
super.dispatchEvent(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2014,16 +2010,6 @@ class PlayState extends MusicBeatState implements IHook
|
||||||
instance = null;
|
instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Refreshes the state, by redoing the render order of all elements.
|
|
||||||
* It does this based on the `zIndex` of each element.
|
|
||||||
*/
|
|
||||||
public function refresh()
|
|
||||||
{
|
|
||||||
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
|
|
||||||
trace('Stage sorted by z-index');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is called whenever Flixel switches switching to a new FlxState.
|
* This function is called whenever Flixel switches switching to a new FlxState.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
package funkin.play.stage;
|
package funkin.play.stage;
|
||||||
|
|
||||||
import funkin.modding.events.ScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.NoteScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.SongTimeScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
|
|
||||||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||||
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Bopper is a stage prop which plays a dance animation.
|
* A Bopper is a stage prop which plays a dance animation.
|
||||||
|
@ -16,6 +12,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The bopper plays the dance animation once every `danceEvery` beats.
|
* The bopper plays the dance animation once every `danceEvery` beats.
|
||||||
|
* Set to 0 to disable idle animation.
|
||||||
*/
|
*/
|
||||||
public var danceEvery:Int = 1;
|
public var danceEvery:Int = 1;
|
||||||
|
|
||||||
|
@ -29,16 +26,14 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
public var shouldAlternate:Null<Bool> = null;
|
public var shouldAlternate:Null<Bool> = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this value to define an additional horizontal offset to this sprite's position.
|
* Offset the character's sprite by this much when playing each animation.
|
||||||
*/
|
*/
|
||||||
public var xOffset:Float = 0;
|
public var animationOffsets:Map<String, Array<Float>> = new Map<String, Array<Float>>();
|
||||||
|
|
||||||
override function set_x(value:Float):Float
|
|
||||||
{
|
|
||||||
this.x = value + this.xOffset;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a suffix to the `idle` animation (or `danceLeft` and `danceRight` animations)
|
||||||
|
* that this bopper will play.
|
||||||
|
*/
|
||||||
public var idleSuffix(default, set):String = "";
|
public var idleSuffix(default, set):String = "";
|
||||||
|
|
||||||
function set_idleSuffix(value:String):String
|
function set_idleSuffix(value:String):String
|
||||||
|
@ -49,14 +44,26 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this value to define an additional vertical offset to this sprite's position.
|
* The offset of the character relative to the position specified by the stage.
|
||||||
*/
|
*/
|
||||||
public var yOffset:Float = 0;
|
public var globalOffsets(default, null):Array<Float> = [0, 0];
|
||||||
|
|
||||||
override function set_y(value:Float):Float
|
private var animOffsets(default, set):Array<Float> = [0, 0];
|
||||||
|
|
||||||
|
function set_animOffsets(value:Array<Float>)
|
||||||
{
|
{
|
||||||
this.y = value + this.yOffset;
|
if (animOffsets == null)
|
||||||
|
animOffsets = [0, 0];
|
||||||
|
if (animOffsets == value)
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
|
var xDiff = animOffsets[0] - value[0];
|
||||||
|
var yDiff = animOffsets[1] - value[1];
|
||||||
|
|
||||||
|
this.x += xDiff;
|
||||||
|
this.y += yDiff;
|
||||||
|
|
||||||
|
return animOffsets = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +80,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
|
|
||||||
function update_shouldAlternate():Void
|
function update_shouldAlternate():Void
|
||||||
{
|
{
|
||||||
if (this.animation.getByName('danceLeft') != null)
|
if (hasAnimation('danceLeft'))
|
||||||
{
|
{
|
||||||
this.shouldAlternate = true;
|
this.shouldAlternate = true;
|
||||||
}
|
}
|
||||||
|
@ -84,16 +91,16 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
*/
|
*/
|
||||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||||
{
|
{
|
||||||
if (event.beat % danceEvery == 0)
|
if (danceEvery > 0 && event.beat % danceEvery == 0)
|
||||||
{
|
{
|
||||||
dance();
|
dance(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called every `danceEvery` beats of the song.
|
* Called every `danceEvery` beats of the song.
|
||||||
*/
|
*/
|
||||||
function dance():Void
|
public function dance(force:Bool = false):Void
|
||||||
{
|
{
|
||||||
if (this.animation == null)
|
if (this.animation == null)
|
||||||
{
|
{
|
||||||
|
@ -109,20 +116,113 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
{
|
{
|
||||||
if (hasDanced)
|
if (hasDanced)
|
||||||
{
|
{
|
||||||
this.animation.play('danceRight$idleSuffix');
|
playAnimation('danceRight$idleSuffix', true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.animation.play('danceLeft$idleSuffix');
|
playAnimation('danceLeft$idleSuffix', true);
|
||||||
}
|
}
|
||||||
hasDanced = !hasDanced;
|
hasDanced = !hasDanced;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.animation.play('idle$idleSuffix');
|
playAnimation('idle$idleSuffix', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasAnimation(id:String):Bool
|
||||||
|
{
|
||||||
|
if (this.animation == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this.animation.getByName(id) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that a given animation exists before playing it.
|
||||||
|
* Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play.
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
function correctAnimationName(name:String)
|
||||||
|
{
|
||||||
|
// If the animation exists, we're good.
|
||||||
|
if (hasAnimation(name))
|
||||||
|
return name;
|
||||||
|
|
||||||
|
trace('[BOPPER] Animation "$name" does not exist!');
|
||||||
|
|
||||||
|
// Attempt to strip a `-alt` suffix, if it exists.
|
||||||
|
if (name.lastIndexOf('-') != -1)
|
||||||
|
{
|
||||||
|
var correctName = name.substring(0, name.lastIndexOf('-'));
|
||||||
|
trace('[BOPPER] Attempting to fallback to "$correctName"');
|
||||||
|
return correctAnimationName(correctName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (name != 'idle')
|
||||||
|
{
|
||||||
|
trace('[BOPPER] Attempting to fallback to "idle"');
|
||||||
|
return correctAnimationName('idle');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('[BOPPER] Failing animation playback.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The name of the animation to play.
|
||||||
|
* @param restart Whether to restart the animation if it is already playing.
|
||||||
|
*/
|
||||||
|
public function playAnimation(name:String, restart:Bool = false):Void
|
||||||
|
{
|
||||||
|
var correctName = correctAnimationName(name);
|
||||||
|
if (correctName == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.animation.play(correctName, restart, false, 0);
|
||||||
|
|
||||||
|
applyAnimationOffsets(correctName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAnimationOffsets(name:String)
|
||||||
|
{
|
||||||
|
var offsets = animationOffsets.get(name);
|
||||||
|
if (offsets != null)
|
||||||
|
{
|
||||||
|
this.animOffsets = [offsets[0] + globalOffsets[0], offsets[1] + globalOffsets[1]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.animOffsets = globalOffsets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAnimationFinished():Bool
|
||||||
|
{
|
||||||
|
return this.animation.finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void
|
||||||
|
{
|
||||||
|
animationOffsets.set(name, [xOffset, yOffset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the animation that is currently playing.
|
||||||
|
* If no animation is playing (usually this means the character is BROKEN!),
|
||||||
|
* returns an empty string to prevent NPEs.
|
||||||
|
*/
|
||||||
|
public function getCurrentAnimation():String
|
||||||
|
{
|
||||||
|
if (this.animation == null || this.animation.curAnim == null)
|
||||||
|
return "";
|
||||||
|
return this.animation.curAnim.name;
|
||||||
|
}
|
||||||
|
|
||||||
public function onScriptEvent(event:ScriptEvent) {}
|
public function onScriptEvent(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onCreate(event:ScriptEvent) {}
|
public function onCreate(event:ScriptEvent) {}
|
||||||
|
@ -131,7 +231,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
|
|
||||||
public function onUpdate(event:UpdateScriptEvent) {}
|
public function onUpdate(event:UpdateScriptEvent) {}
|
||||||
|
|
||||||
public function onPause(event:ScriptEvent) {}
|
public function onPause(event:PauseScriptEvent) {}
|
||||||
|
|
||||||
public function onResume(event:ScriptEvent) {}
|
public function onResume(event:ScriptEvent) {}
|
||||||
|
|
||||||
|
@ -139,16 +239,14 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
|
|
||||||
public function onSongEnd(event:ScriptEvent) {}
|
public function onSongEnd(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onSongReset(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onGameOver(event:ScriptEvent) {}
|
public function onGameOver(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onGameRetry(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onNoteHit(event:NoteScriptEvent) {}
|
public function onNoteHit(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
public function onNoteMiss(event:NoteScriptEvent) {}
|
public function onNoteMiss(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
|
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
|
||||||
|
|
||||||
public function onStepHit(event:SongTimeScriptEvent) {}
|
public function onStepHit(event:SongTimeScriptEvent) {}
|
||||||
|
|
||||||
public function onCountdownStart(event:CountdownScriptEvent) {}
|
public function onCountdownStart(event:CountdownScriptEvent) {}
|
||||||
|
@ -158,4 +256,6 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
|
||||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSongRetry(event:ScriptEvent) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
package funkin.play.stage;
|
package funkin.play.stage;
|
||||||
|
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
|
||||||
import funkin.modding.events.ScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
|
|
||||||
import funkin.modding.events.ScriptEvent.KeyboardInputScriptEvent;
|
|
||||||
import funkin.modding.IScriptedClass;
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.group.FlxSpriteGroup;
|
import flixel.group.FlxSpriteGroup;
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import flixel.util.FlxSort;
|
import flixel.util.FlxSort;
|
||||||
import funkin.modding.IHook;
|
import funkin.modding.IHook;
|
||||||
|
import funkin.modding.IScriptedClass;
|
||||||
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.character.Character.CharacterType;
|
import funkin.play.character.Character.CharacterType;
|
||||||
import funkin.play.stage.StageData.StageDataParser;
|
import funkin.play.stage.StageData.StageDataParser;
|
||||||
import funkin.util.SortUtil;
|
import funkin.util.SortUtil;
|
||||||
|
import funkin.util.assets.FlxAnimationUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Stage is a group of objects rendered in the PlayState.
|
* A Stage is a group of objects rendered in the PlayState.
|
||||||
|
@ -143,19 +141,19 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
for (propAnim in dataProp.animations)
|
for (propAnim in dataProp.animations)
|
||||||
{
|
{
|
||||||
propSprite.animation.add(propAnim.name, propAnim.frameIndices);
|
propSprite.animation.add(propAnim.name, propAnim.frameIndices);
|
||||||
|
|
||||||
|
if (Std.isOfType(propSprite, Bopper))
|
||||||
|
{
|
||||||
|
cast(propSprite, Bopper).setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default: // "sparrow"
|
default: // "sparrow"
|
||||||
|
FlxAnimationUtil.addAtlasAnimations(propSprite, dataProp.animations);
|
||||||
|
if (Std.isOfType(propSprite, Bopper))
|
||||||
|
{
|
||||||
for (propAnim in dataProp.animations)
|
for (propAnim in dataProp.animations)
|
||||||
{
|
{
|
||||||
if (propAnim.frameIndices.length == 0)
|
cast(propSprite, Bopper).setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
|
||||||
{
|
|
||||||
propSprite.animation.addByPrefix(propAnim.name, propAnim.prefix, propAnim.frameRate, propAnim.loop, propAnim.flipX,
|
|
||||||
propAnim.flipY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
propSprite.animation.addByIndices(propAnim.name, propAnim.prefix, propAnim.frameIndices, "", propAnim.frameRate, propAnim.loop,
|
|
||||||
propAnim.flipX, propAnim.flipY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,7 +375,7 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
|
|
||||||
public function onScriptEvent(event:ScriptEvent) {}
|
public function onScriptEvent(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onPause(event:ScriptEvent) {}
|
public function onPause(event:PauseScriptEvent) {}
|
||||||
|
|
||||||
public function onResume(event:ScriptEvent) {}
|
public function onResume(event:ScriptEvent) {}
|
||||||
|
|
||||||
|
@ -385,29 +383,23 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
|
||||||
|
|
||||||
public function onSongEnd(event:ScriptEvent) {}
|
public function onSongEnd(event:ScriptEvent) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the stage and its props.
|
|
||||||
*/
|
|
||||||
public function onSongReset(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onGameOver(event:ScriptEvent) {}
|
public function onGameOver(event:ScriptEvent) {}
|
||||||
|
|
||||||
public function onGameRetry(event:ScriptEvent) {}
|
|
||||||
|
|
||||||
public function onCountdownStart(event:CountdownScriptEvent) {}
|
public function onCountdownStart(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onCountdownStep(event:CountdownScriptEvent) {}
|
public function onCountdownStep(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
public function onCountdownEnd(event:CountdownScriptEvent) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* A function that should get called every frame.
|
|
||||||
*/
|
|
||||||
public function onUpdate(event:UpdateScriptEvent) {}
|
public function onUpdate(event:UpdateScriptEvent) {}
|
||||||
|
|
||||||
public function onNoteHit(event:NoteScriptEvent) {}
|
public function onNoteHit(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
public function onNoteMiss(event:NoteScriptEvent) {}
|
public function onNoteMiss(event:NoteScriptEvent) {}
|
||||||
|
|
||||||
|
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {}
|
||||||
|
|
||||||
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
public function onSongLoaded(eent:SongLoadScriptEvent) {}
|
||||||
|
|
||||||
|
public function onSongRetry(event:ScriptEvent) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package funkin.play.stage;
|
package funkin.play.stage;
|
||||||
|
|
||||||
import openfl.Assets;
|
import flixel.util.typeLimit.OneOfTwo;
|
||||||
|
import funkin.util.VersionUtil;
|
||||||
import funkin.util.assets.DataAssets;
|
import funkin.util.assets.DataAssets;
|
||||||
import haxe.Json;
|
import haxe.Json;
|
||||||
import flixel.util.typeLimit.OneOfTwo;
|
import openfl.Assets;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
|
@ -17,7 +18,12 @@ class StageDataParser
|
||||||
* Handle breaking changes by incrementing this value
|
* Handle breaking changes by incrementing this value
|
||||||
* and adding migration to the `migrateStageData()` function.
|
* and adding migration to the `migrateStageData()` function.
|
||||||
*/
|
*/
|
||||||
public static final STAGE_DATA_VERSION:String = "1.0";
|
public static final STAGE_DATA_VERSION:String = "1.0.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version rule check for the stage data format.
|
||||||
|
*/
|
||||||
|
public static final STAGE_DATA_VERSION_RULE:String = "1.0.x";
|
||||||
|
|
||||||
static final stageCache:Map<String, Stage> = new Map<String, Stage>();
|
static final stageCache:Map<String, Stage> = new Map<String, Stage>();
|
||||||
|
|
||||||
|
@ -163,20 +169,21 @@ class StageDataParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final DEFAULT_NAME:String = "Untitled Stage";
|
|
||||||
static final DEFAULT_CAMERAZOOM:Float = 1.0;
|
|
||||||
static final DEFAULT_ZINDEX:Int = 0;
|
|
||||||
static final DEFAULT_DANCEEVERY:Int = 0;
|
|
||||||
static final DEFAULT_SCALE:Float = 1.0;
|
|
||||||
static final DEFAULT_ISPIXEL:Bool = false;
|
|
||||||
static final DEFAULT_POSITION:Array<Float> = [0, 0];
|
|
||||||
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
|
|
||||||
static final DEFAULT_FRAMEINDICES:Array<Int> = [];
|
|
||||||
static final DEFAULT_ANIMTYPE:String = "sparrow";
|
static final DEFAULT_ANIMTYPE:String = "sparrow";
|
||||||
|
static final DEFAULT_CAMERAZOOM:Float = 1.0;
|
||||||
|
static final DEFAULT_DANCEEVERY:Int = 0;
|
||||||
|
static final DEFAULT_ISPIXEL:Bool = false;
|
||||||
|
static final DEFAULT_NAME:String = "Untitled Stage";
|
||||||
|
static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
|
||||||
|
static final DEFAULT_POSITION:Array<Float> = [0, 0];
|
||||||
|
static final DEFAULT_SCALE:Float = 1.0;
|
||||||
|
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
|
||||||
|
static final DEFAULT_ZINDEX:Int = 0;
|
||||||
|
|
||||||
static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
|
static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
|
||||||
zIndex: DEFAULT_ZINDEX,
|
zIndex: DEFAULT_ZINDEX,
|
||||||
position: DEFAULT_POSITION,
|
position: DEFAULT_POSITION,
|
||||||
|
cameraOffsets: DEFAULT_OFFSETS,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,12 +201,18 @@ class StageDataParser
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.version != STAGE_DATA_VERSION)
|
if (input.version == null)
|
||||||
{
|
{
|
||||||
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version');
|
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!VersionUtil.validateVersion(input.version, STAGE_DATA_VERSION_RULE))
|
||||||
|
{
|
||||||
|
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": bad version (got ${input.version}, expected ${STAGE_DATA_VERSION_RULE})');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (input.name == null)
|
if (input.name == null)
|
||||||
{
|
{
|
||||||
trace('[STAGEDATA] WARN: Stage data for "$id" missing name');
|
trace('[STAGEDATA] WARN: Stage data for "$id" missing name');
|
||||||
|
@ -211,10 +224,9 @@ class StageDataParser
|
||||||
input.cameraZoom = DEFAULT_CAMERAZOOM;
|
input.cameraZoom = DEFAULT_CAMERAZOOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.props == null || input.props.length == 0)
|
if (input.props == null)
|
||||||
{
|
{
|
||||||
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props');
|
input.props = [];
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (inputProp in input.props)
|
for (inputProp in input.props)
|
||||||
|
@ -296,14 +308,14 @@ class StageDataParser
|
||||||
inputAnimation.frameRate = 24;
|
inputAnimation.frameRate = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputAnimation.frameIndices == null)
|
if (inputAnimation.offsets == null)
|
||||||
{
|
{
|
||||||
inputAnimation.frameIndices = DEFAULT_FRAMEINDICES;
|
inputAnimation.offsets = DEFAULT_OFFSETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputAnimation.loop == null)
|
if (inputAnimation.looped == null)
|
||||||
{
|
{
|
||||||
inputAnimation.loop = true;
|
inputAnimation.looped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputAnimation.flipX == null)
|
if (inputAnimation.flipX == null)
|
||||||
|
@ -347,6 +359,10 @@ class StageDataParser
|
||||||
{
|
{
|
||||||
inputCharacter.position = [0, 0];
|
inputCharacter.position = [0, 0];
|
||||||
}
|
}
|
||||||
|
if (inputCharacter.cameraOffsets == null || inputCharacter.cameraOffsets.length != 2)
|
||||||
|
{
|
||||||
|
inputCharacter.cameraOffsets = [0, 0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good!
|
// All good!
|
||||||
|
@ -356,8 +372,12 @@ class StageDataParser
|
||||||
|
|
||||||
typedef StageData =
|
typedef StageData =
|
||||||
{
|
{
|
||||||
// Uses semantic versioning.
|
/**
|
||||||
|
* The sematic version number of the stage data JSON format.
|
||||||
|
* Supports fancy comparisons like NPM does it's neat.
|
||||||
|
*/
|
||||||
var version:String;
|
var version:String;
|
||||||
|
|
||||||
var name:String;
|
var name:String;
|
||||||
var cameraZoom:Null<Float>;
|
var cameraZoom:Null<Float>;
|
||||||
var props:Array<StageDataProp>;
|
var props:Array<StageDataProp>;
|
||||||
|
@ -432,7 +452,7 @@ typedef StageDataProp =
|
||||||
* An optional array of animations which the prop can play.
|
* An optional array of animations which the prop can play.
|
||||||
* @default Prop has no animations.
|
* @default Prop has no animations.
|
||||||
*/
|
*/
|
||||||
var animations:Array<StageDataPropAnimation>;
|
var animations:Array<AnimationData>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If animations are used, this is the name of the animation to play first.
|
* If animations are used, this is the name of the animation to play first.
|
||||||
|
@ -448,52 +468,6 @@ typedef StageDataProp =
|
||||||
var animType:String;
|
var animType:String;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef StageDataPropAnimation =
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the animation.
|
|
||||||
*/
|
|
||||||
var name:String;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The common beginning of image names in atlas for this animation's frames.
|
|
||||||
* For example, if the frames are named "test0001.png", "test0002.png", etc., use "test".
|
|
||||||
*/
|
|
||||||
var prefix:String;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If you want this animation to use only certain frames of an animation with a given prefix,
|
|
||||||
* select them here.
|
|
||||||
* @example [0, 1, 2, 3] (use only the first four frames)
|
|
||||||
* @default [] (all frames)
|
|
||||||
*/
|
|
||||||
var frameIndices:Array<Int>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The speed of the animation in frames per second.
|
|
||||||
* @default 24
|
|
||||||
*/
|
|
||||||
var frameRate:Null<Int>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the animation should loop.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
var loop:Null<Bool>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to flip the sprite horizontally while animating.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
var flipX:Null<Bool>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to flip the sprite vertically while animating.
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
var flipY:Null<Bool>;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef StageDataCharacter =
|
typedef StageDataCharacter =
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -505,5 +479,12 @@ typedef StageDataCharacter =
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position to render the character at.
|
* The position to render the character at.
|
||||||
*/ position:Array<Float>
|
*/
|
||||||
|
position:Array<Float>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The camera offsets to apply when focusing on the character on this stage.
|
||||||
|
* @default [0, 0]
|
||||||
|
*/
|
||||||
|
cameraOffsets:Array<Float>,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
package funkin.ui;
|
package funkin.ui;
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.group.FlxSpriteGroup;
|
|
||||||
import flixel.graphics.frames.FlxAtlasFrames;
|
import flixel.graphics.frames.FlxAtlasFrames;
|
||||||
|
import flixel.group.FlxSpriteGroup;
|
||||||
import flixel.util.FlxStringUtil;
|
import flixel.util.FlxStringUtil;
|
||||||
|
|
||||||
@:forward
|
|
||||||
abstract BoldText(AtlasText) from AtlasText to AtlasText
|
|
||||||
{
|
|
||||||
inline public function new(x = 0.0, y = 0.0, text:String)
|
|
||||||
{
|
|
||||||
this = new AtlasText(x, y, text, Bold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alphabet.hx has a ton of bugs and does a bunch of stuff I don't need, fuck that class
|
* AtlasText is an improved version of Alphabet and FlxBitmapText.
|
||||||
|
* It supports animations on the letters, and is less buggy than Alphabet.
|
||||||
*/
|
*/
|
||||||
class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
|
class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
|
||||||
{
|
{
|
||||||
|
@ -41,7 +33,7 @@ class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
|
||||||
inline function get_maxHeight()
|
inline function get_maxHeight()
|
||||||
return font.maxHeight;
|
return font.maxHeight;
|
||||||
|
|
||||||
public function new(x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default)
|
public function new(x = 0.0, y = 0.0, text:String, fontName:AtlasFont = AtlasFont.DEFAULT)
|
||||||
{
|
{
|
||||||
if (!fonts.exists(fontName))
|
if (!fonts.exists(fontName))
|
||||||
fonts[fontName] = new AtlasFontData(fontName);
|
fonts[fontName] = new AtlasFontData(fontName);
|
||||||
|
@ -246,7 +238,14 @@ private class AtlasFontData
|
||||||
|
|
||||||
public function new(name:AtlasFont)
|
public function new(name:AtlasFont)
|
||||||
{
|
{
|
||||||
atlas = Paths.getSparrowAtlas("fonts/" + name.getName().toLowerCase());
|
var fontName:String = name;
|
||||||
|
atlas = Paths.getSparrowAtlas('fonts/${fontName.toLowerCase()}');
|
||||||
|
if (atlas == null)
|
||||||
|
{
|
||||||
|
FlxG.log.warn('Could not find font atlas for font "${fontName}".');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
atlas.parent.destroyOnNoUse = false;
|
atlas.parent.destroyOnNoUse = false;
|
||||||
atlas.parent.persist = true;
|
atlas.parent.persist = true;
|
||||||
|
|
||||||
|
@ -276,8 +275,8 @@ enum Case
|
||||||
Lower;
|
Lower;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AtlasFont
|
enum abstract AtlasFont(String) from String to String
|
||||||
{
|
{
|
||||||
Default;
|
var DEFAULT = "default";
|
||||||
Bold;
|
var BOLD = "bold";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package funkin.ui;
|
package funkin.ui;
|
||||||
|
|
||||||
import funkin.Controls;
|
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
@ -8,6 +7,7 @@ import flixel.group.FlxGroup;
|
||||||
import flixel.input.actions.FlxActionInput;
|
import flixel.input.actions.FlxActionInput;
|
||||||
import flixel.input.gamepad.FlxGamepadInputID;
|
import flixel.input.gamepad.FlxGamepadInputID;
|
||||||
import flixel.input.keyboard.FlxKey;
|
import flixel.input.keyboard.FlxKey;
|
||||||
|
import funkin.Controls;
|
||||||
import funkin.ui.AtlasText;
|
import funkin.ui.AtlasText;
|
||||||
import funkin.ui.MenuList;
|
import funkin.ui.MenuList;
|
||||||
import funkin.ui.TextMenuList;
|
import funkin.ui.TextMenuList;
|
||||||
|
@ -66,11 +66,11 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
|
|
||||||
var item;
|
var item;
|
||||||
|
|
||||||
item = deviceList.createItem("Keyboard", Bold, selectDevice.bind(Keys));
|
item = deviceList.createItem("Keyboard", AtlasFont.BOLD, selectDevice.bind(Keys));
|
||||||
item.x = FlxG.width / 2 - item.width - 30;
|
item.x = FlxG.width / 2 - item.width - 30;
|
||||||
item.y = (devicesBg.height - item.height) / 2;
|
item.y = (devicesBg.height - item.height) / 2;
|
||||||
|
|
||||||
item = deviceList.createItem("Gamepad", Bold, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
|
item = deviceList.createItem("Gamepad", AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
|
||||||
item.x = FlxG.width / 2 + 30;
|
item.x = FlxG.width / 2 + 30;
|
||||||
item.y = (devicesBg.height - item.height) / 2;
|
item.y = (devicesBg.height - item.height) / 2;
|
||||||
}
|
}
|
||||||
|
@ -87,20 +87,20 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
if (currentHeader != "UI_" && name.indexOf("UI_") == 0)
|
if (currentHeader != "UI_" && name.indexOf("UI_") == 0)
|
||||||
{
|
{
|
||||||
currentHeader = "UI_";
|
currentHeader = "UI_";
|
||||||
headers.add(new BoldText(0, y, "UI")).screenCenter(X);
|
headers.add(new AtlasText(0, y, "UI", AtlasFont.BOLD)).screenCenter(X);
|
||||||
y += spacer;
|
y += spacer;
|
||||||
}
|
}
|
||||||
else if (currentHeader != "NOTE_" && name.indexOf("NOTE_") == 0)
|
else if (currentHeader != "NOTE_" && name.indexOf("NOTE_") == 0)
|
||||||
{
|
{
|
||||||
currentHeader = "NOTE_";
|
currentHeader = "NOTE_";
|
||||||
headers.add(new BoldText(0, y, "NOTES")).screenCenter(X);
|
headers.add(new AtlasText(0, y, "NOTES", AtlasFont.BOLD)).screenCenter(X);
|
||||||
y += spacer;
|
y += spacer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentHeader != null && name.indexOf(currentHeader) == 0)
|
if (currentHeader != null && name.indexOf(currentHeader) == 0)
|
||||||
name = name.substr(currentHeader.length);
|
name = name.substr(currentHeader.length);
|
||||||
|
|
||||||
var label = labels.add(new BoldText(150, y, name));
|
var label = labels.add(new AtlasText(150, y, name, AtlasFont.BOLD));
|
||||||
label.alpha = 0.6;
|
label.alpha = 0.6;
|
||||||
for (i in 0...COLUMNS)
|
for (i in 0...COLUMNS)
|
||||||
createItem(label.x + 400 + i * 300, y, control, i);
|
createItem(label.x + 400 + i * 300, y, control, i);
|
||||||
|
@ -317,7 +317,7 @@ class InputItem extends TextMenuItem
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.input = getInput();
|
this.input = getInput();
|
||||||
|
|
||||||
super(x, y, getLabel(input), Default, callback);
|
super(x, y, getLabel(input), DEFAULT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateDevice(device:Device)
|
public function updateDevice(device:Device)
|
||||||
|
|
|
@ -5,10 +5,9 @@ import flixel.FlxSubState;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.util.FlxSignal;
|
import flixel.util.FlxSignal;
|
||||||
import funkin.i18n.FireTongueHandler.t;
|
import funkin.util.Constants;
|
||||||
|
import funkin.util.WindowUtil;
|
||||||
|
|
||||||
// typedef OptionsState = OptionsMenu_old;
|
|
||||||
// class OptionsState_new extends MusicBeatState
|
|
||||||
class OptionsState extends MusicBeatState
|
class OptionsState extends MusicBeatState
|
||||||
{
|
{
|
||||||
var pages = new Map<PageName, Page>();
|
var pages = new Map<PageName, Page>();
|
||||||
|
@ -31,17 +30,12 @@ class OptionsState extends MusicBeatState
|
||||||
var options = addPage(Options, new OptionsMenu(false));
|
var options = addPage(Options, new OptionsMenu(false));
|
||||||
var preferences = addPage(Preferences, new PreferencesMenu());
|
var preferences = addPage(Preferences, new PreferencesMenu());
|
||||||
var controls = addPage(Controls, new ControlsMenu());
|
var controls = addPage(Controls, new ControlsMenu());
|
||||||
// var colors = addPage(Colors, new ColorsMenu());
|
|
||||||
|
|
||||||
var mods = addPage(Mods, new ModMenu());
|
|
||||||
|
|
||||||
if (options.hasMultipleOptions())
|
if (options.hasMultipleOptions())
|
||||||
{
|
{
|
||||||
options.onExit.add(exitToMainMenu);
|
options.onExit.add(exitToMainMenu);
|
||||||
controls.onExit.add(switchPage.bind(Options));
|
controls.onExit.add(switchPage.bind(Options));
|
||||||
// colors.onExit.add(switchPage.bind(Options));
|
|
||||||
preferences.onExit.add(switchPage.bind(Options));
|
preferences.onExit.add(switchPage.bind(Options));
|
||||||
mods.onExit.add(switchPage.bind(Options));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -67,12 +61,18 @@ class OptionsState extends MusicBeatState
|
||||||
function setPage(name:PageName)
|
function setPage(name:PageName)
|
||||||
{
|
{
|
||||||
if (pages.exists(currentName))
|
if (pages.exists(currentName))
|
||||||
|
{
|
||||||
currentPage.exists = false;
|
currentPage.exists = false;
|
||||||
|
currentPage.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
currentName = name;
|
currentName = name;
|
||||||
|
|
||||||
if (pages.exists(currentName))
|
if (pages.exists(currentName))
|
||||||
|
{
|
||||||
currentPage.exists = true;
|
currentPage.exists = true;
|
||||||
|
currentPage.visible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override function finishTransIn()
|
override function finishTransIn()
|
||||||
|
@ -91,7 +91,7 @@ class OptionsState extends MusicBeatState
|
||||||
function exitToMainMenu()
|
function exitToMainMenu()
|
||||||
{
|
{
|
||||||
currentPage.enabled = false;
|
currentPage.enabled = false;
|
||||||
// Todo animate?
|
// TODO: Animate this transition?
|
||||||
FlxG.switchState(new MainMenuState());
|
FlxG.switchState(new MainMenuState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,30 +172,29 @@ class OptionsMenu extends Page
|
||||||
super();
|
super();
|
||||||
|
|
||||||
add(items = new TextMenuList());
|
add(items = new TextMenuList());
|
||||||
createItem(t("PREFERENCES"), function() switchPage(Preferences));
|
createItem("PREFERENCES", function() switchPage(Preferences));
|
||||||
createItem(t("CONTROLS"), function() switchPage(Controls));
|
createItem("CONTROLS", function() switchPage(Controls));
|
||||||
// createItem(t("COLORS"), function() switchPage(Colors));
|
// createItem("COLORS", function() switchPage(Colors));
|
||||||
createItem(t("MODS"), function() switchPage(Mods));
|
|
||||||
|
|
||||||
#if CAN_OPEN_LINKS
|
#if CAN_OPEN_LINKS
|
||||||
if (showDonate)
|
if (showDonate)
|
||||||
{
|
{
|
||||||
var hasPopupBlocker = #if web true #else false #end;
|
var hasPopupBlocker = #if web true #else false #end;
|
||||||
createItem(t("DONATE"), selectDonate, hasPopupBlocker);
|
createItem("DONATE", selectDonate, hasPopupBlocker);
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
#if newgrounds
|
#if newgrounds
|
||||||
if (NGio.isLoggedIn)
|
if (NGio.isLoggedIn)
|
||||||
createItem(t("LOGOUT"), selectLogout);
|
createItem("LOGOUT", selectLogout);
|
||||||
else
|
else
|
||||||
createItem(t("LOGIN"), selectLogin);
|
createItem("LOGIN", selectLogin);
|
||||||
#end
|
#end
|
||||||
createItem(t("EXIT"), exit);
|
createItem("EXIT", exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createItem(name:String, callback:Void->Void, fireInstantly = false)
|
function createItem(name:String, callback:Void->Void, fireInstantly = false)
|
||||||
{
|
{
|
||||||
var item = items.createItem(0, 100 + items.length * 100, name, Bold, callback);
|
var item = items.createItem(0, 100 + items.length * 100, name, BOLD, callback);
|
||||||
item.fireInstantly = fireInstantly;
|
item.fireInstantly = fireInstantly;
|
||||||
item.screenCenter(X);
|
item.screenCenter(X);
|
||||||
return item;
|
return item;
|
||||||
|
@ -219,11 +218,7 @@ class OptionsMenu extends Page
|
||||||
#if CAN_OPEN_LINKS
|
#if CAN_OPEN_LINKS
|
||||||
function selectDonate()
|
function selectDonate()
|
||||||
{
|
{
|
||||||
#if linux
|
WindowUtil.openURL(Constants.URL_ITCH);
|
||||||
Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]);
|
|
||||||
#else
|
|
||||||
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
|
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import funkin.ui.AtlasText.AtlasFont;
|
import funkin.ui.AtlasText.AtlasFont;
|
||||||
import funkin.ui.TextMenuList.TextMenuItem;
|
|
||||||
import funkin.ui.OptionsState.Page;
|
import funkin.ui.OptionsState.Page;
|
||||||
|
import funkin.ui.TextMenuList.TextMenuItem;
|
||||||
|
|
||||||
class PreferencesMenu extends Page
|
class PreferencesMenu extends Page
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,7 @@ class PreferencesMenu extends Page
|
||||||
|
|
||||||
private function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void
|
private function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void
|
||||||
{
|
{
|
||||||
items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.Bold, function()
|
items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function()
|
||||||
{
|
{
|
||||||
preferenceCheck(prefString, prefValue);
|
preferenceCheck(prefString, prefValue);
|
||||||
|
|
||||||
|
@ -157,16 +157,17 @@ class PreferencesMenu extends Page
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function preferenceCheck(prefString:String, prefValue:Dynamic):Void
|
private static function preferenceCheck(prefString:String, defaultValue:Dynamic):Void
|
||||||
{
|
{
|
||||||
if (preferences.get(prefString) == null)
|
if (preferences.get(prefString) == null)
|
||||||
{
|
{
|
||||||
preferences.set(prefString, prefValue);
|
// Set the value to default.
|
||||||
trace('set preference!');
|
preferences.set(prefString, defaultValue);
|
||||||
|
trace('Set preference to default: ${prefString} = ${defaultValue}');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('found preference: ' + preferences.get(prefString));
|
trace('Found preference: ${prefString} = ${preferences.get(prefString)}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package funkin.ui;
|
package funkin.ui;
|
||||||
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.graphics.frames.FlxAtlasFrames;
|
import funkin.ui.AtlasText.AtlasFont;
|
||||||
import flixel.text.FlxText;
|
|
||||||
import flixel.util.FlxColor;
|
|
||||||
import funkin.ui.AtlasText;
|
|
||||||
import funkin.ui.MenuList;
|
import funkin.ui.MenuList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a yes/no dialog box as a substate over the current state.
|
||||||
|
*/
|
||||||
class Prompt extends flixel.FlxSubState
|
class Prompt extends flixel.FlxSubState
|
||||||
{
|
{
|
||||||
inline static var MARGIN = 100;
|
inline static var MARGIN = 100;
|
||||||
|
@ -26,7 +26,7 @@ class Prompt extends flixel.FlxSubState
|
||||||
|
|
||||||
buttons = new TextMenuList(Horizontal);
|
buttons = new TextMenuList(Horizontal);
|
||||||
|
|
||||||
field = new BoldText(text);
|
field = new AtlasText(text, AtlasFont.BOLD);
|
||||||
field.scrollFactor.set(0, 0);
|
field.scrollFactor.set(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
||||||
super(navControls, wrapMode);
|
super(navControls, wrapMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback, fireInstantly = false)
|
public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, callback, fireInstantly = false)
|
||||||
{
|
{
|
||||||
var item = new TextMenuItem(x, y, name, font, callback);
|
var item = new TextMenuItem(x, y, name, font, callback);
|
||||||
item.fireInstantly = fireInstantly;
|
item.fireInstantly = fireInstantly;
|
||||||
|
@ -20,7 +20,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
||||||
|
|
||||||
class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
||||||
{
|
{
|
||||||
public function new(x = 0.0, y = 0.0, name:String, font:AtlasFont = Bold, callback)
|
public function new(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, callback)
|
||||||
{
|
{
|
||||||
super(x, y, new AtlasText(0, 0, name, font), name, callback);
|
super(x, y, new AtlasText(0, 0, name, font), name, callback);
|
||||||
setEmptyBackground();
|
setEmptyBackground();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package funkin.util;
|
package funkin.util;
|
||||||
|
|
||||||
import lime.app.Application;
|
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
|
import lime.app.Application;
|
||||||
|
|
||||||
class Constants
|
class Constants
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,8 @@ 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;
|
||||||
|
|
||||||
|
public static final FREAKY_MENU_BPM = 102;
|
||||||
|
|
||||||
#if debug
|
#if debug
|
||||||
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
||||||
|
|
||||||
|
@ -31,4 +33,7 @@ class Constants
|
||||||
return 'v${Application.current.meta.get('version')}' + VERSION_SUFFIX;
|
return 'v${Application.current.meta.get('version')}' + VERSION_SUFFIX;
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
|
||||||
|
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin";
|
||||||
}
|
}
|
||||||
|
|
31
source/funkin/util/VersionUtil.hx
Normal file
31
source/funkin/util/VersionUtil.hx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package funkin.util;
|
||||||
|
|
||||||
|
import thx.semver.Version;
|
||||||
|
import thx.semver.VersionRule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remember, increment the patch version (1.0.x) if you make a bugfix,
|
||||||
|
* increment the minor version (1.x.0) if you make a new feature (but previous content is still compatible),
|
||||||
|
* and increment the major version (x.0.0) if you make a breaking change (e.g. new API or reorganized file format).
|
||||||
|
*/
|
||||||
|
class VersionUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Checks that a given verison number satisisfies a given version rule.
|
||||||
|
* Version rule can be complex, e.g. "1.0.x" or ">=1.0.0,<1.1.0", or anything NPM supports.
|
||||||
|
*/
|
||||||
|
public static function validateVersion(version:String, versionRule:String):Bool
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var v:Version = version; // Perform a cast.
|
||||||
|
var vr:VersionRule = versionRule; // Perform a cast.
|
||||||
|
return v.satisfies(vr);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
trace('[VERSIONUTIL] Invalid semantic version: ${version}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
source/funkin/util/WindowUtil.hx
Normal file
18
source/funkin/util/WindowUtil.hx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package funkin.util;
|
||||||
|
|
||||||
|
class WindowUtil
|
||||||
|
{
|
||||||
|
public static function openURL(targetUrl:String)
|
||||||
|
{
|
||||||
|
#if CAN_OPEN_LINKS
|
||||||
|
#if linux
|
||||||
|
// Sys.command('/usr/bin/xdg-open', [, "&"]);
|
||||||
|
Sys.command('/usr/bin/xdg-open', [targetUrl, "&"]);
|
||||||
|
#else
|
||||||
|
FlxG.openURL(targetUrl);
|
||||||
|
#end
|
||||||
|
#else
|
||||||
|
trace('Cannot open')
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
42
source/funkin/util/assets/FlxAnimationUtil.hx
Normal file
42
source/funkin/util/assets/FlxAnimationUtil.hx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package funkin.util.assets;
|
||||||
|
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import funkin.play.AnimationData;
|
||||||
|
|
||||||
|
class FlxAnimationUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Properly adds an animation to a sprite based on JSON data.
|
||||||
|
*/
|
||||||
|
public static function addAtlasAnimation(target:FlxSprite, anim:AnimationData)
|
||||||
|
{
|
||||||
|
var frameRate = anim.frameRate == null ? 24 : anim.frameRate;
|
||||||
|
var looped = anim.looped == null ? false : anim.looped;
|
||||||
|
var flipX = anim.flipX == null ? false : anim.flipX;
|
||||||
|
var flipY = anim.flipY == null ? false : anim.flipY;
|
||||||
|
|
||||||
|
if (anim.frameIndices != null && anim.frameIndices.length > 0)
|
||||||
|
{
|
||||||
|
// trace('addByIndices(${anim.name}, ${anim.prefix}, ${anim.frameIndices}, ${frameRate}, ${looped}, ${flipX}, ${flipY})');
|
||||||
|
target.animation.addByIndices(anim.name, anim.prefix, anim.frameIndices, "", frameRate, looped, flipX, flipY);
|
||||||
|
// trace('RESULT:${target.animation.getAnimationList()}');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// trace('addByPrefix(${anim.name}, ${anim.prefix}, ${frameRate}, ${looped}, ${flipX}, ${flipY})');
|
||||||
|
target.animation.addByPrefix(anim.name, anim.prefix, frameRate, looped, flipX, flipY);
|
||||||
|
// trace('RESULT:${target.animation.getAnimationList()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properly adds multiple animations to a sprite based on JSON data.
|
||||||
|
*/
|
||||||
|
public static function addAtlasAnimations(target:FlxSprite, animations:Array<AnimationData>)
|
||||||
|
{
|
||||||
|
for (anim in animations)
|
||||||
|
{
|
||||||
|
addAtlasAnimation(target, anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue