diff --git a/.vscode/launch.json b/.vscode/launch.json index 15d3b4b7c..4afcd170e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,19 +1,17 @@ { "version": "0.2.0", "configurations": [ - { - "name": "HTML5 Debug", - "type": "chrome", - "request": "launch", - "url": "http://127.0.0.1:3001", - "sourceMaps": true, - "webRoot": "${workspaceFolder}", - "preLaunchTask": "debug: html" - }, { + // Launch in native/CPP "name": "Lime", "type": "lime", "request": "launch" + }, + { + // Evaluate macros with debugging enabled + "name": "Haxe Eval", + "type": "haxe-eval", + "request": "launch" } ] -} \ No newline at end of file +} diff --git a/Project.xml b/Project.xml index 938b96ed4..1382f5f71 100644 --- a/Project.xml +++ b/Project.xml @@ -2,7 +2,7 @@ - + @@ -24,10 +24,10 @@ - + - + @@ -37,78 +37,79 @@ - - - + + + - - + +
- - + + - - - - - - - - + + + + + + + +
- +
- - + + - - - - - - - - + + + + + + + +
- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + + @@ -119,26 +120,27 @@ - - + + - + - - + + - + - + + @@ -179,52 +181,57 @@ - - + + + + + + + + + + + - + + + - - - - + + + + - - - - - - - + + + +
- + - +
- + - +
- + - +
@@ -232,8 +239,8 @@ - - + +
@@ -259,10 +266,10 @@
- +
- + \ No newline at end of file diff --git a/example_mods/introMod/_polymod_icon.png b/example_mods/introMod/_polymod_icon.png new file mode 100644 index 000000000..2828bd554 Binary files /dev/null and b/example_mods/introMod/_polymod_icon.png differ diff --git a/example_mods/introMod/_polymod_meta.json b/example_mods/introMod/_polymod_meta.json new file mode 100644 index 000000000..ccd7405d8 --- /dev/null +++ b/example_mods/introMod/_polymod_meta.json @@ -0,0 +1,8 @@ +{ + "title": "Intro Mod", + "description": "An introductory mod.", + "author": "MasterEric", + "api_version": "0.1.0", + "mod_version": "1.0.0", + "license": "Apache-2.0" +} diff --git a/example_mods/introMod/assets/preload/data/introText.txt b/example_mods/introMod/data/introText.txt similarity index 100% rename from example_mods/introMod/assets/preload/data/introText.txt rename to example_mods/introMod/data/introText.txt diff --git a/example_mods/introMod/assets/preload/images/gfDanceTitle.png b/example_mods/introMod/images/gfDanceTitle.png similarity index 100% rename from example_mods/introMod/assets/preload/images/gfDanceTitle.png rename to example_mods/introMod/images/gfDanceTitle.png diff --git a/example_mods/testing123/_polymod_meta.json b/example_mods/testing123/_polymod_meta.json index e74efc4f3..0c134dd9f 100644 --- a/example_mods/testing123/_polymod_meta.json +++ b/example_mods/testing123/_polymod_meta.json @@ -1,8 +1,8 @@ -{ - "title": "Testing123", - "description": "Newgrounds? More like OLDGROUNDS lol.", - "author": "MasterEric", - "api_version": "0.1.0", - "mod_version": "1.0.0", - "license": "Apache-2.0" -} +{ + "title": "Testing123", + "description": "Newgrounds? More like OLDGROUNDS lol.", + "author": "MasterEric", + "api_version": "0.1.0", + "mod_version": "1.0.0", + "license": "Apache-2.0" +} diff --git a/source/BGSprite.hx b/source/BGSprite.hx deleted file mode 100644 index 45894efa6..000000000 --- a/source/BGSprite.hx +++ /dev/null @@ -1,64 +0,0 @@ -package; - -import flixel.FlxSprite; - -class BGSprite extends FlxSprite -{ - /** - Cool lil utility thing just so that it can easy do antialiasing and scrollfactor bullshit - */ - public var idleAnim:String; - - /** - * NOTE: loadOldWay param is just for current backward compatibility! Will be moved later! - */ - public function new(image:String, x:Float = 0, y:Float = 0, parX:Float = 1, parY:Float = 1, ?daAnimations:Array, ?loopingAnim:Bool = false, - ?loadOldWay:Bool = true) - { - super(x, y); - - if (loadOldWay) - { - if (daAnimations != null) - { - setupSparrow(image, daAnimations, loopingAnim); - } - else - { - justLoadImage(image); - } - } - - scrollFactor.set(parX, parY); - antialiasing = true; - } - - public function setupSparrow(image:String, daAnimations:Array, ?loopingAnim:Bool = false) - { - frames = Paths.getSparrowAtlas(image); - for (anims in daAnimations) - { - var daLoop:Bool = loopingAnim; - if (loopingAnim == null) - daLoop = false; - - animation.addByPrefix(anims, anims, 24, daLoop); - animation.play(anims); - - if (idleAnim == null) - idleAnim = anims; - } - } - - public function justLoadImage(image:String) - { - loadGraphic(Paths.image(image)); - active = false; - } - - public function dance():Void - { - if (idleAnim != null) - animation.play(idleAnim); - } -} diff --git a/source/BackgroundDancer.hx b/source/BackgroundDancer.hx deleted file mode 100644 index 43a68c145..000000000 --- a/source/BackgroundDancer.hx +++ /dev/null @@ -1,31 +0,0 @@ -package; - -import flixel.FlxSprite; -import flixel.graphics.frames.FlxAtlasFrames; - -class BackgroundDancer extends FlxSprite -{ - public function new(x:Float, y:Float) - { - super(x, y); - - frames = Paths.getSparrowAtlas("limo/limoDancer"); - animation.addByIndices('danceLeft', 'bg dancer sketch PINK', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); - animation.addByIndices('danceRight', 'bg dancer sketch PINK', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); - animation.play('danceLeft'); - animation.finish(); - antialiasing = true; - } - - var danceDir:Bool = false; - - public function dance():Void - { - danceDir = !danceDir; - - if (danceDir) - animation.play('danceRight', true); - else - animation.play('danceLeft', true); - } -} diff --git a/source/BackgroundGirls.hx b/source/BackgroundGirls.hx deleted file mode 100644 index 1a6030362..000000000 --- a/source/BackgroundGirls.hx +++ /dev/null @@ -1,41 +0,0 @@ -package; - -import flixel.FlxSprite; -import flixel.graphics.frames.FlxAtlasFrames; - -class BackgroundGirls extends FlxSprite -{ - public function new(x:Float, y:Float) - { - super(x, y); - - // BG fangirls dissuaded - frames = Paths.getSparrowAtlas('weeb/bgFreaks'); - - animation.addByIndices('danceLeft', 'BG girls group', CoolUtil.numberArray(14), "", 24, false); - animation.addByIndices('danceRight', 'BG girls group', CoolUtil.numberArray(30, 15), "", 24, false); - - animation.play('danceLeft'); - animation.finish(); - } - - var danceDir:Bool = false; - - public function getScared():Void - { - animation.addByIndices('danceLeft', 'BG fangirls dissuaded', CoolUtil.numberArray(14), "", 24, false); - animation.addByIndices('danceRight', 'BG fangirls dissuaded', CoolUtil.numberArray(30, 15), "", 24, false); - dance(); - animation.finish(); - } - - public function dance():Void - { - danceDir = !danceDir; - - if (danceDir) - animation.play('danceRight', true); - else - animation.play('danceLeft', true); - } -} diff --git a/source/Main.hx b/source/Main.hx index 5edf6f82a..fb2a01a69 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -1,5 +1,7 @@ package; +import funkin.InitState; +import funkin.MemoryCounter; import flixel.FlxGame; import flixel.FlxState; import openfl.Lib; @@ -42,9 +44,9 @@ class Main extends Sprite // 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). - modding.PolymodHandler.loadAllMods(); + funkin.modding.PolymodHandler.loadAllMods(); - i18n.FireTongueHandler.init(); + funkin.i18n.FireTongueHandler.init(); if (stage != null) { @@ -98,9 +100,11 @@ class Main extends Sprite #if debug fpsCounter = new FPS(10, 3, 0xFFFFFF); addChild(fpsCounter); + #if !html5 memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF); addChild(memoryCounter); #end + #end /* video = new Video(); diff --git a/Preloader.hx b/source/Preloader.hx similarity index 100% rename from Preloader.hx rename to source/Preloader.hx diff --git a/source/TankmenBG.hx b/source/TankmenBG.hx deleted file mode 100644 index b889b5a93..000000000 --- a/source/TankmenBG.hx +++ /dev/null @@ -1,89 +0,0 @@ -package; - -import flixel.FlxSprite; -import haxe.display.Display.Package; - -class TankmenBG extends FlxSprite -{ - public static var animationNotes:Array = []; - - public var strumTime:Float = 0; - public var goingRight:Bool = false; - public var tankSpeed:Float = 0.7; - - public var endingOffset:Float; - - public function new(x:Float, y:Float, isGoingRight:Bool) - { - super(x, y); - - // makeGraphic(200, 200); - - frames = Paths.getSparrowAtlas('tankmanKilled1'); - antialiasing = true; - animation.addByPrefix('run', 'tankman running', 24, true); - animation.addByPrefix('shot', 'John Shot ' + FlxG.random.int(1, 2), 24, false); - - animation.play('run'); - animation.curAnim.curFrame = FlxG.random.int(0, animation.curAnim.numFrames - 1); - - updateHitbox(); - - setGraphicSize(Std.int(width * 0.8)); - updateHitbox(); - } - - public function resetShit(x:Float, y:Float, isGoingRight:Bool) - { - setPosition(x, y); - goingRight = isGoingRight; - endingOffset = FlxG.random.float(50, 200); - tankSpeed = FlxG.random.float(0.6, 1); - - if (goingRight) - flipX = true; - } - - override function update(elapsed:Float) - { - super.update(elapsed); - - if (x >= FlxG.width * 1.2 || x <= FlxG.width * -0.5) - visible = false; - else - visible = true; - - if (animation.curAnim.name == 'run') - { - var endDirection:Float = (FlxG.width * 0.74) + endingOffset; - - if (goingRight) - { - endDirection = (FlxG.width * 0.02) - endingOffset; - - x = (endDirection + (Conductor.songPosition - strumTime) * tankSpeed); - } - else - { - x = (endDirection - (Conductor.songPosition - strumTime) * tankSpeed); - } - } - - if (Conductor.songPosition > strumTime) - { - // kill(); - animation.play('shot'); - - if (goingRight) - { - offset.y = 200; - offset.x = 300; - } - } - - if (animation.curAnim.name == 'shot' && animation.curAnim.curFrame >= animation.curAnim.frames.length - 1) - { - kill(); - } - } -} diff --git a/source/Alphabet.hx b/source/funkin/Alphabet.hx similarity index 99% rename from source/Alphabet.hx rename to source/funkin/Alphabet.hx index 5caaccbf4..018882e0c 100644 --- a/source/Alphabet.hx +++ b/source/funkin/Alphabet.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; diff --git a/source/Boyfriend.hx b/source/funkin/Boyfriend.hx similarity index 98% rename from source/Boyfriend.hx rename to source/funkin/Boyfriend.hx index d407ae8ab..12bf7553e 100644 --- a/source/Boyfriend.hx +++ b/source/funkin/Boyfriend.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; diff --git a/source/ButtonRemapSubstate.hx b/source/funkin/ButtonRemapSubstate.hx similarity index 88% rename from source/ButtonRemapSubstate.hx rename to source/funkin/ButtonRemapSubstate.hx index 64ff07948..35c7a1476 100644 --- a/source/ButtonRemapSubstate.hx +++ b/source/funkin/ButtonRemapSubstate.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSubState; diff --git a/source/Character.hx b/source/funkin/Character.hx similarity index 96% rename from source/Character.hx rename to source/funkin/Character.hx index 0c1eb4118..5c266078a 100644 --- a/source/Character.hx +++ b/source/funkin/Character.hx @@ -1,11 +1,14 @@ -package; +package funkin; -import Section.SwagSection; +import funkin.Note.NoteData; +import funkin.SongLoad.SwagSong; +import funkin.Section.SwagSection; import flixel.FlxSprite; import flixel.animation.FlxBaseAnimation; import flixel.graphics.frames.FlxAtlasFrames; import flixel.util.FlxSort; import haxe.io.Path; +import funkin.play.PlayState; using StringTools; @@ -19,7 +22,7 @@ class Character extends FlxSprite public var holdTimer:Float = 0; - public var animationNotes:Array = []; + public var animationNotes:Array = []; public function new(x:Float, y:Float, ?character:String = "bf", ?isPlayer:Bool = false) { @@ -267,6 +270,8 @@ class Character extends FlxSprite quickAnimAdd('singLEFTmiss', 'BF NOTE LEFT MISS'); quickAnimAdd('singRIGHTmiss', 'BF NOTE RIGHT MISS'); quickAnimAdd('singDOWNmiss', 'BF NOTE DOWN MISS'); + quickAnimAdd('preAttack', 'bf pre attack'); + quickAnimAdd('attack', 'boyfriend attack'); quickAnimAdd('hey', 'BF HEY'); quickAnimAdd('firstDeath', "BF dies"); @@ -582,27 +587,25 @@ class Character extends FlxSprite public function loadMappedAnims() { - var swagshit = SongLoad.loadFromJson('picospeaker', 'stress'); + var swagshit:SwagSong = SongLoad.loadFromJson('stress', 'stress'); - var notes = swagshit.extraNotes.get('picospeaker'); + var notes:Array = swagshit.noteMap.get('picospeaker'); for (section in notes) { - for (idk in section.sectionNotes) + for (noteData in section.sectionNotes) { - animationNotes.push(idk); + animationNotes.push(noteData); } } - TankmenBG.animationNotes = animationNotes; - trace(animationNotes); animationNotes.sort(sortAnims); } - function sortAnims(val1:Array, val2:Array):Int + function sortAnims(val1:NoteData, val2:NoteData):Int { - return FlxSort.byValues(FlxSort.ASCENDING, val1[0], val2[0]); + return FlxSort.byValues(FlxSort.ASCENDING, val1.strumTime, val2.strumTime); } function quickAnimAdd(name:String, prefix:String) @@ -657,13 +660,13 @@ class Character extends FlxSprite // for pico?? if (animationNotes.length > 0) { - if (Conductor.songPosition > animationNotes[0][0]) + if (Conductor.songPosition > animationNotes[0].strumTime) { - trace('played shoot anim' + animationNotes[0][1]); + trace('played shoot anim' + animationNotes[0].noteData); var shootAnim:Int = 1; - if (animationNotes[0][1] >= 2) + if ((cast animationNotes[0].noteData) >= 2) shootAnim = 3; shootAnim += FlxG.random.int(0, 1); @@ -689,6 +692,8 @@ class Character extends FlxSprite */ public function dance() { + if (animation == null) + return; if (!debugMode) { switch (curCharacter) @@ -723,6 +728,8 @@ class Character extends FlxSprite public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void { + if (animation == null) + return; animation.play(AnimName, Force, Reversed, Frame); var daOffset = animOffsets.get(AnimName); diff --git a/source/ComboCounter.hx b/source/funkin/ComboCounter.hx similarity index 99% rename from source/ComboCounter.hx rename to source/funkin/ComboCounter.hx index a7f9c921f..a5f8b71b5 100644 --- a/source/ComboCounter.hx +++ b/source/funkin/ComboCounter.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; diff --git a/source/Conductor.hx b/source/funkin/Conductor.hx similarity index 96% rename from source/Conductor.hx rename to source/funkin/Conductor.hx index a1a010ef7..765686740 100644 --- a/source/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import SongLoad.SwagSong; +import funkin.SongLoad.SwagSong; /** * ... diff --git a/source/Controls.hx b/source/funkin/Controls.hx similarity index 99% rename from source/Controls.hx rename to source/funkin/Controls.hx index 82a44af0f..31b2933f2 100644 --- a/source/Controls.hx +++ b/source/funkin/Controls.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxObject; import flixel.input.FlxInput; diff --git a/source/CoolUtil.hx b/source/funkin/CoolUtil.hx similarity index 97% rename from source/CoolUtil.hx rename to source/funkin/CoolUtil.hx index d548ab7b8..60e12f343 100644 --- a/source/CoolUtil.hx +++ b/source/funkin/CoolUtil.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.FlxState; @@ -15,7 +15,8 @@ import haxe.format.JsonParser; import lime.math.Rectangle; import lime.utils.Assets; import openfl.filters.ShaderFilter; -import shaderslmfao.ScreenWipeShader; +import funkin.play.PlayState; +import funkin.shaderslmfao.ScreenWipeShader; using StringTools; diff --git a/source/CutsceneAnimTestState.hx b/source/funkin/CutsceneAnimTestState.hx similarity index 99% rename from source/CutsceneAnimTestState.hx rename to source/funkin/CutsceneAnimTestState.hx index eddbfe62e..ae2a0b80f 100644 --- a/source/CutsceneAnimTestState.hx +++ b/source/funkin/CutsceneAnimTestState.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.FlxState; diff --git a/source/CutsceneCharacter.hx b/source/funkin/CutsceneCharacter.hx similarity index 99% rename from source/CutsceneCharacter.hx rename to source/funkin/CutsceneCharacter.hx index c85d53b92..cf2caa870 100644 --- a/source/CutsceneCharacter.hx +++ b/source/funkin/CutsceneCharacter.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; diff --git a/source/DialogueBox.hx b/source/funkin/DialogueBox.hx similarity index 99% rename from source/DialogueBox.hx rename to source/funkin/DialogueBox.hx index 75b9ea30d..cc582cc84 100644 --- a/source/DialogueBox.hx +++ b/source/funkin/DialogueBox.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.addons.text.FlxTypeText; @@ -8,6 +8,7 @@ import flixel.input.FlxKeyManager; import flixel.text.FlxText; import flixel.util.FlxColor; import flixel.util.FlxTimer; +import funkin.play.PlayState; using StringTools; diff --git a/source/Discord.hx b/source/funkin/Discord.hx similarity index 94% rename from source/Discord.hx rename to source/funkin/Discord.hx index 04e9ebfa1..34fce44ed 100644 --- a/source/Discord.hx +++ b/source/funkin/Discord.hx @@ -1,92 +1,92 @@ -package; - -import Sys.sleep; - -using StringTools; - -#if discord_rpc -import discord_rpc.DiscordRpc; -#end - -class DiscordClient -{ - #if discord_rpc - public function new() - { - trace("Discord Client starting..."); - DiscordRpc.start({ - clientID: "814588678700924999", - onReady: onReady, - onError: onError, - onDisconnected: onDisconnected - }); - trace("Discord Client started."); - - while (true) - { - DiscordRpc.process(); - sleep(2); - // trace("Discord Client Update"); - } - - DiscordRpc.shutdown(); - } - - public static function shutdown() - { - DiscordRpc.shutdown(); - } - - static function onReady() - { - DiscordRpc.presence({ - details: "In the Menus", - state: null, - largeImageKey: 'icon', - largeImageText: "Friday Night Funkin'" - }); - } - - static function onError(_code:Int, _message:String) - { - trace('Error! $_code : $_message'); - } - - static function onDisconnected(_code:Int, _message:String) - { - trace('Disconnected! $_code : $_message'); - } - - public static function initialize() - { - var DiscordDaemon = sys.thread.Thread.create(() -> - { - new DiscordClient(); - }); - trace("Discord Client initialized"); - } - - public static function changePresence(details:String, state:Null, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float) - { - var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0; - - if (endTimestamp > 0) - { - endTimestamp = startTimestamp + endTimestamp; - } - - DiscordRpc.presence({ - details: details, - state: state, - largeImageKey: 'icon', - largeImageText: "Friday Night Funkin'", - smallImageKey: smallImageKey, - // Obtained times are in milliseconds so they are divided so Discord can use it - startTimestamp: Std.int(startTimestamp / 1000), - endTimestamp: Std.int(endTimestamp / 1000) - }); - - // trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp'); - } - #end -} +package funkin; + +import Sys.sleep; + +using StringTools; + +#if discord_rpc +import discord_rpc.DiscordRpc; +#end + +class DiscordClient +{ + #if discord_rpc + public function new() + { + trace("Discord Client starting..."); + DiscordRpc.start({ + clientID: "814588678700924999", + onReady: onReady, + onError: onError, + onDisconnected: onDisconnected + }); + trace("Discord Client started."); + + while (true) + { + DiscordRpc.process(); + sleep(2); + // trace("Discord Client Update"); + } + + DiscordRpc.shutdown(); + } + + public static function shutdown() + { + DiscordRpc.shutdown(); + } + + static function onReady() + { + DiscordRpc.presence({ + details: "In the Menus", + state: null, + largeImageKey: 'icon', + largeImageText: "Friday Night Funkin'" + }); + } + + static function onError(_code:Int, _message:String) + { + trace('Error! $_code : $_message'); + } + + static function onDisconnected(_code:Int, _message:String) + { + trace('Disconnected! $_code : $_message'); + } + + public static function initialize() + { + var DiscordDaemon = sys.thread.Thread.create(() -> + { + new DiscordClient(); + }); + trace("Discord Client initialized"); + } + + public static function changePresence(details:String, state:Null, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float) + { + var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0; + + if (endTimestamp > 0) + { + endTimestamp = startTimestamp + endTimestamp; + } + + DiscordRpc.presence({ + details: details, + state: state, + largeImageKey: 'icon', + largeImageText: "Friday Night Funkin'", + smallImageKey: smallImageKey, + // Obtained times are in milliseconds so they are divided so Discord can use it + startTimestamp: Std.int(startTimestamp / 1000), + endTimestamp: Std.int(endTimestamp / 1000) + }); + + // trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp'); + } + #end +} diff --git a/source/FlxSwf.hx b/source/funkin/FlxSwf.hx similarity index 97% rename from source/FlxSwf.hx rename to source/funkin/FlxSwf.hx index 48d0eff11..87d106c0e 100644 --- a/source/FlxSwf.hx +++ b/source/funkin/FlxSwf.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxCamera; import flixel.FlxSprite; diff --git a/source/FlxVideo.hx b/source/funkin/FlxVideo.hx similarity index 98% rename from source/FlxVideo.hx rename to source/funkin/FlxVideo.hx index 7b9fa5702..82e7c366c 100644 --- a/source/FlxVideo.hx +++ b/source/funkin/FlxVideo.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxBasic; import flixel.FlxSprite; diff --git a/source/FreeplayState.hx b/source/funkin/FreeplayState.hx similarity index 97% rename from source/FreeplayState.hx rename to source/funkin/FreeplayState.hx index 719178bfd..c309ec5e0 100644 --- a/source/FreeplayState.hx +++ b/source/funkin/FreeplayState.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Controls.Control; +import funkin.Controls.Control; import flash.text.TextField; import flixel.FlxCamera; import flixel.FlxGame; @@ -21,15 +21,16 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxSpriteUtil; import flixel.util.FlxTimer; -import freeplayStuff.BGScrollingText; -import freeplayStuff.DJBoyfriend; -import freeplayStuff.FreeplayScore; -import freeplayStuff.SongMenuItem; +import funkin.freeplayStuff.BGScrollingText; +import funkin.freeplayStuff.DJBoyfriend; +import funkin.freeplayStuff.FreeplayScore; +import funkin.freeplayStuff.SongMenuItem; import lime.app.Future; import lime.utils.Assets; -import shaderslmfao.AngleMask; -import shaderslmfao.PureColor; -import shaderslmfao.StrokeShader; +import funkin.shaderslmfao.AngleMask; +import funkin.shaderslmfao.PureColor; +import funkin.shaderslmfao.StrokeShader; +import funkin.play.PlayState; using StringTools; @@ -78,6 +79,7 @@ class FreeplayState extends MusicBeatSubstate #if debug isDebug = true; addSong('Test', 1, 'bf-pixel'); + addSong('Pyro', 4, 'bf'); #end var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); @@ -517,10 +519,7 @@ class FreeplayState extends MusicBeatSubstate if (controls.BACK) { FlxG.sound.play(Paths.sound('cancelMenu')); - - close(); - - // FlxG.switchState(new MainMenuState()); + FlxG.switchState(new MainMenuState()); } if (accepted) diff --git a/source/GameOverSubstate.hx b/source/funkin/GameOverSubstate.hx similarity index 96% rename from source/GameOverSubstate.hx rename to source/funkin/GameOverSubstate.hx index 469a2a609..f35d5f094 100644 --- a/source/GameOverSubstate.hx +++ b/source/funkin/GameOverSubstate.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxObject; import flixel.FlxSubState; @@ -6,8 +6,9 @@ import flixel.math.FlxPoint; import flixel.system.FlxSound; import flixel.util.FlxColor; import flixel.util.FlxTimer; -import haxe.display.Display.Package; -import ui.PreferencesMenu; +import haxe.display.Display; +import funkin.ui.PreferencesMenu; +import funkin.play.PlayState; class GameOverSubstate extends MusicBeatSubstate { @@ -24,7 +25,7 @@ class GameOverSubstate extends MusicBeatSubstate gameOverMusic = new FlxSound(); FlxG.sound.list.add(gameOverMusic); - var daStage = PlayState.curStage; + var daStage = PlayState.curStageId; var daBf:String = ''; switch (daStage) { diff --git a/source/GitarooPause.hx b/source/funkin/GitarooPause.hx similarity index 97% rename from source/GitarooPause.hx rename to source/funkin/GitarooPause.hx index 61d431de1..d22620a46 100644 --- a/source/GitarooPause.hx +++ b/source/funkin/GitarooPause.hx @@ -1,7 +1,8 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; +import funkin.play.PlayState; class GitarooPause extends MusicBeatState { diff --git a/source/HealthIcon.hx b/source/funkin/HealthIcon.hx similarity index 97% rename from source/HealthIcon.hx rename to source/funkin/HealthIcon.hx index a11251de7..e3b48a153 100644 --- a/source/HealthIcon.hx +++ b/source/funkin/HealthIcon.hx @@ -1,7 +1,8 @@ -package; +package funkin; import flixel.FlxSprite; import openfl.utils.Assets; +import funkin.play.PlayState; using StringTools; diff --git a/source/Highscore.hx b/source/funkin/Highscore.hx similarity index 99% rename from source/Highscore.hx rename to source/funkin/Highscore.hx index 9618114de..45a115075 100644 --- a/source/Highscore.hx +++ b/source/funkin/Highscore.hx @@ -1,4 +1,4 @@ -package; +package funkin; class Highscore { diff --git a/source/InitState.hx b/source/funkin/InitState.hx similarity index 87% rename from source/InitState.hx rename to source/funkin/InitState.hx index 9526720b0..812df1b51 100644 --- a/source/InitState.hx +++ b/source/funkin/InitState.hx @@ -1,7 +1,7 @@ -package; +package funkin; -#if !(macro) -import charting.ChartingState; +import funkin.play.stage.StageData; +import funkin.charting.ChartingState; import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.TransitionData; @@ -10,9 +10,11 @@ import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.util.FlxColor; import openfl.display.BitmapData; -import play.PicoFight; -import ui.PreferencesMenu; -import ui.stageBuildShit.StageBuilderState; +import funkin.play.PicoFight; +import funkin.ui.PreferencesMenu; +import funkin.ui.stageBuildShit.StageBuilderState; +import funkin.util.macro.MacroUtil; +import funkin.play.PlayState; using StringTools; @@ -29,12 +31,17 @@ import sys.io.File; import sys.thread.Thread; #end +/** + * Initializes the game state using custom defines. + * Only used in Debug builds. + */ class InitState extends FlxTransitionableState { override public function create():Void { + trace('This is a debug build, loading InitState...'); #if android - FlxG.android.preventDefaultKeys = [FlxAndroidKey.BACK]; + FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK]; #end #if newgrounds NGio.init(); @@ -113,6 +120,8 @@ class InitState extends FlxTransitionableState // FlxTransitionableState.skipNextTransOut = true; FlxTransitionableState.skipNextTransIn = true; + StageDataParser.loadStageCache(); + #if song var song = getSong(); @@ -191,21 +200,12 @@ class InitState extends FlxTransitionableState LoadingState.loadAndSwitchState(new PlayState()); } } -#end function getWeek() - return Std.parseInt(getDefine("week")); + return Std.parseInt(MacroUtil.getDefine("week")); function getSong() - return getDefine("song"); + return MacroUtil.getDefine("song"); function getDif() - return Std.parseInt(getDefine("dif", "1")); - -macro function getDefine(key:String, defaultValue:String = null):haxe.macro.Expr -{ - var value = haxe.macro.Context.definedValue(key); - if (value == null) - value = defaultValue; - return macro $v{value}; -} + return Std.parseInt(MacroUtil.getDefine("dif", "1")); diff --git a/source/InputFormatter.hx b/source/funkin/InputFormatter.hx similarity index 99% rename from source/InputFormatter.hx rename to source/funkin/InputFormatter.hx index 30c10ae44..b2d3f596a 100644 --- a/source/InputFormatter.hx +++ b/source/funkin/InputFormatter.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Controls; +import funkin.Controls; import flixel.input.gamepad.FlxGamepad; import flixel.input.gamepad.FlxGamepadInputID; import flixel.input.keyboard.FlxKey; diff --git a/source/LatencyState.hx b/source/funkin/LatencyState.hx similarity index 98% rename from source/LatencyState.hx rename to source/funkin/LatencyState.hx index e4214577d..4e0ca07af 100644 --- a/source/LatencyState.hx +++ b/source/funkin/LatencyState.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.FlxState; diff --git a/source/LoadingState.hx b/source/funkin/LoadingState.hx similarity index 97% rename from source/LoadingState.hx rename to source/funkin/LoadingState.hx index 016377ecb..3b9d88b4f 100644 --- a/source/LoadingState.hx +++ b/source/funkin/LoadingState.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.FlxState; @@ -12,6 +12,7 @@ import lime.utils.AssetLibrary; import lime.utils.AssetManifest; import lime.utils.Assets as LimeAssets; import openfl.utils.Assets; +import funkin.play.PlayState; class LoadingState extends MusicBeatState { @@ -187,7 +188,14 @@ class LoadingState extends MusicBeatState static function getNextState(target:FlxState, stopMusic = false):FlxState { - Paths.setCurrentLevel("week" + PlayState.storyWeek); + if (PlayState.storyWeek == 0) + { + Paths.setCurrentLevel('tutorial'); + } + else + { + Paths.setCurrentLevel("week" + PlayState.storyWeek); + } #if NO_PRELOAD_ALL var loaded = isSoundLoaded(getSongPath()) && (!PlayState.SONG.needsVoices || isSoundLoaded(getVocalPath())) diff --git a/source/MainMenuState.hx b/source/funkin/MainMenuState.hx similarity index 96% rename from source/MainMenuState.hx rename to source/funkin/MainMenuState.hx index 5a7dc505a..f85c2a3c3 100644 --- a/source/MainMenuState.hx +++ b/source/funkin/MainMenuState.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import NGio; +import funkin.NGio; import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; @@ -17,12 +17,12 @@ import flixel.util.FlxColor; import flixel.util.FlxTimer; import lime.app.Application; import openfl.filters.ShaderFilter; -import shaderslmfao.ScreenWipeShader; -import ui.AtlasMenuList; -import ui.MenuList; -import ui.OptionsState; -import ui.PreferencesMenu; -import ui.Prompt; +import funkin.shaderslmfao.ScreenWipeShader; +import funkin.ui.AtlasMenuList; +import funkin.ui.MenuList; +import funkin.ui.OptionsState; +import funkin.ui.PreferencesMenu; +import funkin.ui.Prompt; using StringTools; @@ -31,7 +31,7 @@ import Discord.DiscordClient; #end #if newgrounds import io.newgrounds.NG; -import ui.NgPrompt; +import funkin.ui.NgPrompt; #end class MainMenuState extends MusicBeatState diff --git a/source/MemoryCounter.hx b/source/funkin/MemoryCounter.hx similarity index 98% rename from source/MemoryCounter.hx rename to source/funkin/MemoryCounter.hx index 2772ada77..8ce1f47f8 100644 --- a/source/MemoryCounter.hx +++ b/source/funkin/MemoryCounter.hx @@ -1,3 +1,5 @@ +package funkin; + import openfl.text.TextFormat; import openfl.system.System; import openfl.text.TextField; diff --git a/source/MenuCharacter.hx b/source/funkin/MenuCharacter.hx similarity index 98% rename from source/MenuCharacter.hx rename to source/funkin/MenuCharacter.hx index 5403478e1..14cc4e05f 100644 --- a/source/MenuCharacter.hx +++ b/source/funkin/MenuCharacter.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; diff --git a/source/MenuItem.hx b/source/funkin/MenuItem.hx similarity index 98% rename from source/MenuItem.hx rename to source/funkin/MenuItem.hx index 98b08f87d..de03c5b69 100644 --- a/source/MenuItem.hx +++ b/source/funkin/MenuItem.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; diff --git a/source/MusicBeatState.hx b/source/funkin/MusicBeatState.hx similarity index 96% rename from source/MusicBeatState.hx rename to source/funkin/MusicBeatState.hx index 5a5d1e18f..570c0c677 100644 --- a/source/MusicBeatState.hx +++ b/source/funkin/MusicBeatState.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Conductor.BPMChangeEvent; +import funkin.Conductor.BPMChangeEvent; import flixel.FlxGame; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxUIState; diff --git a/source/MusicBeatSubstate.hx b/source/funkin/MusicBeatSubstate.hx similarity index 95% rename from source/MusicBeatSubstate.hx rename to source/funkin/MusicBeatSubstate.hx index add132693..8374848e4 100644 --- a/source/MusicBeatSubstate.hx +++ b/source/funkin/MusicBeatSubstate.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Conductor.BPMChangeEvent; +import funkin.Conductor.BPMChangeEvent; import flixel.FlxSubState; class MusicBeatSubstate extends FlxSubState diff --git a/source/NGio.hx b/source/funkin/NGio.hx similarity index 99% rename from source/NGio.hx rename to source/funkin/NGio.hx index 2b30865bb..b0b429f6c 100644 --- a/source/NGio.hx +++ b/source/funkin/NGio.hx @@ -1,4 +1,4 @@ -package; +package funkin; #if newgrounds import flixel.util.FlxSignal; diff --git a/source/Note.hx b/source/funkin/Note.hx similarity index 76% rename from source/Note.hx rename to source/funkin/Note.hx index 1cb0b6a4e..290fea0b6 100644 --- a/source/Note.hx +++ b/source/funkin/Note.hx @@ -1,19 +1,13 @@ -package; +package funkin; import flixel.FlxSprite; -import flixel.graphics.frames.FlxAtlasFrames; import flixel.math.FlxMath; -import flixel.util.FlxColor; -import flixel.util.FlxTimer; -import shaderslmfao.ColorSwap; -import ui.PreferencesMenu; +import funkin.shaderslmfao.ColorSwap; +import funkin.ui.PreferencesMenu; +import funkin.play.PlayState; using StringTools; -#if polymod -import polymod.format.ParseRules.TargetSignatureElement; -#end - class Note extends FlxSprite { public var data = new NoteData(); @@ -43,29 +37,41 @@ class Note extends FlxSprite public var isSustainNote:Bool = false; public var colorSwap:ColorSwap; - + /** the lowercase name of the note, for anim control, i.e. left right up down */ public var dirName(get, never):String; - inline function get_dirName() return data.dirName; - + + inline function get_dirName() + return data.dirName; + /** the uppercase name of the note, for anim control, i.e. left right up down */ public var dirNameUpper(get, never):String; - inline function get_dirNameUpper() return data.dirNameUpper; - + + inline function get_dirNameUpper() + return data.dirNameUpper; + /** the lowercase name of the note's color, for anim control, i.e. purple blue green red */ public var colorName(get, never):String; - inline function get_colorName() return data.colorName; - + + inline function get_colorName() + return data.colorName; + /** the lowercase name of the note's color, for anim control, i.e. purple blue green red */ public var colorNameUpper(get, never):String; - inline function get_colorNameUpper() return data.colorNameUpper; - + + inline function get_colorNameUpper() + return data.colorNameUpper; + public var highStakes(get, never):Bool; - inline function get_highStakes() return data.highStakes; - + + inline function get_highStakes() + return data.highStakes; + public var lowStakes(get, never):Bool; - inline function get_lowStakes() return data.lowStakes; - + + inline function get_lowStakes() + return data.lowStakes; + public static var swagWidth:Float = 160 * 0.7; public static var PURP_NOTE:Int = 0; public static var GREEN_NOTE:Int = 2; @@ -103,8 +109,9 @@ class Note extends FlxSprite data.noteData = noteData; - var daStage:String = PlayState.curStage; + var daStage:String = PlayState.curStageId; + // TODO: Make this logic more generic switch (daStage) { case 'school' | 'schoolEvil': @@ -187,7 +194,7 @@ class Note extends FlxSprite x -= width / 2; - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) x += 30; if (prevNote.isSustainNote) @@ -263,7 +270,7 @@ class Note extends FlxSprite alpha = 0.3; } } - + static public function fromData(data:NoteData, prevNote:Note, isSustainNote = false) { return new Note(data.strumTime, data.noteData, prevNote, isSustainNote); @@ -276,101 +283,133 @@ typedef RawNoteData = var noteData:NoteType; var sustainLength:Float; var altNote:Bool; + var noteKind:NoteKind; } @:forward abstract NoteData(RawNoteData) { - public function new (strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, altNote = false) + public function new(strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, altNote = false, noteKind = NORMAL) { - this = - { strumTime : strumTime - , noteData : noteData - , sustainLength : sustainLength - , altNote : altNote + this = { + strumTime: strumTime, + noteData: noteData, + sustainLength: sustainLength, + altNote: altNote, + noteKind: noteKind } } - + public var note(get, never):NoteType; - inline function get_note() return this.noteData.value; - + + inline function get_note() + return this.noteData.value; + public var int(get, never):Int; - inline function get_int() return this.noteData.int; - + + inline function get_int() + return this.noteData.int; + public var dir(get, never):NoteDir; - inline function get_dir() return this.noteData.value; - + + inline function get_dir() + return this.noteData.value; + public var dirName(get, never):String; - inline function get_dirName() return dir.name; - + + inline function get_dirName() + return dir.name; + public var dirNameUpper(get, never):String; - inline function get_dirNameUpper() return dir.nameUpper; - + + inline function get_dirNameUpper() + return dir.nameUpper; + public var color(get, never):NoteColor; - inline function get_color() return this.noteData.value; - + + inline function get_color() + return this.noteData.value; + public var colorName(get, never):String; - inline function get_colorName() return color.name; - + + inline function get_colorName() + return color.name; + public var colorNameUpper(get, never):String; - inline function get_colorNameUpper() return color.nameUpper; - + + inline function get_colorNameUpper() + return color.nameUpper; + public var highStakes(get, never):Bool; - inline function get_highStakes() return this.noteData.highStakes; - + + inline function get_highStakes() + return this.noteData.highStakes; + public var lowStakes(get, never):Bool; - inline function get_lowStakes() return this.noteData.lowStakes; + + inline function get_lowStakes() + return this.noteData.lowStakes; } enum abstract NoteType(Int) from Int to Int { // public var raw(get, never):Int; // inline function get_raw() return this; - public var int(get, never):Int; - inline function get_int() return this < 0 ? -this : this % 4; - + + inline function get_int() + return this < 0 ? -this : this % 4; + public var value(get, never):NoteType; - inline function get_value() return int; - + + inline function get_value() + return int; + public var highStakes(get, never):Bool; - inline function get_highStakes() return this > 3; - + + inline function get_highStakes() + return this > 3; + public var lowStakes(get, never):Bool; - inline function get_lowStakes() return this < 0; + + inline function get_lowStakes() + return this < 0; } @:forward enum abstract NoteDir(NoteType) from Int to Int from NoteType { - var LEFT = 0; - var DOWN = 1; - var UP = 2; + var LEFT = 0; + var DOWN = 1; + var UP = 2; var RIGHT = 3; - var value(get, never):NoteDir; - inline function get_value() return this.value; - + + inline function get_value() + return this.value; + public var name(get, never):String; + function get_name() { - return switch(value) + return switch (value) { - case LEFT : "left" ; - case DOWN : "down" ; - case UP : "up" ; + case LEFT: "left"; + case DOWN: "down"; + case UP: "up"; case RIGHT: "right"; } } - + public var nameUpper(get, never):String; + function get_nameUpper() { - return switch(value) + return switch (value) { - case LEFT : "LEFT" ; - case DOWN : "DOWN" ; - case UP : "UP" ; + case LEFT: "LEFT"; + case DOWN: "DOWN"; + case UP: "UP"; case RIGHT: "RIGHT"; } } @@ -380,34 +419,47 @@ enum abstract NoteDir(NoteType) from Int to Int from NoteType enum abstract NoteColor(NoteType) from Int to Int from NoteType { var PURPLE = 0; - var BLUE = 1; - var GREEN = 2; - var RED = 3; - + var BLUE = 1; + var GREEN = 2; + var RED = 3; var value(get, never):NoteColor; - inline function get_value() return this.value; - + + inline function get_value() + return this.value; + public var name(get, never):String; + function get_name() { - return switch(value) + return switch (value) { case PURPLE: "purple"; - case BLUE : "blue" ; - case GREEN : "green" ; - case RED : "red" ; + case BLUE: "blue"; + case GREEN: "green"; + case RED: "red"; } } - + public var nameUpper(get, never):String; + function get_nameUpper() { - return switch(value) + return switch (value) { case PURPLE: "PURPLE"; - case BLUE : "BLUE" ; - case GREEN : "GREEN" ; - case RED : "RED" ; + case BLUE: "BLUE"; + case GREEN: "GREEN"; + case RED: "RED"; } } -} \ No newline at end of file +} + +enum abstract NoteKind(String) from String to String +{ + var NORMAL = "normal"; + var PYRO_LIGHT = "pyro_light"; + var PYRO_KICK = "pyro_kick"; + var PYRO_TOSS = "pyro_toss"; + var PYRO_COCK = "pyro_cock"; // lol + var PYRO_SHOOT = "pyro_shoot"; +} diff --git a/source/NoteSplash.hx b/source/funkin/NoteSplash.hx similarity index 94% rename from source/NoteSplash.hx rename to source/funkin/NoteSplash.hx index b23daefe6..3e4d3a3b5 100644 --- a/source/NoteSplash.hx +++ b/source/funkin/NoteSplash.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import haxe.io.Path; @@ -34,7 +34,8 @@ class NoteSplash extends FlxSprite animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true); animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2); - animation.finishCallback = function(name){ + animation.finishCallback = function(name) + { kill(); }; updateHitbox(); diff --git a/source/Options.hx b/source/funkin/Options.hx similarity index 79% rename from source/Options.hx rename to source/funkin/Options.hx index f392a8b1e..7edaa25af 100644 --- a/source/Options.hx +++ b/source/funkin/Options.hx @@ -1,4 +1,4 @@ -package; +package funkin; class Options { diff --git a/source/OutdatedSubState.hx b/source/funkin/OutdatedSubState.hx similarity index 96% rename from source/OutdatedSubState.hx rename to source/funkin/OutdatedSubState.hx index 1af9eb614..32792032e 100644 --- a/source/OutdatedSubState.hx +++ b/source/funkin/OutdatedSubState.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.FlxSubState; @@ -6,6 +6,7 @@ import flixel.text.FlxText; import flixel.util.FlxColor; import lime.app.Application; +#if newgrounds class OutdatedSubState extends MusicBeatState { public static var leftState:Bool = false; @@ -42,3 +43,4 @@ class OutdatedSubState extends MusicBeatState super.update(elapsed); } } +#end diff --git a/source/Paths.hx b/source/funkin/Paths.hx similarity index 99% rename from source/Paths.hx rename to source/funkin/Paths.hx index e1b42b901..03a90ff9a 100644 --- a/source/Paths.hx +++ b/source/funkin/Paths.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.graphics.frames.FlxAtlasFrames; import openfl.utils.AssetType; diff --git a/source/PauseSubState.hx b/source/funkin/PauseSubState.hx similarity index 98% rename from source/PauseSubState.hx rename to source/funkin/PauseSubState.hx index f2ee9c60f..14cbb63a8 100644 --- a/source/PauseSubState.hx +++ b/source/funkin/PauseSubState.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Controls.Control; +import funkin.Controls.Control; import flixel.FlxSprite; import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; @@ -11,6 +11,7 @@ import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import funkin.play.PlayState; class PauseSubState extends MusicBeatSubstate { diff --git a/source/PlayerSettings.hx b/source/funkin/PlayerSettings.hx similarity index 99% rename from source/PlayerSettings.hx rename to source/funkin/PlayerSettings.hx index 1b9681579..ab5d9e689 100644 --- a/source/PlayerSettings.hx +++ b/source/funkin/PlayerSettings.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Controls; +import funkin.Controls; import flixel.FlxCamera; import flixel.input.actions.FlxActionInput; import flixel.input.gamepad.FlxGamepad; diff --git a/source/Section.hx b/source/funkin/Section.hx similarity index 92% rename from source/Section.hx rename to source/funkin/Section.hx index e1fc84f5e..cc0931acb 100644 --- a/source/Section.hx +++ b/source/funkin/Section.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import Note.NoteData; +import funkin.Note.NoteData; typedef SwagSection = { diff --git a/source/SongLoad.hx b/source/funkin/SongLoad.hx similarity index 95% rename from source/SongLoad.hx rename to source/funkin/SongLoad.hx index c5d694220..770795602 100644 --- a/source/SongLoad.hx +++ b/source/funkin/SongLoad.hx @@ -1,7 +1,7 @@ -package; +package funkin; -import Note.NoteData; -import Section.SwagSection; +import funkin.Note.NoteData; +import funkin.Section.SwagSection; import haxe.Json; import haxe.format.JsonParser; import lime.utils.Assets; @@ -48,7 +48,7 @@ class SongLoad public static function loadFromJson(jsonInput:String, ?folder:String):SwagSong { - var rawJson = Assets.getText(Paths.json(folder.toLowerCase() + '/' + jsonInput.toLowerCase())).trim(); + var rawJson = Assets.getText(Paths.json('songs/${folder.toLowerCase()}/${jsonInput.toLowerCase()}')).trim(); while (!rawJson.endsWith("}")) { @@ -174,6 +174,8 @@ class SongLoad for (sectionIndex => section in noteStuff) { + if (section == null || section.sectionNotes == null) + continue; for (noteIndex => noteDataArray in section.sectionNotes) { var arrayDipshit:Array = cast noteDataArray; // crackhead @@ -188,6 +190,10 @@ class SongLoad noteStuff[sectionIndex].sectionNotes[noteIndex].noteData = arrayDipshit[1]; noteStuff[sectionIndex].sectionNotes[noteIndex].sustainLength = arrayDipshit[2]; noteStuff[sectionIndex].sectionNotes[noteIndex].altNote = arrayDipshit[3]; + if (arrayDipshit.length >= 5) + { + noteStuff[sectionIndex].sectionNotes[noteIndex].noteKind = arrayDipshit[4]; + } } else if (noteDataArray != null) { diff --git a/source/StoryMenuState.hx b/source/funkin/StoryMenuState.hx similarity index 98% rename from source/StoryMenuState.hx rename to source/funkin/StoryMenuState.hx index 8ff7d1370..00e482a8a 100644 --- a/source/StoryMenuState.hx +++ b/source/funkin/StoryMenuState.hx @@ -1,4 +1,4 @@ -package; +package funkin; #if discord_rpc import Discord.DiscordClient; @@ -14,6 +14,7 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; import lime.net.curl.CURLCode; +import funkin.play.PlayState; using StringTools; @@ -21,7 +22,7 @@ class StoryMenuState extends MusicBeatState { var scoreText:FlxText; - var weekData:Array = [ + var weekData:Array> = [ ['Tutorial'], ['Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South', "Monster"], @@ -430,11 +431,10 @@ class StoryMenuState extends MusicBeatState // grpWeekCharacters.members[0].updateHitbox(); } - var stringThing:Array = weekData[curWeek]; - - for (i in stringThing) + var trackNames:Array = weekData[curWeek]; + for (i in trackNames) { - txtTracklist.text += "\n" + i; + txtTracklist.text += '\n${i}'; } txtTracklist.text = txtTracklist.text.toUpperCase(); diff --git a/source/SwagCamera.hx b/source/funkin/SwagCamera.hx similarity index 99% rename from source/SwagCamera.hx rename to source/funkin/SwagCamera.hx index 4903b0e07..7184601b9 100644 --- a/source/SwagCamera.hx +++ b/source/funkin/SwagCamera.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxCamera; import flixel.FlxSprite; diff --git a/source/TankCutscene.hx b/source/funkin/TankCutscene.hx similarity index 96% rename from source/TankCutscene.hx rename to source/funkin/TankCutscene.hx index a8a18321c..a144b16fe 100644 --- a/source/TankCutscene.hx +++ b/source/funkin/TankCutscene.hx @@ -1,4 +1,4 @@ -package; +package funkin; import flixel.FlxSprite; import flixel.system.FlxSound; diff --git a/source/TitleState.hx b/source/funkin/TitleState.hx similarity index 97% rename from source/TitleState.hx rename to source/funkin/TitleState.hx index e3feea22d..f54bfbd3c 100644 --- a/source/TitleState.hx +++ b/source/funkin/TitleState.hx @@ -1,6 +1,6 @@ -package; +package funkin; -import audiovis.SpectogramSprite; +import funkin.audiovis.SpectogramSprite; import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; @@ -29,10 +29,10 @@ import openfl.events.NetStatusEvent; import openfl.media.Video; import openfl.net.NetConnection; import openfl.net.NetStream; -import shaderslmfao.BuildingShaders; -import shaderslmfao.ColorSwap; -import shaderslmfao.TitleOutline; -import ui.PreferencesMenu; +import funkin.shaderslmfao.BuildingShaders; +import funkin.shaderslmfao.ColorSwap; +import funkin.shaderslmfao.TitleOutline; +import funkin.ui.PreferencesMenu; using StringTools; @@ -351,17 +351,6 @@ class TitleState extends MusicBeatState FlxG.switchState(new CutsceneAnimTestState()); #end - /* - if (FlxG.keys.justPressed.R) - { - #if polymod - polymod.Polymod.init({modRoot: "mods", dirs: ['introMod']}); - trace('reinitialized'); - #end - } - - */ - if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time; if (FlxG.keys.justPressed.F) diff --git a/source/VideoState.hx b/source/funkin/VideoState.hx similarity index 99% rename from source/VideoState.hx rename to source/funkin/VideoState.hx index 5130cc223..d5954ccda 100644 --- a/source/VideoState.hx +++ b/source/funkin/VideoState.hx @@ -1,4 +1,4 @@ -package; +package funkin; import openfl.display.Sprite; import openfl.events.AsyncErrorEvent; diff --git a/source/VoicesGroup.hx b/source/funkin/VoicesGroup.hx similarity index 99% rename from source/VoicesGroup.hx rename to source/funkin/VoicesGroup.hx index 86f22d1d8..ae34bb0c6 100644 --- a/source/VoicesGroup.hx +++ b/source/funkin/VoicesGroup.hx @@ -1,3 +1,5 @@ +package funkin; + import flixel.group.FlxGroup.FlxTypedGroup; import flixel.system.FlxSound; diff --git a/source/animate/AnimTestStage.hx b/source/funkin/animate/AnimTestStage.hx similarity index 96% rename from source/animate/AnimTestStage.hx rename to source/funkin/animate/AnimTestStage.hx index eb7a2b38b..26e1fceb2 100644 --- a/source/animate/AnimTestStage.hx +++ b/source/funkin/animate/AnimTestStage.hx @@ -1,4 +1,4 @@ -package animate; +package funkin.animate; import flixel.FlxSprite; import flixel.FlxState; diff --git a/source/animate/AnimateTimeline.hx b/source/funkin/animate/AnimateTimeline.hx similarity index 94% rename from source/animate/AnimateTimeline.hx rename to source/funkin/animate/AnimateTimeline.hx index 70fc3f5a7..27f70f7ae 100644 --- a/source/animate/AnimateTimeline.hx +++ b/source/funkin/animate/AnimateTimeline.hx @@ -1,7 +1,5 @@ -package animate; +package funkin.animate; -// import animate.FlxSymbol.Parsed; -// import animate.FlxSymbol.Timeline; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; diff --git a/source/animate/FlxAnimate.hx b/source/funkin/animate/FlxAnimate.hx similarity index 97% rename from source/animate/FlxAnimate.hx rename to source/funkin/animate/FlxAnimate.hx index 60408697a..607776275 100644 --- a/source/animate/FlxAnimate.hx +++ b/source/funkin/animate/FlxAnimate.hx @@ -1,10 +1,8 @@ -package animate; +package funkin.animate; -// import animateAtlasPlayer.assets.AssetManager; -// import animateAtlasPlayer.core.Animation; -import animate.ParseAnimate.AnimJson; -import animate.ParseAnimate.Sprite; -import animate.ParseAnimate.Spritemap; +import funkin.animate.ParseAnimate.AnimJson; +import funkin.animate.ParseAnimate.Sprite; +import funkin.animate.ParseAnimate.Spritemap; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.graphics.FlxGraphic; diff --git a/source/animate/FlxSymbol.hx b/source/funkin/animate/FlxSymbol.hx similarity index 84% rename from source/animate/FlxSymbol.hx rename to source/funkin/animate/FlxSymbol.hx index bb995057b..3e2fffd84 100644 --- a/source/animate/FlxSymbol.hx +++ b/source/funkin/animate/FlxSymbol.hx @@ -1,12 +1,12 @@ -package animate; +package funkin.animate; -import animate.ParseAnimate.AnimJson; -import animate.ParseAnimate.Animation; -import animate.ParseAnimate.Frame; -import animate.ParseAnimate.Sprite; -import animate.ParseAnimate.Spritemap; -import animate.ParseAnimate.SymbolDictionary; -import animate.ParseAnimate.Timeline; +import funkin.animate.ParseAnimate.AnimJson; +import funkin.animate.ParseAnimate.Animation; +import funkin.animate.ParseAnimate.Frame; +import funkin.animate.ParseAnimate.Sprite; +import funkin.animate.ParseAnimate.Spritemap; +import funkin.animate.ParseAnimate.SymbolDictionary; +import funkin.animate.ParseAnimate.Timeline; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.graphics.frames.FlxFrame.FlxFrameAngle; diff --git a/source/animate/ParseAnimate.hx b/source/funkin/animate/ParseAnimate.hx similarity index 99% rename from source/animate/ParseAnimate.hx rename to source/funkin/animate/ParseAnimate.hx index 673cbcce1..a9902fa6d 100644 --- a/source/animate/ParseAnimate.hx +++ b/source/funkin/animate/ParseAnimate.hx @@ -1,10 +1,9 @@ -package animate; +package funkin.animate; import haxe.format.JsonParser; import openfl.Assets; import openfl.geom.Matrix3D; import openfl.geom.Matrix; -import sys.io.File; /** * Generally designed / written in a way that can be easily taken out of FNF and used elsewhere diff --git a/source/animate/TimelineFrame.hx b/source/funkin/animate/TimelineFrame.hx similarity index 94% rename from source/animate/TimelineFrame.hx rename to source/funkin/animate/TimelineFrame.hx index 629e91f62..dbd5bfc7a 100644 --- a/source/animate/TimelineFrame.hx +++ b/source/funkin/animate/TimelineFrame.hx @@ -1,6 +1,6 @@ -package animate; +package funkin.animate; -import animate.FlxSymbol.Frame; +import funkin.animate.ParseAnimate.Frame; import flixel.FlxSprite; import flixel.input.mouse.FlxMouseEventManager; import flixel.util.FlxColor; diff --git a/source/audiovis/ABotVis.hx b/source/funkin/audiovis/ABotVis.hx similarity index 97% rename from source/audiovis/ABotVis.hx rename to source/funkin/audiovis/ABotVis.hx index c6411c4d8..a05238e9f 100644 --- a/source/audiovis/ABotVis.hx +++ b/source/funkin/audiovis/ABotVis.hx @@ -1,13 +1,13 @@ -package audiovis; +package funkin.audiovis; -import audiovis.dsp.FFT; +import funkin.audiovis.dsp.FFT; import flixel.FlxSprite; import flixel.addons.plugin.taskManager.FlxTask; import flixel.graphics.frames.FlxAtlasFrames; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.math.FlxMath; import flixel.system.FlxSound; -import ui.PreferencesMenu.CheckboxThingie; +import funkin.ui.PreferencesMenu.CheckboxThingie; using Lambda; diff --git a/source/audiovis/PolygonSpectogram.hx b/source/funkin/audiovis/PolygonSpectogram.hx similarity index 96% rename from source/audiovis/PolygonSpectogram.hx rename to source/funkin/audiovis/PolygonSpectogram.hx index 31409c45f..d8af4e07d 100644 --- a/source/audiovis/PolygonSpectogram.hx +++ b/source/funkin/audiovis/PolygonSpectogram.hx @@ -1,12 +1,12 @@ -package audiovis; +package funkin.audiovis; -import audiovis.VisShit.CurAudioInfo; +import funkin.audiovis.VisShit.CurAudioInfo; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.system.FlxSound; import flixel.util.FlxColor; import lime.utils.Int16Array; -import rendering.MeshRender; +import funkin.rendering.MeshRender; class PolygonSpectogram extends MeshRender { diff --git a/source/audiovis/SpectogramSprite.hx b/source/funkin/audiovis/SpectogramSprite.hx similarity index 98% rename from source/audiovis/SpectogramSprite.hx rename to source/funkin/audiovis/SpectogramSprite.hx index 394bfadf6..b3cb6ff62 100644 --- a/source/audiovis/SpectogramSprite.hx +++ b/source/funkin/audiovis/SpectogramSprite.hx @@ -1,8 +1,8 @@ -package audiovis; +package funkin.audiovis; -import audiovis.PolygonSpectogram.VISTYPE; -import audiovis.VisShit.CurAudioInfo; -import audiovis.dsp.FFT; +import funkin.audiovis.PolygonSpectogram.VISTYPE; +import funkin.audiovis.VisShit.CurAudioInfo; +import funkin.audiovis.dsp.FFT; import flixel.FlxSprite; import flixel.group.FlxGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; diff --git a/source/audiovis/VisShit.hx b/source/funkin/audiovis/VisShit.hx similarity index 98% rename from source/audiovis/VisShit.hx rename to source/funkin/audiovis/VisShit.hx index 819a76e59..232d060a5 100644 --- a/source/audiovis/VisShit.hx +++ b/source/funkin/audiovis/VisShit.hx @@ -1,6 +1,6 @@ -package audiovis; +package funkin.audiovis; -import audiovis.dsp.FFT; +import funkin.audiovis.dsp.FFT; import flixel.math.FlxMath; import flixel.system.FlxSound; import lime.utils.Int16Array; diff --git a/source/audiovis/dsp/Complex.hx b/source/funkin/audiovis/dsp/Complex.hx similarity index 98% rename from source/audiovis/dsp/Complex.hx rename to source/funkin/audiovis/dsp/Complex.hx index 8dc8c9b7a..eeff9679f 100644 --- a/source/audiovis/dsp/Complex.hx +++ b/source/funkin/audiovis/dsp/Complex.hx @@ -1,4 +1,4 @@ -package audiovis.dsp; +package funkin.audiovis.dsp; /** Complex number representation. diff --git a/source/audiovis/dsp/FFT.hx b/source/funkin/audiovis/dsp/FFT.hx similarity index 97% rename from source/audiovis/dsp/FFT.hx rename to source/funkin/audiovis/dsp/FFT.hx index d46c8ffa1..7929c0f0a 100644 --- a/source/audiovis/dsp/FFT.hx +++ b/source/funkin/audiovis/dsp/FFT.hx @@ -1,9 +1,9 @@ -package audiovis.dsp; +package funkin.audiovis.dsp; -import audiovis.dsp.Complex; +import funkin.audiovis.dsp.Complex; -using audiovis.dsp.OffsetArray; -using audiovis.dsp.Signal; +using funkin.audiovis.dsp.OffsetArray; +using funkin.audiovis.dsp.Signal; // these are only used for testing, down in FFT.main() diff --git a/source/audiovis/dsp/OffsetArray.hx b/source/funkin/audiovis/dsp/OffsetArray.hx similarity index 98% rename from source/audiovis/dsp/OffsetArray.hx rename to source/funkin/audiovis/dsp/OffsetArray.hx index ec5ddf539..db06ce40f 100644 --- a/source/audiovis/dsp/OffsetArray.hx +++ b/source/funkin/audiovis/dsp/OffsetArray.hx @@ -1,4 +1,4 @@ -package audiovis.dsp; +package funkin.audiovis.dsp; /** A view into an Array with an indexing offset. diff --git a/source/audiovis/dsp/Signal.hx b/source/funkin/audiovis/dsp/Signal.hx similarity index 98% rename from source/audiovis/dsp/Signal.hx rename to source/funkin/audiovis/dsp/Signal.hx index 7c5631c21..aeb6c3f91 100644 --- a/source/audiovis/dsp/Signal.hx +++ b/source/funkin/audiovis/dsp/Signal.hx @@ -1,4 +1,4 @@ -package audiovis.dsp; +package funkin.audiovis.dsp; using Lambda; diff --git a/source/charting/ChartingState.hx b/source/funkin/charting/ChartingState.hx similarity index 99% rename from source/charting/ChartingState.hx rename to source/funkin/charting/ChartingState.hx index ab098e1ac..fd7b739de 100644 --- a/source/charting/ChartingState.hx +++ b/source/funkin/charting/ChartingState.hx @@ -1,12 +1,12 @@ -package charting; +package funkin.charting; -import Conductor.BPMChangeEvent; -import Note.NoteData; -import Section.SwagSection; -import SongLoad.SwagSong; -import audiovis.ABotVis; -import audiovis.PolygonSpectogram; -import audiovis.SpectogramSprite; +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.addons.display.FlxGridOverlay; import flixel.addons.transition.FlxTransitionableState; @@ -29,7 +29,8 @@ import lime.utils.Assets; import openfl.events.Event; import openfl.events.IOErrorEvent; import openfl.net.FileReference; -import rendering.MeshRender; +import funkin.rendering.MeshRender; +import funkin.play.PlayState; using Lambda; using StringTools; diff --git a/source/freeplayStuff/BGScrollingText.hx b/source/funkin/freeplayStuff/BGScrollingText.hx similarity index 98% rename from source/freeplayStuff/BGScrollingText.hx rename to source/funkin/freeplayStuff/BGScrollingText.hx index f25b8c82e..39837e031 100644 --- a/source/freeplayStuff/BGScrollingText.hx +++ b/source/funkin/freeplayStuff/BGScrollingText.hx @@ -1,4 +1,4 @@ -package freeplayStuff; +package funkin.freeplayStuff; import flixel.FlxObject; import flixel.group.FlxGroup.FlxTypedGroup; diff --git a/source/freeplayStuff/DJBoyfriend.hx b/source/funkin/freeplayStuff/DJBoyfriend.hx similarity index 97% rename from source/freeplayStuff/DJBoyfriend.hx rename to source/funkin/freeplayStuff/DJBoyfriend.hx index 25c725b11..8ea52370d 100644 --- a/source/freeplayStuff/DJBoyfriend.hx +++ b/source/funkin/freeplayStuff/DJBoyfriend.hx @@ -1,4 +1,4 @@ -package freeplayStuff; +package funkin.freeplayStuff; import flixel.FlxSprite; import flixel.util.FlxSignal; diff --git a/source/freeplayStuff/FreeplayScore.hx b/source/funkin/freeplayStuff/FreeplayScore.hx similarity index 96% rename from source/freeplayStuff/FreeplayScore.hx rename to source/funkin/freeplayStuff/FreeplayScore.hx index 315f881ea..cef363cd0 100644 --- a/source/freeplayStuff/FreeplayScore.hx +++ b/source/funkin/freeplayStuff/FreeplayScore.hx @@ -1,4 +1,4 @@ -package freeplayStuff; +package funkin.freeplayStuff; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; @@ -11,6 +11,8 @@ class FreeplayScore extends FlxTypedSpriteGroup function set_scoreShit(val):Int { + if (group == null || group.members == null) + return val; var loopNum:Int = group.members.length - 1; var dumbNumb = Std.parseInt(Std.string(val)); var prevNum:ScoreNum; diff --git a/source/freeplayStuff/SongMenuItem.hx b/source/funkin/freeplayStuff/SongMenuItem.hx similarity index 98% rename from source/freeplayStuff/SongMenuItem.hx rename to source/funkin/freeplayStuff/SongMenuItem.hx index cf9935ee2..7eb862bef 100644 --- a/source/freeplayStuff/SongMenuItem.hx +++ b/source/funkin/freeplayStuff/SongMenuItem.hx @@ -1,4 +1,4 @@ -package freeplayStuff; +package funkin.freeplayStuff; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; diff --git a/source/i18n/FireTongueHandler.hx b/source/funkin/i18n/FireTongueHandler.hx similarity index 99% rename from source/i18n/FireTongueHandler.hx rename to source/funkin/i18n/FireTongueHandler.hx index 53a16059d..fdb13e41c 100644 --- a/source/i18n/FireTongueHandler.hx +++ b/source/funkin/i18n/FireTongueHandler.hx @@ -1,4 +1,4 @@ -package i18n; +package funkin.i18n; import firetongue.FireTongue; diff --git a/source/funkin/i18n/README.md b/source/funkin/i18n/README.md new file mode 100644 index 000000000..d55a8f41c --- /dev/null +++ b/source/funkin/i18n/README.md @@ -0,0 +1,3 @@ +# i18n + +This package contains functions used for internationalization (i18n). diff --git a/source/import.hx b/source/funkin/import.hx similarity index 88% rename from source/import.hx rename to source/funkin/import.hx index 13736ea5e..67be91c51 100644 --- a/source/import.hx +++ b/source/funkin/import.hx @@ -1,6 +1,6 @@ #if !macro // Only import these when we aren't in a macro. -import Paths; +import funkin.Paths; import flixel.FlxG; // This one in particular causes a compile error if you're using macros. #end diff --git a/source/modding/IHook.hx b/source/funkin/modding/IHook.hx similarity index 92% rename from source/modding/IHook.hx rename to source/funkin/modding/IHook.hx index a1a75d08d..fdcf9da27 100644 --- a/source/modding/IHook.hx +++ b/source/funkin/modding/IHook.hx @@ -1,13 +1,13 @@ -package modding; - -import polymod.hscript.HScriptable; - -/** - * Add this interface to a class to make it a scriptable object. - * Functions annotated with @:hscript will call the relevant script. - */ -@:hscript({ - // ALL of these values are added to ALL scripts in the child classes. - context: [FlxG, FlxSprite, Math, Paths, Std] -}) -interface IHook extends HScriptable {} +package funkin.modding; + +import polymod.hscript.HScriptable; + +/** + * Add this interface to a class to make it a scriptable object. + * Functions annotated with @:hscript will call the relevant script. + */ +@:hscript({ + // ALL of these values are added to ALL scripts in the child classes. + context: [FlxG, FlxSprite, Math, Paths, Std] +}) +interface IHook extends HScriptable {} diff --git a/source/funkin/modding/PolymodErrorHandler.hx b/source/funkin/modding/PolymodErrorHandler.hx new file mode 100644 index 000000000..c0616dfb5 --- /dev/null +++ b/source/funkin/modding/PolymodErrorHandler.hx @@ -0,0 +1,71 @@ +package funkin.modding; + +import polymod.Polymod; + +class PolymodErrorHandler +{ + /** + * Show a popup with the given text. + * This displays a system popup, it WILL interrupt the game. + * Make sure to only use this when it's important, like when there's a script error. + * + * @param name The name at the top of the popup. + * @param desc The body text of the popup. + */ + public static function showAlert(name:String, desc:String):Void + { + lime.app.Application.current.window.alert(desc, name); + } + + public static function onPolymodError(error:PolymodError):Void + { + // Perform an action based on the error code. + switch (error.code) + { + case MOD_LOAD_PREPARE: + logInfo('[POLYMOD]: ${error.message}'); + case MOD_LOAD_DONE: + logInfo('[POLYMOD]: ${error.message}'); + case MISSING_ICON: + logWarn('[POLYMOD]: A mod is missing an icon. Please add one.'); + case SCRIPT_PARSE_ERROR: + // A syntax error when parsing a script. + logError('[POLYMOD]: ${error.message}'); + showAlert('Polymod Script Parsing Error', error.message); + case SCRIPT_EXCEPTION: + // A runtime error when running a script. + logError('[POLYMOD]: ${error.message}'); + showAlert('Polymod Script Execution Error', error.message); + case SCRIPT_CLASS_NOT_FOUND: + // A scripted class tried to reference an unknown superclass. + logError('[POLYMOD]: ${error.message}'); + showAlert('Polymod Script Parsing Error', error.message); + default: + // Log the message based on its severity. + switch (error.severity) + { + case NOTICE: + logInfo('[POLYMOD]: ${error.message}'); + case WARNING: + logWarn('[POLYMOD]: ${error.message}'); + case ERROR: + logError('[POLYMOD]: ${error.message}'); + } + } + } + + static function logInfo(message:String):Void + { + trace('[INFO ] ${message}'); + } + + static function logError(message:String):Void + { + trace('[ERROR] ${message}'); + } + + static function logWarn(message:String):Void + { + trace('[WARN ] ${message}'); + } +} diff --git a/source/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx similarity index 69% rename from source/modding/PolymodHandler.hx rename to source/funkin/modding/PolymodHandler.hx index de4438b54..1f6d149cb 100644 --- a/source/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -1,202 +1,168 @@ -package modding; - -#if polymod -import polymod.Polymod.ModMetadata; -import polymod.Polymod; -import polymod.backends.OpenFLBackend; -import polymod.backends.PolymodAssets.PolymodAssetType; -import polymod.format.ParseRules.LinesParseFormat; -import polymod.format.ParseRules.TextFileFormat; -#end - -class PolymodHandler -{ - /** - * The API version that mods should comply with. - * Format this with Semantic Versioning; ... - * Bug fixes increment the patch version, new features increment the minor version. - * Changes that break old mods increment the major version. - */ - static final API_VERSION = "0.1.0"; - - /** - * Where relative to the executable that mods are located. - */ - static final MOD_FOLDER = "mods"; - - /** - * Loads the game with ALL mods enabled with Polymod. - */ - public static function loadAllMods() - { - #if polymod - trace("Initializing Polymod (using all mods)..."); - loadModsById(getAllModIds()); - #else - trace("Polymod not initialized; not supported on this platform."); - #end - } - - /** - * Loads the game without any mods enabled with Polymod. - */ - public static function loadNoMods() - { - // We still need to configure the debug print calls etc. - #if polymod - trace("Initializing Polymod (using no mods)..."); - loadModsById([]); - #else - trace("Polymod not initialized; not supported on this platform."); - #end - } - - public static function loadModsById(ids:Array) - { - #if polymod - if (ids.length == 0) - { - trace('You attempted to load zero mods.'); - } - else - { - trace('Attempting to load ${ids.length} mods...'); - } - var loadedModList = polymod.Polymod.init({ - // Root directory for all mods. - modRoot: MOD_FOLDER, - // The directories for one or more mods to load. - dirs: ids, - // Framework being used to load assets. - framework: OPENFL, - // The current version of our API. - apiVersion: API_VERSION, - // Call this function any time an error occurs. - errorCallback: onPolymodError, - // Enforce semantic version patterns for each mod. - // modVersions: null, - // A map telling Polymod what the asset type is for unfamiliar file extensions. - // extensionMap: [], - - frameworkParams: buildFrameworkParams(), - - // List of filenames to ignore in mods. Use the default list to ignore the metadata file, etc. - ignoredFiles: Polymod.getDefaultIgnoreList(), - - // Parsing rules for various data formats. - parseRules: buildParseRules(), - }); - - if (loadedModList == null) - { - trace('[POLYMOD] An error occurred! Failed when loading mods!'); - } - else - { - if (loadedModList.length == 0) - { - trace('[POLYMOD] Mod loading complete. We loaded no mods / ${ids.length} mods.'); - } - else - { - trace('[POLYMOD] Mod loading complete. We loaded ${loadedModList.length} / ${ids.length} mods.'); - } - } - - for (mod in loadedModList) - trace(' * ${mod.title} v${mod.modVersion} [${mod.id}]'); - - #if debug - var fileList = Polymod.listModFiles("IMAGE"); - trace('[POLYMOD] Installed mods have replaced ${fileList.length} images.'); - for (item in fileList) - trace(' * $item'); - - fileList = Polymod.listModFiles("TEXT"); - trace('[POLYMOD] Installed mods have replaced ${fileList.length} text files.'); - for (item in fileList) - trace(' * $item'); - - fileList = Polymod.listModFiles("MUSIC"); - trace('[POLYMOD] Installed mods have replaced ${fileList.length} music files.'); - for (item in fileList) - trace(' * $item'); - - fileList = Polymod.listModFiles("SOUND"); - trace('[POLYMOD] Installed mods have replaced ${fileList.length} sound files.'); - for (item in fileList) - trace(' * $item'); - #end - #else - trace("[POLYMOD] Mods are not supported on this platform."); - #end - } - - #if polymod - static function buildParseRules():polymod.format.ParseRules - { - var output = polymod.format.ParseRules.getDefault(); - // Ensure TXT files have merge support. - output.addType("txt", TextFileFormat.LINES); - // Ensure script files have merge support. - output.addType("hscript", TextFileFormat.PLAINTEXT); - - // You can specify the format of a specific file, with file extension. - // output.addFile("data/introText.txt", TextFileFormat.LINES) - return output; - } - - static inline function buildFrameworkParams():polymod.Polymod.FrameworkParams - { - return { - assetLibraryPaths: [ - "songs" => "./songs", "shared" => "./", "tutorial" => "./tutorial", "scripts" => "./scripts", "week1" => "./week1", "week2" => "./week2", - "week3" => "./week3", "week4" => "./week4", "week5" => "./week5", "week6" => "./week6", "week7" => "./week7", "week8" => "./week8", - ] - } - } - - static function onPolymodError(error:PolymodError):Void - { - // Perform an action based on the error code. - switch (error.code) - { - case MOD_LOAD_PREPARE: - trace('[POLYMOD] ${error.message}'); - case MOD_LOAD_DONE: - trace('[POLYMOD] ${error.message}'); - case MISSING_ICON: - trace('[POLYMOD] A mod is missing an icon. Please add one.'); - default: - // Log the message based on its severity. - switch (error.severity) - { - case NOTICE: - trace('[POLYMOD] ${error.message}'); - case WARNING: - trace('[POLYMOD] ${error.message}'); - case ERROR: - trace('[POLYMOD] ${error.message}'); - } - } - } - #end - - public static function getAllMods() - { - #if polymod - trace('Scanning the mods folder...'); - var modMetadata = Polymod.scan(MOD_FOLDER); - trace('Found ${modMetadata.length} mods when scanning.'); - return modMetadata; - #else - return new Array(); - #end - } - - public static function getAllModIds():Array - { - var modIds = [for (i in getAllMods()) i.id]; - return modIds; - } -} +package funkin.modding; + +import polymod.Polymod.ModMetadata; +import polymod.Polymod; +import polymod.backends.OpenFLBackend; +import polymod.backends.PolymodAssets.PolymodAssetType; +import polymod.format.ParseRules.LinesParseFormat; +import polymod.format.ParseRules.TextFileFormat; + +class PolymodHandler +{ + /** + * The API version that mods should comply with. + * Format this with Semantic Versioning; ... + * Bug fixes increment the patch version, new features increment the minor version. + * Changes that break old mods increment the major version. + */ + static final API_VERSION = "0.1.0"; + + /** + * Where relative to the executable that mods are located. + */ + static final MOD_FOLDER = "mods"; + + /** + * Loads the game with ALL mods enabled with Polymod. + */ + public static function loadAllMods() + { + trace("Initializing Polymod (using all mods)..."); + loadModsById(getAllModIds()); + } + + /** + * Loads the game without any mods enabled with Polymod. + */ + public static function loadNoMods() + { + // We still need to configure the debug print calls etc. + trace("Initializing Polymod (using no mods)..."); + loadModsById([]); + } + + public static function loadModsById(ids:Array) + { + if (ids.length == 0) + { + trace('You attempted to load zero mods.'); + } + else + { + trace('Attempting to load ${ids.length} mods...'); + } + var loadedModList = polymod.Polymod.init({ + // Root directory for all mods. + modRoot: MOD_FOLDER, + // The directories for one or more mods to load. + dirs: ids, + // Framework being used to load assets. + framework: OPENFL, + // The current version of our API. + apiVersion: API_VERSION, + // Call this function any time an error occurs. + errorCallback: PolymodErrorHandler.onPolymodError, + // Enforce semantic version patterns for each mod. + // modVersions: null, + // A map telling Polymod what the asset type is for unfamiliar file extensions. + // extensionMap: [], + + frameworkParams: buildFrameworkParams(), + + // List of filenames to ignore in mods. Use the default list to ignore the metadata file, etc. + ignoredFiles: Polymod.getDefaultIgnoreList(), + + // Parsing rules for various data formats. + parseRules: buildParseRules(), + + // Parse hxc files and register the scripted classes in them. + useScriptedClasses: true, + }); + + if (loadedModList == null) + { + trace('[POLYMOD] An error occurred! Failed when loading mods!'); + } + else + { + if (loadedModList.length == 0) + { + trace('[POLYMOD] Mod loading complete. We loaded no mods / ${ids.length} mods.'); + } + else + { + trace('[POLYMOD] Mod loading complete. We loaded ${loadedModList.length} / ${ids.length} mods.'); + } + } + + for (mod in loadedModList) + { + trace(' * ${mod.title} v${mod.modVersion} [${mod.id}]'); + } + + #if debug + var fileList = Polymod.listModFiles(PolymodAssetType.IMAGE); + trace('[POLYMOD] Installed mods have replaced ${fileList.length} images.'); + for (item in fileList) + trace(' * $item'); + + fileList = Polymod.listModFiles(PolymodAssetType.TEXT); + trace('[POLYMOD] Installed mods have replaced ${fileList.length} text files.'); + for (item in fileList) + trace(' * $item'); + + fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_MUSIC); + trace('[POLYMOD] Installed mods have replaced ${fileList.length} music files.'); + for (item in fileList) + trace(' * $item'); + + fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_SOUND); + trace('[POLYMOD] Installed mods have replaced ${fileList.length} sound files.'); + for (item in fileList) + trace(' * $item'); + + fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_GENERIC); + trace('[POLYMOD] Installed mods have replaced ${fileList.length} generic audio files.'); + for (item in fileList) + trace(' * $item'); + #end + } + + static function buildParseRules():polymod.format.ParseRules + { + var output = polymod.format.ParseRules.getDefault(); + // Ensure TXT files have merge support. + output.addType("txt", TextFileFormat.LINES); + // Ensure script files have merge support. + output.addType("hscript", TextFileFormat.PLAINTEXT); + output.addType("hxs", TextFileFormat.PLAINTEXT); + + // You can specify the format of a specific file, with file extension. + // output.addFile("data/introText.txt", TextFileFormat.LINES) + return output; + } + + static inline function buildFrameworkParams():polymod.Polymod.FrameworkParams + { + return { + assetLibraryPaths: [ + "songs" => "songs", "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1", "week2" => "week2", + "week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "week8" => "week8", + ] + } + } + + public static function getAllMods():Array + { + trace('Scanning the mods folder...'); + var modMetadata = Polymod.scan(MOD_FOLDER); + trace('Found ${modMetadata.length} mods when scanning.'); + return modMetadata; + } + + public static function getAllModIds():Array + { + var modIds = [for (i in getAllMods()) i.id]; + return modIds; + } +} diff --git a/source/funkin/modding/base/README.md b/source/funkin/modding/base/README.md new file mode 100644 index 000000000..ebcea06f3 --- /dev/null +++ b/source/funkin/modding/base/README.md @@ -0,0 +1,5 @@ +# modding.base + +This package is used to allow modders to create scripted classes which extend these base classes. +For example, one script can extend FlxSprite and another can call `ScriptedFlxSprite.init('ClassName')`. +Most of these scripted class stubs are not used by the game itself, so this package has been explicitly marked to be ignored by DCE. diff --git a/source/funkin/modding/base/ScriptedFlxSprite.hx b/source/funkin/modding/base/ScriptedFlxSprite.hx new file mode 100644 index 000000000..9bccdc778 --- /dev/null +++ b/source/funkin/modding/base/ScriptedFlxSprite.hx @@ -0,0 +1,10 @@ +package funkin.modding.base; + +import flixel.FlxSprite; +import funkin.modding.IHook; + +@:hscriptClass +class ScriptedFlxSprite extends FlxSprite implements IHook +{ + // No body needed for this class, it's magic ;) +} diff --git a/source/funkin/modding/base/ScriptedFlxSpriteGroup.hx b/source/funkin/modding/base/ScriptedFlxSpriteGroup.hx new file mode 100644 index 000000000..a60dfdad3 --- /dev/null +++ b/source/funkin/modding/base/ScriptedFlxSpriteGroup.hx @@ -0,0 +1,10 @@ +package funkin.modding.base; + +import flixel.group.FlxSpriteGroup; +import funkin.modding.IHook; + +@:hscriptClass +class ScriptedFlxSpriteGroup extends FlxSpriteGroup implements IHook +{ + // No body needed for this class, it's magic ;) +} diff --git a/source/play/Fighter.hx b/source/funkin/play/Fighter.hx similarity index 97% rename from source/play/Fighter.hx rename to source/funkin/play/Fighter.hx index cde3c1f30..fa17b6bd7 100644 --- a/source/play/Fighter.hx +++ b/source/funkin/play/Fighter.hx @@ -1,4 +1,4 @@ -package play; +package funkin.play; import flixel.FlxSprite; diff --git a/source/play/PicoFight.hx b/source/funkin/play/PicoFight.hx similarity index 98% rename from source/play/PicoFight.hx rename to source/funkin/play/PicoFight.hx index 576d8b4f8..1ef8a1fa2 100644 --- a/source/play/PicoFight.hx +++ b/source/funkin/play/PicoFight.hx @@ -1,7 +1,7 @@ -package play; +package funkin.play; -import Note.NoteData; -import audiovis.PolygonSpectogram; +import funkin.Note.NoteData; +import funkin.audiovis.PolygonSpectogram; import flixel.FlxObject; import flixel.FlxSprite; import flixel.addons.effects.FlxTrail; diff --git a/source/PlayState.hx b/source/funkin/play/PlayState.hx similarity index 76% rename from source/PlayState.hx rename to source/funkin/play/PlayState.hx index 345db7912..fa7a482be 100644 --- a/source/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1,22 +1,15 @@ -package; +package funkin.play; -import Note; -import Section.SwagSection; -import SongLoad.SwagSong; -import charting.ChartingState; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; import flixel.FlxSubState; -import flixel.addons.effects.FlxTrail; import flixel.addons.transition.FlxTransitionableState; import flixel.group.FlxGroup; -import flixel.math.FlxAngle; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import flixel.system.FlxSound; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; @@ -24,14 +17,18 @@ import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxSort; import flixel.util.FlxTimer; +import funkin.Note; +import funkin.Section.SwagSection; +import funkin.SongLoad.SwagSong; +import funkin.charting.ChartingState; +import funkin.play.stage.Stage; +import funkin.play.stage.StageData; +import funkin.shaderslmfao.ColorSwap; +import funkin.ui.PopUpStuff; +import funkin.ui.PreferencesMenu; import haxe.Json; import lime.ui.Haptic; import lime.utils.Assets; -import shaderslmfao.BuildingShaders; -import shaderslmfao.ColorSwap; -import shaderslmfao.OverlayBlend; -import ui.PopUpStuff; -import ui.PreferencesMenu; using StringTools; @@ -41,7 +38,8 @@ import Discord.DiscordClient; class PlayState extends MusicBeatState { - public static var curStage:String = ''; + // TODO: Reorganize these variables (maybe there should be a separate class like Conductor just to hold them?) + public static var curStageId:String = ''; public static var SONG:SwagSong; public static var isStoryMode:Bool = false; public static var storyWeek:Int = 0; @@ -51,8 +49,6 @@ class PlayState extends MusicBeatState public static var practiceMode:Bool = false; public static var needsReset:Bool = false; - var halloweenLevel:Bool = false; - private var vocals:VoicesGroup; private var vocalsFinished:Bool = false; @@ -75,6 +71,11 @@ class PlayState extends MusicBeatState private var strumLineNotes:FlxTypedGroup; + /** + * The currently active PlayState. + */ + public static var instance:PlayState = null; + /** * Strumline for player */ @@ -84,7 +85,9 @@ class PlayState extends MusicBeatState private var curSong:String = ""; private var gfSpeed:Int = 1; - private var health:Float = 1; + + public static var health:Float = 1; + private var healthDisplay:Float = 1; private var combo:Int = 0; @@ -103,32 +106,6 @@ class PlayState extends MusicBeatState public static var seenCutscene:Bool = false; - var halloweenBG:FlxSprite; - var isHalloween:Bool = false; - - var phillyCityLights:FlxTypedGroup; - var phillyTrain:FlxSprite; - var trainSound:FlxSound; - - var foregroundSprites:FlxTypedGroup; - - var limo:FlxSprite; - var grpLimoDancers:FlxTypedGroup; - var fastCar:FlxSprite; - - var upperBoppers:FlxSprite; - var bottomBoppers:FlxSprite; - var santa:FlxSprite; - - var bgGirls:BackgroundGirls; - var wiggleShit:WiggleEffect = new WiggleEffect(); - - var tankmanRun:FlxTypedGroup; - var gfCutsceneLayer:FlxGroup; - var bfTankCutsceneLayer:FlxGroup; - var tankWatchtower:BGSprite; - var tankGround:BGSprite; - var talking:Bool = true; var songScore:Int = 0; var scoreTxt:FlxText; @@ -157,14 +134,18 @@ class PlayState extends MusicBeatState #end var camPos:FlxPoint; - var lightFadeShader:BuildingShaders; var comboPopUps:PopUpStuff; override public function create() { + instance = this; + initCameras(); + // Starting health. + health = 1; + persistentUpdate = true; persistentDraw = true; @@ -174,28 +155,24 @@ class PlayState extends MusicBeatState Conductor.mapBPMChanges(SONG); Conductor.changeBPM(SONG.bpm); - foregroundSprites = new FlxTypedGroup(); - // dialogue init shit, just for week 5 really (for now...?) switch (SONG.song.toLowerCase()) { case 'senpai': - dialogue = CoolUtil.coolTextFile(Paths.txt('senpai/senpaiDialogue')); + dialogue = CoolUtil.coolTextFile(Paths.txt('songs/senpai/senpaiDialogue')); case 'roses': - dialogue = CoolUtil.coolTextFile(Paths.txt('roses/rosesDialogue')); + dialogue = CoolUtil.coolTextFile(Paths.txt('songs/roses/rosesDialogue')); case 'thorns': - dialogue = CoolUtil.coolTextFile(Paths.txt('thorns/thornsDialogue')); + dialogue = CoolUtil.coolTextFile(Paths.txt('songs/thorns/thornsDialogue')); } #if discord_rpc initDiscord(); #end - initStageBullshit(); + initStage(); initCharacters(); - add(foregroundSprites); - if (dialogue != null) { doof = new DialogueBox(false, dialogue); @@ -384,316 +361,35 @@ class PlayState extends MusicBeatState FlxG.cameras.add(camHUD, false); } - // a lot of this stage code will be cleaned up more when the stage load shit is more fleshed out! For now it's still a lot of hardcoded shit!! - function initStageBullshit() + function initStage() { + // TODO: Move stageId to the song file. switch (SONG.song.toLowerCase()) { case 'spookeez' | 'monster' | 'south': - curStage = "spooky"; - halloweenLevel = true; - - var hallowTex = Paths.getSparrowAtlas('halloween_bg'); - - halloweenBG = new FlxSprite(-200, -100); - halloweenBG.frames = hallowTex; - halloweenBG.animation.addByPrefix('idle', 'halloweem bg0'); - halloweenBG.animation.addByPrefix('lightning', 'halloweem bg lightning strike', 24, false); - halloweenBG.animation.play('idle'); - halloweenBG.antialiasing = true; - add(halloweenBG); - - isHalloween = true; + curStageId = "spookyMansion"; case 'pico' | 'blammed' | 'philly': - curStage = 'philly'; - - var bg:FlxSprite = new FlxSprite(-100).loadGraphic(Paths.image('philly/sky')); - bg.scrollFactor.set(0.1, 0.1); - add(bg); - - var city:FlxSprite = new FlxSprite(-10).loadGraphic(Paths.image('philly/city')); - city.scrollFactor.set(0.3, 0.3); - city.setGraphicSize(Std.int(city.width * 0.85)); - city.updateHitbox(); - add(city); - - lightFadeShader = new BuildingShaders(); - phillyCityLights = new FlxTypedGroup(); - - add(phillyCityLights); - - for (i in 0...5) - { - var light:FlxSprite = new FlxSprite(city.x).loadGraphic(Paths.image('philly/win' + i)); - light.scrollFactor.set(0.3, 0.3); - light.visible = false; - light.setGraphicSize(Std.int(light.width * 0.85)); - light.updateHitbox(); - light.antialiasing = true; - light.shader = lightFadeShader.shader; - phillyCityLights.add(light); - } - - var streetBehind:FlxSprite = new FlxSprite(-40, 50).loadGraphic(Paths.image('philly/behindTrain')); - add(streetBehind); - - phillyTrain = new FlxSprite(2000, 360).loadGraphic(Paths.image('philly/train')); - add(phillyTrain); - - trainSound = new FlxSound().loadEmbedded(Paths.sound('train_passes')); - FlxG.sound.list.add(trainSound); - - // var cityLights:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.win0.png); - - var street:FlxSprite = new FlxSprite(-40, streetBehind.y).loadGraphic(Paths.image('philly/street')); - add(street); + curStageId = 'phillyTrain'; case "milf" | 'satin-panties' | 'high': - curStage = 'limo'; - defaultCamZoom *= 0.90; - - var skyBG:FlxSprite = new FlxSprite(-120, -50).loadGraphic(Paths.image('limo/limoSunset')); - - var overlayShader:OverlayBlend = new OverlayBlend(); - var sunOverlay:FlxSprite = new FlxSprite().loadGraphic(Paths.image('limo/limoOverlay')); - sunOverlay.setGraphicSize(Std.int(sunOverlay.width * 2)); - sunOverlay.updateHitbox(); - overlayShader.funnyShit.input = sunOverlay.pixels; - skyBG.shader = overlayShader; - - skyBG.scrollFactor.set(0.1, 0.1); - add(skyBG); - - var bgLimo:FlxSprite = new FlxSprite(-200, 480); - bgLimo.frames = Paths.getSparrowAtlas('limo/bgLimo'); - bgLimo.animation.addByPrefix('drive', "background limo pink", 24); - bgLimo.animation.play('drive'); - bgLimo.scrollFactor.set(0.4, 0.4); - add(bgLimo); - - grpLimoDancers = new FlxTypedGroup(); - add(grpLimoDancers); - - for (i in 0...5) - { - var dancer:BackgroundDancer = new BackgroundDancer((370 * i) + 130, bgLimo.y - 400); - dancer.scrollFactor.set(0.4, 0.4); - grpLimoDancers.add(dancer); - } - - var overlayShit:FlxSprite = new FlxSprite(-500, -600).loadGraphic(Paths.image('limo/limoOverlay')); - overlayShit.alpha = 0.5; - // add(overlayShit); - // var shaderBullshit = new BlendModeEffect(new OverlayShader(), FlxColor.RED); - // FlxG.camera.setFilters([new ShaderFilter(cast shaderBullshit.shader)]); - // overlayShit.shader = shaderBullshit; - - limo = new FlxSprite(-120, 550); - limo.frames = Paths.getSparrowAtlas('limo/limoDrive'); - limo.animation.addByPrefix('drive', "Limo stage", 24); - limo.animation.play('drive'); - limo.antialiasing = true; - - fastCar = new FlxSprite(-300, 160).loadGraphic(Paths.image('limo/fastCarLol')); - // add(limo); + curStageId = 'limoRide'; case "cocoa" | 'eggnog': - curStage = 'mall'; - - defaultCamZoom *= 0.80; - - var bg:FlxSprite = new FlxSprite(-1000, -500).loadGraphic(Paths.image('christmas/bgWalls')); - bg.antialiasing = true; - bg.scrollFactor.set(0.2, 0.2); - bg.active = false; - bg.setGraphicSize(Std.int(bg.width * 0.8)); - bg.updateHitbox(); - add(bg); - - upperBoppers = new FlxSprite(-240, -90); - upperBoppers.frames = Paths.getSparrowAtlas('christmas/upperBop'); - upperBoppers.animation.addByPrefix('bop', "Upper Crowd Bob", 24, false); - upperBoppers.antialiasing = true; - upperBoppers.scrollFactor.set(0.33, 0.33); - upperBoppers.setGraphicSize(Std.int(upperBoppers.width * 0.85)); - upperBoppers.updateHitbox(); - add(upperBoppers); - - var bgEscalator:FlxSprite = new FlxSprite(-1100, -600).loadGraphic(Paths.image('christmas/bgEscalator')); - bgEscalator.antialiasing = true; - bgEscalator.scrollFactor.set(0.3, 0.3); - bgEscalator.active = false; - bgEscalator.setGraphicSize(Std.int(bgEscalator.width * 0.9)); - bgEscalator.updateHitbox(); - add(bgEscalator); - - var tree:FlxSprite = new FlxSprite(370, -250).loadGraphic(Paths.image('christmas/christmasTree')); - tree.antialiasing = true; - tree.scrollFactor.set(0.40, 0.40); - add(tree); - - bottomBoppers = new FlxSprite(-300, 140); - bottomBoppers.frames = Paths.getSparrowAtlas('christmas/bottomBop'); - bottomBoppers.animation.addByPrefix('bop', 'Bottom Level Boppers', 24, false); - bottomBoppers.antialiasing = true; - bottomBoppers.scrollFactor.set(0.9, 0.9); - bottomBoppers.setGraphicSize(Std.int(bottomBoppers.width * 1)); - bottomBoppers.updateHitbox(); - add(bottomBoppers); - - var fgSnow:FlxSprite = new FlxSprite(-600, 700).loadGraphic(Paths.image('christmas/fgSnow')); - fgSnow.active = false; - fgSnow.antialiasing = true; - add(fgSnow); - - santa = new FlxSprite(-840, 150); - santa.frames = Paths.getSparrowAtlas('christmas/santa'); - santa.animation.addByPrefix('idle', 'santa idle in fear', 24, false); - santa.antialiasing = true; - add(santa); + curStageId = 'mallXmas'; case 'winter-horrorland': - loadStage('mallEvil'); + curStageId = 'mallEvil'; + case 'pyro': + curStageId = 'pyro'; case 'senpai' | 'roses': - curStage = 'school'; - - // defaultCamZoom *= 0.9; - - var bgSky = new FlxSprite().loadGraphic(Paths.image('weeb/weebSky')); - bgSky.scrollFactor.set(0.1, 0.1); - add(bgSky); - - var repositionShit = -200; - - var bgSchool:FlxSprite = new FlxSprite(repositionShit, 0).loadGraphic(Paths.image('weeb/weebSchool')); - bgSchool.scrollFactor.set(0.6, 0.90); - add(bgSchool); - - var bgStreet:FlxSprite = new FlxSprite(repositionShit).loadGraphic(Paths.image('weeb/weebStreet')); - bgStreet.scrollFactor.set(0.95, 0.95); - add(bgStreet); - - var fgTrees:FlxSprite = new FlxSprite(repositionShit + 170, 130).loadGraphic(Paths.image('weeb/weebTreesBack')); - fgTrees.scrollFactor.set(0.9, 0.9); - add(fgTrees); - - var bgTrees:FlxSprite = new FlxSprite(repositionShit - 380, -800); - var treetex = Paths.getPackerAtlas('weeb/weebTrees'); - bgTrees.frames = treetex; - bgTrees.animation.add('treeLoop', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], 12); - bgTrees.animation.play('treeLoop'); - bgTrees.scrollFactor.set(0.85, 0.85); - add(bgTrees); - - var treeLeaves:FlxSprite = new FlxSprite(repositionShit, -40); - treeLeaves.frames = Paths.getSparrowAtlas('weeb/petals'); - treeLeaves.animation.addByPrefix('leaves', 'PETALS ALL', 24, true); - treeLeaves.animation.play('leaves'); - treeLeaves.scrollFactor.set(0.85, 0.85); - add(treeLeaves); - - var widShit = Std.int(bgSky.width * 6); - - bgSky.setGraphicSize(widShit); - bgSchool.setGraphicSize(widShit); - bgStreet.setGraphicSize(widShit); - bgTrees.setGraphicSize(Std.int(widShit * 1.4)); - fgTrees.setGraphicSize(Std.int(widShit * 0.8)); - treeLeaves.setGraphicSize(widShit); - - fgTrees.updateHitbox(); - bgSky.updateHitbox(); - bgSchool.updateHitbox(); - bgStreet.updateHitbox(); - bgTrees.updateHitbox(); - treeLeaves.updateHitbox(); - - bgGirls = new BackgroundGirls(-100, 190); - bgGirls.scrollFactor.set(0.9, 0.9); - - if (SONG.song.toLowerCase() == 'roses') - { - bgGirls.getScared(); - } - - bgGirls.setGraphicSize(Std.int(bgGirls.width * daPixelZoom)); - bgGirls.updateHitbox(); - add(bgGirls); - case 'thorns': - // loadStage('schoolEvil'); - curStage = 'schoolEvil'; - - var schoolBG:FlxSprite = new FlxSprite(-200, 0).loadGraphic(Paths.image('weeb/evilSchoolBG')); - wiggleShit.waveAmplitude = 0.017; - wiggleShit.waveSpeed = 2; - wiggleShit.waveFrequency = 4; - schoolBG.shader = wiggleShit.shader; - schoolBG.setGraphicSize(Std.int(schoolBG.width * 6)); - schoolBG.updateHitbox(); - // schoolBG.scale.set(6, 6); - add(schoolBG); - - schoolBG.scrollFactor.set(0.7, 1); - - var schoolFront:FlxSprite = new FlxSprite(-250, schoolBG.y + 20).loadGraphic(Paths.image('weeb/evilSchoolFG')); - - schoolFront.shader = wiggleShit.shader; - - schoolFront.setGraphicSize(Std.int(schoolFront.width * 6)); - schoolFront.updateHitbox(); - add(schoolFront); - - case 'guns' | 'stress' | 'ugh': - loadStage('tank'); - - // this goes after tankSky and before tankMountains in stage file - // need to accomodate for the velocity thing! - var tankSky:BGSprite = new BGSprite('tankClouds', FlxG.random.int(-700, -100), FlxG.random.int(-20, 20), 0.1, 0.1); - tankSky.active = true; - tankSky.velocity.x = FlxG.random.float(5, 15); - add(tankSky); - - tankWatchtower = new BGSprite('tankWatchtower', 100, 50, 0.5, 0.5, ['watchtower gradient color']); - add(tankWatchtower); - - tankGround = new BGSprite('tankRolling', 300, 300, 0.5, 0.5, ['BG tank w lighting'], true); - add(tankGround); - // tankGround.active = false; - - tankmanRun = new FlxTypedGroup(); - add(tankmanRun); - - var tankGround:BGSprite = new BGSprite('tankGround', -420, -150); - tankGround.setGraphicSize(Std.int(tankGround.width * 1.15)); - tankGround.updateHitbox(); - add(tankGround); - - moveTank(); - - // smokeLeft.screenCenter(); - - var fgTank0:BGSprite = new BGSprite('tank0', -500, 650, 1.7, 1.5, ['fg']); - foregroundSprites.add(fgTank0); - - var fgTank1:BGSprite = new BGSprite('tank1', -300, 750, 2, 0.2, ['fg']); - foregroundSprites.add(fgTank1); - - // just called 'foreground' just cuz small inconsistency no bbiggei - var fgTank2:BGSprite = new BGSprite('tank2', 450, 940, 1.5, 1.5, ['foreground']); - foregroundSprites.add(fgTank2); - - var fgTank4:BGSprite = new BGSprite('tank4', 1300, 900, 1.5, 1.5, ['fg']); - foregroundSprites.add(fgTank4); - - var fgTank5:BGSprite = new BGSprite('tank5', 1620, 700, 1.5, 1.5, ['fg']); - foregroundSprites.add(fgTank5); - - var fgTank3:BGSprite = new BGSprite('tank3', 1300, 1200, 3.5, 2.5, ['fg']); - foregroundSprites.add(fgTank3); - + curStageId = 'school'; case "darnell": - loadStage('phillyStreets'); - + curStageId = 'phillyStreets'; + case 'thorns': + curStageId = 'schoolEvil'; + case 'guns' | 'stress' | 'ugh': + curStageId = 'tankmanBattlefield'; default: - loadStage('stage'); + curStageId = "mainStage"; } + loadStage(curStageId); } function initCharacters() @@ -701,15 +397,15 @@ class PlayState extends MusicBeatState // all dis is shitty, redo later for stage shit var gfVersion:String = 'gf'; - switch (curStage) + switch (curStageId) { - case 'limo': + case 'limoRide': gfVersion = 'gf-car'; - case 'mall' | 'mallEvil': + case 'mallXmas' | 'mallEvil': gfVersion = 'gf-christmas'; case 'school' | 'schoolEvil': gfVersion = 'gf-pixel'; - case 'tank': + case 'tankmanBattlefield': gfVersion = 'gf-tankmen'; } @@ -729,23 +425,6 @@ class PlayState extends MusicBeatState case 'pico-speaker': gf.x -= 50; gf.y -= 200; - - var tempTankman:TankmenBG = new TankmenBG(20, 500, true); - tempTankman.strumTime = 10; - tempTankman.resetShit(20, 600, true); - tankmanRun.add(tempTankman); - - for (i in 0...TankmenBG.animationNotes.length) - { - if (FlxG.random.bool(16)) - { - var tankman:TankmenBG = tankmanRun.recycle(TankmenBG); - // new TankmenBG(500, 200 + FlxG.random.int(50, 100), TankmenBG.animationNotes[i][1] < 2); - tankman.strumTime = TankmenBG.animationNotes[i][0]; - tankman.resetShit(500, 200 + FlxG.random.int(50, 100), TankmenBG.animationNotes[i][1] < 2); - tankmanRun.add(tankman); - } - } } dad = new Character(100, 100, SONG.player2); @@ -790,37 +469,8 @@ class PlayState extends MusicBeatState boyfriend = new Boyfriend(770, 450, SONG.player1); // REPOSITIONING PER STAGE - switch (curStage) + switch (curStageId) { - case 'limo': - boyfriend.y -= 220; - boyfriend.x += 260; - - resetFastCar(); - add(fastCar); - case 'mall': - boyfriend.x += 200; - case 'mallEvil': - boyfriend.x += 320; - dad.y -= 80; - case 'school': - boyfriend.x += 200; - boyfriend.y += 220; - gf.x += 180; - gf.y += 300; - case 'schoolEvil': - // trailArea.scrollFactor.set(); - - var evilTrail = new FlxTrail(dad, null, 4, 24, 0.3, 0.069); - // evilTrail.changeValuesEnabled(false, false, false, false); - // evilTrail.changeGraphic() - add(evilTrail); - // evilTrail.scrollFactor.set(1.1, 1.1); - - boyfriend.x += 200; - boyfriend.y += 220; - gf.x += 180; - gf.y += 300; case "tank": gf.y += 10; gf.x -= 30; @@ -834,24 +484,25 @@ class PlayState extends MusicBeatState gf.x -= 170; gf.y -= 75; } - case 'stage' | 'phillyStreets': - dad.y = 870 - dad.height; } - add(gf); + if (curStage != null) + { + // We're using Eric's stage handler. + // Characters get added to the stage, not the main scene. + curStage.addCharacter(gf, GF); + curStage.addCharacter(boyfriend, BF); + curStage.addCharacter(dad, DAD); - gfCutsceneLayer = new FlxGroup(); - add(gfCutsceneLayer); - - bfTankCutsceneLayer = new FlxGroup(); - add(bfTankCutsceneLayer); - - // Shitty layering but whatev it works LOL - if (curStage == 'limo') - add(limo); - - add(dad); - add(boyfriend); + // Redo z-indexes. + curStage.refresh(); + } + else + { + add(gf); + add(dad); + add(boyfriend); + } } function ughIntro() @@ -862,6 +513,7 @@ class PlayState extends MusicBeatState blackShit.scrollFactor.set(); add(blackShit); + #if html5 var vid:FlxVideo = new FlxVideo('music/ughCutscene.mp4'); vid.finishCallback = function() { @@ -870,6 +522,12 @@ class PlayState extends MusicBeatState startCountdown(); cameraMovement(); }; + #else + remove(blackShit); + FlxTween.tween(FlxG.camera, {zoom: defaultCamZoom}, (Conductor.crochet / 1000) * 5, {ease: FlxEase.quadInOut}); + startCountdown(); + cameraMovement(); + #end FlxG.camera.zoom = defaultCamZoom * 1.2; @@ -940,43 +598,59 @@ class PlayState extends MusicBeatState });*/ } - function loadStage(path:String) + /** + * Removes any references to the current stage, then clears the stage cache, + * then reloads all the stages. + * + * This is useful for when you want to edit a stage without reloading the whole game. + * Reloading works on both the JSON and the HXC, if applicable. + * + * Call this by pressing F5 on a debug build. + */ + function debug_refreshStages() { - curStage = path; - - var json = Assets.getText(Paths.file('data/stagedata/' + curStage + 'Stage.json')); - - var parsed:StageData = cast Json.parse(json); - - defaultCamZoom *= parsed.camZoom; - - for (prop in parsed.propsBackground) + // Remove the current stage. If the stage gets deleted while it's still in use, + // it'll probably crash the game or something. + if (this.curStage != null) { - var funnyProp:BGSprite = new BGSprite(prop.path, prop.x, prop.y, prop.scrollX, prop.scrollY, null, false, false); - - if (prop.animBullshit != null) - funnyProp.setupSparrow(prop.path, prop.animBullshit.anims, prop.animBullshit.isLooping); - else - funnyProp.justLoadImage(prop.path); - - if (prop.updateHitbox != null && !prop.updateHitbox) - { - funnyProp.scale.set(prop.scaleX, prop.scaleY); - } - else - { - funnyProp.setGraphicSize(Std.int(funnyProp.width * prop.scaleX), Std.int(funnyProp.height * prop.scaleY)); - funnyProp.updateHitbox(); - } - - if (prop.antialiasing != null) - funnyProp.antialiasing = prop.antialiasing; - - funnyProp.scrollFactor.set(prop.scrollX, prop.scrollY); - add(funnyProp); + remove(curStage); + curStage.kill(); + curStage = null; } - trace(parsed.propsBackground); + // 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(); + + // Reload the level. This should use new data from the assets folder. + LoadingState.loadAndSwitchState(new PlayState()); + } + + public var curStage:Stage; + + /** + * Loads stage data from cache, assembles the props, + * and adds it to the state. + * @param id + */ + function loadStage(id:String) + { + curStage = StageDataParser.fetchStage(id); + + if (curStage != null) + { + // Actually create and position the sprites. + curStage.buildStage(); + + // Apply camera zoom. + defaultCamZoom *= curStage.camZoom; + + // Add the stage to the scene. + this.add(curStage); + } } function gunsIntro() @@ -987,6 +661,7 @@ class PlayState extends MusicBeatState blackShit.scrollFactor.set(); add(blackShit); + #if html5 var vid:FlxVideo = new FlxVideo('music/gunsCutscene.mp4'); vid.finishCallback = function() { @@ -996,6 +671,13 @@ class PlayState extends MusicBeatState startCountdown(); cameraMovement(); }; + #else + remove(blackShit); + + FlxTween.tween(FlxG.camera, {zoom: defaultCamZoom}, (Conductor.crochet / 1000) * 5, {ease: FlxEase.quadInOut}); + startCountdown(); + cameraMovement(); + #end /* camFollow.setPosition(camPos.x, camPos.y); @@ -1061,6 +743,7 @@ class PlayState extends MusicBeatState blackShit.scrollFactor.set(); add(blackShit); + #if html5 var vid:FlxVideo = new FlxVideo('music/stressCutscene.mp4'); vid.finishCallback = function() { @@ -1070,6 +753,13 @@ class PlayState extends MusicBeatState startCountdown(); cameraMovement(); }; + #else + remove(blackShit); + + FlxTween.tween(FlxG.camera, {zoom: defaultCamZoom}, (Conductor.crochet / 1000) * 5, {ease: FlxEase.quadInOut}); + startCountdown(); + cameraMovement(); + #end /* camHUD.visible = false; @@ -1451,10 +1141,17 @@ class PlayState extends MusicBeatState gf.dance(); if (swagCounter % 2 == 0) { - if (!boyfriend.animation.curAnim.name.startsWith("sing")) - boyfriend.playAnim('idle'); - if (!dad.animation.curAnim.name.startsWith("sing")) - dad.dance(); + if (boyfriend.animation != null) + { + if (!boyfriend.animation.curAnim.name.startsWith("sing")) + boyfriend.playAnim('idle'); + } + + if (dad.animation != null) + { + if (!dad.animation.curAnim.name.startsWith("sing")) + dad.dance(); + } } else if (dad.curCharacter == 'spooky' && !dad.animation.curAnim.name.startsWith("sing")) dad.dance(); @@ -1464,7 +1161,7 @@ class PlayState extends MusicBeatState var introSprPaths:Array = ["ready", "set", "go"]; var altSuffix:String = ""; - if (curStage.startsWith("school")) + if (curStageId.startsWith("school")) { altSuffix = '-pixel'; introSprPaths = ['weeb/pixelUI/ready-pixel', 'weeb/pixelUI/set-pixel', 'weeb/pixelUI/date-pixel']; @@ -1500,7 +1197,7 @@ class PlayState extends MusicBeatState var spr:FlxSprite = new FlxSprite().loadGraphic(Paths.image(path)); spr.scrollFactor.set(); - if (curStage.startsWith('school')) + if (curStageId.startsWith('school')) spr.setGraphicSize(Std.int(spr.width * daPixelZoom)); spr.updateHitbox(); @@ -1603,7 +1300,7 @@ class PlayState extends MusicBeatState var gottaHitNote:Bool = section.mustHitSection; - if (songNotes.highStakes) + if (songNotes.highStakes) // noteData > 3 gottaHitNote = !section.mustHitSection; var oldNote:Note; @@ -1613,8 +1310,10 @@ class PlayState extends MusicBeatState oldNote = null; var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote); + // swagNote.data = songNotes; swagNote.data.sustainLength = songNotes.sustainLength; swagNote.data.altNote = songNotes.altNote; + swagNote.scrollFactor.set(0, 0); var susLength:Float = swagNote.data.sustainLength; @@ -1673,7 +1372,7 @@ class PlayState extends MusicBeatState babyArrow.shader = colorswap.shader; colorswap.update(Note.arrowColors[i]); - switch (curStage) + switch (curStageId) { case 'school' | 'schoolEvil': babyArrow.loadGraphic(Paths.image('weeb/pixelUI/arrows-pixels'), true, 17, 17); @@ -1872,9 +1571,13 @@ class PlayState extends MusicBeatState FlxG.sound.music.pause(); vocals.pause(); + curStage.resetStage(); + FlxG.sound.music.time = 0; + regenNoteData(); // loads the note data from start health = 1; + songScore = 0; restartCountdownTimer(); needsReset = false; @@ -1943,31 +1646,8 @@ class PlayState extends MusicBeatState // Conductor.lastSongPos = FlxG.sound.music.time; } - switch (curStage) - { - case 'philly': - if (trainMoving) - { - trainFrameTiming += elapsed; - - if (trainFrameTiming >= 1 / 24) - { - updateTrainPos(); - trainFrameTiming = 0; - } - } - - lightFadeShader.update((Conductor.crochet / 1000) * FlxG.elapsed * 1.5); - // phillyCityLights.members[curLight].alpha -= (Conductor.crochet / 1000) * FlxG.elapsed; - - case 'tank': - moveTank(); - } - super.update(elapsed); // idk if there's a particular reason why some code is before super.update(), and some is after. Prob nothing too much to worry about. - wiggleShit.update(elapsed); - var androidPause:Bool = false; #if android @@ -1980,11 +1660,11 @@ class PlayState extends MusicBeatState persistentDraw = true; paused = true; - // 1 / 1000 chance for Gitaroo Man easter egg - // can this please move to dying it's kinda fucked up that pausing has a 1/1000 chance ur forced to restart - if (FlxG.random.bool(0.1)) + // There is a 1/1000 change to use a special pause menu. + // 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. + if (FlxG.random.bool(1 / 1000)) { - // gitaroo man easter egg FlxG.switchState(new GitarooPause()); } else @@ -2014,7 +1694,10 @@ class PlayState extends MusicBeatState scoreTxt.text = "Score:" + songScore; if (FlxG.keys.justPressed.EIGHT) - FlxG.switchState(new ui.animDebugShit.DebugBoundingState()); + FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState()); + + if (FlxG.keys.justPressed.F5) + debug_refreshStages(); if (FlxG.keys.justPressed.NINE) iconP1.swapOldIcon(); @@ -2243,9 +1926,16 @@ class PlayState extends MusicBeatState } else if (daNote.tooLate || daNote.wasGoodHit) { + // TODO: Why the hell is the noteMiss logic in two different places? if (daNote.tooLate) { - health -= 0.0775; + if (curStage != null) + { + curStage.onNoteMiss(daNote); + } + + // lose less health on sustain notes! + health -= 0.0775 * (daNote.isSustainNote ? 0.2 : 1); // if it's sustain, multiply it by 0.2 (not checked for balence yet), else keep it same (multiply by 1) vocals.volume = 0; killCombo(); } @@ -2262,6 +1952,12 @@ class PlayState extends MusicBeatState if (!inCutscene) keyShit(); + + if (curStage != null) + { + // We're using Eric's stage handler. + curStage.onUpdate(elapsed); + } } function applyClipRect(daNote:Note):Void @@ -2451,6 +2147,12 @@ class PlayState extends MusicBeatState health += healthMulti; + // TODO: Redo note hit logic to make sure this always gets called + if (curStage != null) + { + curStage.onNoteHit(daNote); + } + if (isSick) { var noteSplash:NoteSplash = grpNoteSplashes.recycle(NoteSplash); @@ -2498,11 +2200,11 @@ class PlayState extends MusicBeatState { camFollow.setPosition(boyfriend.getMidpoint().x - 100, boyfriend.getMidpoint().y - 100); - switch (curStage) + switch (curStageId) { case 'limo': camFollow.x = boyfriend.getMidpoint().x - 300; - case 'mall': + case 'mallXmas': camFollow.y = boyfriend.getMidpoint().y - 200; case 'school' | 'schoolEvil': camFollow.x = boyfriend.getMidpoint().x - 200; @@ -2684,7 +2386,7 @@ class PlayState extends MusicBeatState if (!holdArray[spr.ID]) spr.animation.play('static'); - if (spr.animation.curAnim.name == 'confirm' && !curStage.startsWith('school')) + if (spr.animation.curAnim.name == 'confirm' && !curStageId.startsWith('school')) { spr.centerOffsets(); spr.offset.x -= 13; @@ -2695,11 +2397,31 @@ class PlayState extends MusicBeatState }); } - override function switchTo(nextState:FlxState):Bool + function performCleanup() { + // Uncache the song. openfl.utils.Assets.cache.clear(Paths.inst(SONG.song)); openfl.utils.Assets.cache.clear(Paths.voices(SONG.song)); + // Remove reference to stage and remove sprites from it to save memory. + if (curStage != null) + { + remove(curStage); + curStage.kill(); + curStage = null; + } + + // Clear the static reference to this state. + instance = null; + } + + /** + * This function is called before switching to a new FlxState. + */ + override function switchTo(nextState:FlxState):Bool + { + performCleanup(); + return super.switchTo(nextState); } @@ -2786,112 +2508,6 @@ class PlayState extends MusicBeatState FlxG.camera.focusOn(camFollow.getPosition()); } - var fastCarCanDrive:Bool = true; - - function resetFastCar():Void - { - fastCar.x = -12600; - fastCar.y = FlxG.random.int(140, 250); - fastCar.velocity.x = 0; - fastCarCanDrive = true; - } - - function fastCarDrive() - { - FlxG.sound.play(Paths.soundRandom('carPass', 0, 1), 0.7); - - fastCar.velocity.x = (FlxG.random.int(170, 220) / FlxG.elapsed) * 3; - fastCarCanDrive = false; - new FlxTimer().start(2, function(tmr:FlxTimer) - { - resetFastCar(); - }); - } - - function moveTank():Void - { - if (!inCutscene) - { - var daAngleOffset:Float = 1; - tankAngle += FlxG.elapsed * tankSpeed; - tankGround.angle = tankAngle - 90 + 15; - - tankGround.x = tankX + Math.cos(FlxAngle.asRadians((tankAngle * daAngleOffset) + 180)) * 1500; - tankGround.y = 1300 + Math.sin(FlxAngle.asRadians((tankAngle * daAngleOffset) + 180)) * 1100; - } - } - - var tankResetShit:Bool = false; - var tankMoving:Bool = false; - var tankAngle:Float = FlxG.random.int(-90, 45); - var tankSpeed:Float = FlxG.random.float(5, 7); - var tankX:Float = 400; - - var trainMoving:Bool = false; - var trainFrameTiming:Float = 0; - - var trainCars:Int = 8; - var trainFinishing:Bool = false; - var trainCooldown:Int = 0; - - function trainStart():Void - { - trainMoving = true; - trainSound.play(true); - } - - var startedMoving:Bool = false; - - function updateTrainPos():Void - { - if (trainSound.time >= 4700) - { - startedMoving = true; - gf.playAnim('hairBlow'); - } - - if (startedMoving) - { - phillyTrain.x -= 400; - - if (phillyTrain.x < -2000 && !trainFinishing) - { - phillyTrain.x = -1150; - trainCars -= 1; - - if (trainCars <= 0) - trainFinishing = true; - } - - if (phillyTrain.x < -4000 && trainFinishing) - trainReset(); - } - } - - function trainReset():Void - { - gf.playAnim('hairFall'); - phillyTrain.x = FlxG.width + 200; - trainMoving = false; - // trainSound.stop(); - // trainSound.time = 0; - trainCars = 8; - trainFinishing = false; - startedMoving = false; - } - - function lightningStrikeShit():Void - { - FlxG.sound.play(Paths.soundRandom('thunder_', 1, 2)); - halloweenBG.animation.play('lightning'); - - lightningStrikeBeat = curBeat; - lightningOffset = FlxG.random.int(8, 24); - - boyfriend.playAnim('scared', true); - gf.playAnim('scared', true); - } - override function stepHit() { super.stepHit(); @@ -2901,10 +2517,13 @@ class PlayState extends MusicBeatState { resyncVocals(); } - } - var lightningStrikeBeat:Int = 0; - var lightningOffset:Int = 8; + if (curStage != null) + { + // We're using Eric's stage handler. The stage should know that a beat has been hit. + curStage.onStepHit(curBeat); + } + } override function beatHit() { @@ -2975,9 +2594,9 @@ class PlayState extends MusicBeatState if (curBeat % 2 == 0) { - if (!boyfriend.animation.curAnim.name.startsWith("sing")) + if (boyfriend.animation != null && !boyfriend.animation.curAnim.name.startsWith("sing")) boyfriend.playAnim('idle'); - if (!dad.animation.curAnim.name.startsWith("sing")) + if (dad.animation != null && !dad.animation.curAnim.name.startsWith("sing")) dad.dance(); } else if (dad.curCharacter == 'spooky') @@ -2997,61 +2616,10 @@ class PlayState extends MusicBeatState dad.playAnim('cheer', true); } - foregroundSprites.forEach(function(spr:BGSprite) + if (curStage != null) { - spr.dance(); - }); - - // boppin friends - switch (curStage) - { - case 'school': - bgGirls.dance(); - - case 'mall': - upperBoppers.animation.play('bop', true); - bottomBoppers.animation.play('bop', true); - santa.animation.play('idle', true); - - case 'limo': - grpLimoDancers.forEach(function(dancer:BackgroundDancer) - { - dancer.dance(); - }); - - if (FlxG.random.bool(10) && fastCarCanDrive) - fastCarDrive(); - case "philly": - if (!trainMoving) - trainCooldown += 1; - - if (curBeat % 4 == 0) - { - lightFadeShader.reset(); - - phillyCityLights.forEach(function(light:FlxSprite) - { - light.visible = false; - }); - - curLight = FlxG.random.int(0, phillyCityLights.length - 1); - - phillyCityLights.members[curLight].visible = true; - // phillyCityLights.members[curLight].alpha = 1; - } - - if (curBeat % 8 == 4 && FlxG.random.bool(30) && !trainMoving && trainCooldown > 8) - { - trainCooldown = FlxG.random.int(-4, 0); - trainStart(); - } - case 'tank': - tankWatchtower.dance(); - } - - if (isHalloween && FlxG.random.bool(10) && curBeat > lightningStrikeBeat + lightningOffset) - { - lightningStrikeShit(); + // We're using Eric's stage handler. The stage should know that a beat has been hit. + curStage.onBeatHit(curBeat); } } diff --git a/source/funkin/play/character/Character.hx b/source/funkin/play/character/Character.hx new file mode 100644 index 000000000..c5ae1014a --- /dev/null +++ b/source/funkin/play/character/Character.hx @@ -0,0 +1,9 @@ +package funkin.play.character; + +enum CharacterType +{ + BF; + GF; + DAD; + OTHER; +} diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx new file mode 100644 index 000000000..3105847b2 --- /dev/null +++ b/source/funkin/play/stage/Bopper.hx @@ -0,0 +1,119 @@ +package funkin.play.stage; + +import flixel.FlxSprite; + +/** + * A Bopper is a stage prop which plays a dance animation. + * Y'know, a thingie that bops. A bopper. + */ +class Bopper extends FlxSprite +{ + /** + * The bopper plays the dance animation once every `danceEvery` beats. + */ + public var danceEvery:Int = 1; + + /** + * Whether the bopper should dance left and right. + * - If true, alternate playing `danceLeft` and `danceRight`. + * - If false, play `idle` every time. + * + * You can manually set this value, or you can leave it as `null` to determine it automatically. + */ + public var shouldAlternate:Null = null; + + /** + * Set this value to define an additional horizontal offset to this sprite's position. + */ + public var xOffset:Float = 0; + + override function set_x(value:Float):Float + { + this.x = value + this.xOffset; + return value; + } + + public var idleSuffix(default, set):String = ""; + + function set_idleSuffix(value:String):String + { + this.idleSuffix = value; + this.dance(); + return value; + } + + /** + * Set this value to define an additional vertical offset to this sprite's position. + */ + public var yOffset:Float = 0; + + override function set_y(value:Float):Float + { + this.y = value + this.yOffset; + return value; + } + + /** + * Whether to play `danceRight` next iteration. + * Only used when `shouldAlternate` is true. + */ + var hasDanced:Bool = false; + + public function new(danceEvery:Int = 1) + { + super(); + this.danceEvery = danceEvery; + } + + function update_shouldAlternate():Void + { + if (this.animation.getByName('danceLeft') != null) + { + this.shouldAlternate = true; + } + } + + /** + * Called once every beat of the song. + */ + public function onBeatHit(curBeat:Int):Void + { + if (curBeat % danceEvery == 0) + { + dance(); + } + } + + /** + * Called every `danceEvery` beats of the song. + */ + public function dance():Void + { + if (this.animation == null) + { + return; + } + + if (shouldAlternate == null) + { + update_shouldAlternate(); + } + + if (shouldAlternate) + { + if (hasDanced) + { + this.animation.play('danceRight$idleSuffix'); + } + else + { + this.animation.play('danceLeft$idleSuffix'); + } + hasDanced = !hasDanced; + } + else + { + this.animation.play('idle$idleSuffix'); + } + } +} diff --git a/source/funkin/play/stage/ScriptedBopper.hx b/source/funkin/play/stage/ScriptedBopper.hx new file mode 100644 index 000000000..a344b0428 --- /dev/null +++ b/source/funkin/play/stage/ScriptedBopper.hx @@ -0,0 +1,10 @@ +package funkin.play.stage; + +import funkin.modding.IHook; + +@:hscriptClass +@:keep +class ScriptedBopper extends Bopper implements IHook +{ + // No body needed for this class, it's magic ;) +} diff --git a/source/funkin/play/stage/ScriptedStage.hx b/source/funkin/play/stage/ScriptedStage.hx new file mode 100644 index 000000000..114afb1d5 --- /dev/null +++ b/source/funkin/play/stage/ScriptedStage.hx @@ -0,0 +1,9 @@ +package funkin.play.stage; + +import funkin.modding.IHook; + +@:hscriptClass +class ScriptedStage extends Stage implements IHook +{ + // No body needed for this class, it's magic ;) +} diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx new file mode 100644 index 000000000..502e8ed76 --- /dev/null +++ b/source/funkin/play/stage/Stage.hx @@ -0,0 +1,402 @@ +package funkin.play.stage; + +import flixel.FlxSprite; +import flixel.group.FlxSpriteGroup; +import flixel.math.FlxPoint; +import flixel.util.FlxSort; +import funkin.modding.IHook; +import funkin.play.character.Character.CharacterType; +import funkin.play.stage.StageData.StageDataParser; +import funkin.util.SortUtil; + +/** + * A Stage is a group of objects rendered in the PlayState. + * + * A Stage is comprised of one or more props, each of which is a FlxSprite. + */ +class Stage extends FlxSpriteGroup implements IHook +{ + public final stageId:String; + public final stageName:String; + + final _data:StageData; + + public var camZoom:Float = 1.0; + + var namedProps:Map = new Map(); + var characters:Map = new Map(); + var boppers:Array = new Array(); + + /** + * The Stage elements get initialized at the beginning of the game. + * They're used to cache the data needed to build the stage, + * then accessed and fleshed out when the stage needs to be built. + * + * @param stageId + */ + public function new(stageId:String) + { + super(); + + this.stageId = stageId; + _data = StageDataParser.parseStageData(this.stageId); + if (_data == null) + { + throw 'Could not find stage data for stageId: $stageId'; + } + else + { + this.stageName = _data.name; + } + } + + /** + * The default stage construction routine. Called when the stage is going to be played in. + * Instantiates each prop and adds it to the stage, while setting its parameters. + */ + public function buildStage() + { + trace('Building stage for display: ${this.stageId}'); + + this.camZoom = _data.cameraZoom; + // this.scrollFactor = new FlxPoint(1, 1); + + for (dataProp in _data.props) + { + trace(' Placing prop: ${dataProp.name} (${dataProp.assetPath})'); + + var isAnimated = dataProp.animations.length > 0; + + var propSprite:FlxSprite; + if (dataProp.danceEvery != 0) + { + propSprite = new Bopper(dataProp.danceEvery); + } + else + { + propSprite = new FlxSprite(); + } + + if (isAnimated) + { + // Initalize sprite frames. + switch (dataProp.animType) + { + case "packer": + propSprite.frames = Paths.getPackerAtlas(dataProp.assetPath); + default: // "sparrow" + propSprite.frames = Paths.getSparrowAtlas(dataProp.assetPath); + } + } + else + { + // Initalize static sprite. + propSprite.loadGraphic(Paths.image(dataProp.assetPath)); + + // Disables calls to update() for a performance boost. + propSprite.active = false; + } + + if (propSprite.frames == null || propSprite.frames.numFrames == 0) + { + trace(' ERROR: Could not build texture for prop.'); + continue; + } + + if (Std.isOfType(dataProp.scale, Array)) + { + propSprite.scale.set(dataProp.scale[0], dataProp.scale[1]); + } + else + { + propSprite.scale.set(dataProp.scale); + } + propSprite.updateHitbox(); + + propSprite.x = dataProp.position[0]; + propSprite.y = dataProp.position[1]; + + // If pixel, disable antialiasing. + propSprite.antialiasing = !dataProp.isPixel; + + propSprite.scrollFactor.x = dataProp.scroll[0]; + propSprite.scrollFactor.y = dataProp.scroll[1]; + + propSprite.zIndex = dataProp.zIndex; + + switch (dataProp.animType) + { + case "packer": + for (propAnim in dataProp.animations) + { + propSprite.animation.add(propAnim.name, propAnim.frameIndices); + } + default: // "sparrow" + for (propAnim in dataProp.animations) + { + if (propAnim.frameIndices.length == 0) + { + 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); + } + } + } + + if (dataProp.startingAnimation != null) + { + propSprite.animation.play(dataProp.startingAnimation); + } + + if (Std.isOfType(propSprite, Bopper)) + { + addBopper(cast propSprite, dataProp.name); + } + else + { + addProp(propSprite, dataProp.name); + } + trace(' Prop placed.'); + } + + this.refresh(); + } + + /** + * Add a sprite to the stage. + * @param prop The sprite to add. + * @param name (Optional) A unique name for the sprite. + * You can call `getNamedProp(name)` to retrieve it later. + */ + public function addProp(prop:FlxSprite, ?name:String = null) + { + if (name != null) + { + namedProps.set(name, prop); + } + this.add(prop); + } + + /** + * Add a sprite to the stage which animates to the beat of the song. + */ + public function addBopper(bopper:Bopper, ?name:String = null) + { + boppers.push(bopper); + this.addProp(bopper, name); + } + + /** + * Refreshes the stage, by redoing the render order of all props. + * It does this based on the `zIndex` of each prop. + */ + public function refresh() + { + sort(SortUtil.byZIndex, FlxSort.ASCENDING); + trace('Stage sorted by z-index'); + } + + /** + * Resets the stage and it's props (needs to be overridden with your own logic!) + */ + public function resetStage() + { + // Override me in your script to reset stage shit however you please! + // also note: maybe add some default behaviour to reset stage stuff? + } + + /** + * A function that should get called every frame. + */ + public function onUpdate(elapsed:Float):Void + { + // Override me in your scripted stage to perform custom behavior! + } + + /** + * A function that gets called when the player hits a note. + */ + public function onNoteHit(note:Note):Void + { + // Override me in your scripted stage to perform custom behavior! + } + + /** + * A function that gets called when the player hits a note. + */ + public function onNoteMiss(note:Note):Void + { + // Override me in your scripted stage to perform custom behavior! + } + + /** + * Adjusts the position and other properties of the soon-to-be child of this sprite group. + * Private helper to avoid duplicate code in `add()` and `insert()`. + * + * @param Sprite The sprite or sprite group that is about to be added or inserted into the group. + */ + override function preAdd(Sprite:FlxSprite):Void + { + var sprite:FlxSprite = cast Sprite; + sprite.x += x; + sprite.y += y; + sprite.alpha *= alpha; + // Don't override scroll factors. + // sprite.scrollFactor.copyFrom(scrollFactor); + sprite.cameras = _cameras; // _cameras instead of cameras because get_cameras() will not return null + + if (clipRect != null) + clipRectTransform(sprite, clipRect); + } + + /** + * A function that gets called once per step in the song. + * @param curStep The current step number. + */ + public function onStepHit(curStep:Int):Void + { + // Override me in your scripted stage to perform custom behavior! + } + + /** + * A function that gets called once per beat in the song (once every four steps). + * @param curStep The current beat number. + */ + public function onBeatHit(curBeat:Int):Void + { + // Override me in your scripted stage to perform custom behavior! + // Make sure to call super.onBeatHit(curBeat) if you want to keep the boppers dancing. + + for (bopper in boppers) + { + bopper.onBeatHit(curBeat); + } + } + + /** + * Used by the PlayState to add a character to the stage. + */ + public function addCharacter(character:Character, charType:CharacterType) + { + // Apply position and z-index. + switch (charType) + { + case BF: + this.characters.set("bf", character); + character.zIndex = _data.characters.bf.zIndex; + character.x = _data.characters.bf.position[0]; + character.y = _data.characters.bf.position[1]; + case GF: + this.characters.set("gf", character); + character.zIndex = _data.characters.gf.zIndex; + character.x = _data.characters.gf.position[0]; + character.y = _data.characters.gf.position[1]; + case DAD: + this.characters.set("dad", character); + character.zIndex = _data.characters.dad.zIndex; + character.x = _data.characters.dad.position[0]; + character.y = _data.characters.dad.position[1]; + default: + this.characters.set(character.curCharacter, character); + } + + // Add the character to the scene. + this.add(character); + } + + /** + * Retrieves a given character from the stage. + */ + public function getCharacter(id:String):Character + { + return this.characters.get(id); + } + + public function getBoyfriend():Character + { + return getCharacter('bf'); + } + + public function getGirlfriend():Character + { + return getCharacter('gf'); + } + + public function getDad():Character + { + return getCharacter('dad'); + } + + /** + * Retrieve a specific prop by the name assigned in the JSON file. + * @param name The name of the prop to retrieve. + * @return The corresponding FlxSprite. + */ + public function getNamedProp(name:String):FlxSprite + { + return this.namedProps.get(name); + } + + /** + * Retrieve a list of all the asset paths required to load the stage. + * Override this in a scripted class to ensure that all necessary assets are loaded! + * + * @return An array of file names. + */ + public function fetchAssetPaths():Array + { + var result:Array = []; + for (dataProp in _data.props) + { + result.push(Paths.image(dataProp.assetPath)); + } + return result; + } + + /** + * Perform cleanup for when you are leaving the level. + */ + public override function kill() + { + super.kill(); + + for (prop in this.namedProps) + { + prop.destroy(); + } + namedProps.clear(); + + for (char in this.characters) + { + char.destroy(); + } + characters.clear(); + + for (bopper in boppers) + { + bopper.destroy(); + } + boppers = []; + + for (sprite in this.group) + { + sprite.destroy(); + } + group.clear(); + } + + /** + * Perform cleanup for when you are destroying the stage + * and removing all its data from cache. + * + * Call this ONLY when you are performing a hard cache clear. + */ + public override function destroy() + { + super.destroy(); + } +} diff --git a/source/funkin/play/stage/StageData.hx b/source/funkin/play/stage/StageData.hx new file mode 100644 index 000000000..2e9058a87 --- /dev/null +++ b/source/funkin/play/stage/StageData.hx @@ -0,0 +1,509 @@ +package funkin.play.stage; + +import openfl.Assets; +import funkin.util.assets.DataAssets; +import haxe.Json; +import flixel.util.typeLimit.OneOfTwo; + +using StringTools; + +/** + * Contains utilities for loading and parsing stage data. + */ +class StageDataParser +{ + /** + * The current version string for the stage data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateStageData()` function. + */ + public static final STAGE_DATA_VERSION:String = "1.0"; + + static final stageCache:Map = new Map(); + + static final DEFAULT_STAGE_ID = 'UNKNOWN'; + + /** + * Parses and preloads the game's stage data and scripts when the game starts. + * + * If you want to force stages to be reloaded, you can just call this function again. + */ + public static function loadStageCache():Void + { + // Clear any stages that are cached if there were any. + clearStageCache(); + trace("[STAGEDATA] Loading stage cache..."); + + // + // SCRIPTED STAGES + // + var scriptedStageClassNames:Array = ScriptedStage.listScriptClasses(); + trace(' Instantiating ${scriptedStageClassNames.length} scripted stages...'); + for (stageCls in scriptedStageClassNames) + { + var stage:Stage = ScriptedStage.init(stageCls, DEFAULT_STAGE_ID); + if (stage != null) + { + trace(' Loaded scripted stage: ${stage.stageName}'); + // Disable the rendering logic for stage until it's loaded. + // Note that kill() =/= destroy() + stage.kill(); + + // Then store it. + stageCache.set(stage.stageId, stage); + } + else + { + trace(' Failed to instantiate scripted stage class: ${stageCls}'); + } + } + + // + // UNSCRIPTED STAGES + // + var stageIdList:Array = DataAssets.listDataFilesInPath('stages/'); + var unscriptedStageIds:Array = stageIdList.filter(function(stageId:String):Bool + { + return !stageCache.exists(stageId); + }); + trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...'); + for (stageId in unscriptedStageIds) + { + var stage:Stage; + try + { + stage = new Stage(stageId); + if (stage != null) + { + trace(' Loaded stage data: ${stage.stageName}'); + stageCache.set(stageId, stage); + } + } + catch (e) + { + // Assume error was already logged. + continue; + } + } + + trace(' Successfully loaded ${Lambda.count(stageCache)} stages.'); + } + + public static function fetchStage(stageId:String):Null + { + if (stageCache.exists(stageId)) + { + trace('[STAGEDATA] Successfully fetch stage: ${stageId}'); + var stage:Stage = stageCache.get(stageId); + stage.revive(); + return stage; + } + else + { + trace('[STAGEDATA] Failed to fetch stage, not found in cache: ${stageId}'); + return null; + } + } + + static function clearStageCache():Void + { + if (stageCache != null) + { + for (stage in stageCache) + { + stage.destroy(); + } + stageCache.clear(); + } + } + + /** + * Load a stage's JSON file, parse its data, and return it. + * + * @param stageId The stage to load. + * @return The stage data, or null if validation failed. + */ + public static function parseStageData(stageId:String):Null + { + var rawJson:String = loadStageFile(stageId); + + var stageData:StageData = migrateStageData(rawJson, stageId); + + return validateStageData(stageId, stageData); + } + + static function loadStageFile(stagePath:String):String + { + var stageFilePath:String = Paths.json('stages/${stagePath}'); + var rawJson = Assets.getText(stageFilePath).trim(); + + while (!rawJson.endsWith("}")) + { + rawJson = rawJson.substr(0, rawJson.length - 1); + } + + return rawJson; + } + + static function migrateStageData(rawJson:String, stageId:String) + { + // If you update the stage data format in a breaking way, + // handle migration here by checking the `version` value. + + try + { + var stageData:StageData = cast Json.parse(rawJson); + return stageData; + } + catch (e) + { + trace(' Error parsing data for stage: ${stageId}'); + trace(' ${e}'); + return null; + } + } + + 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 = [0, 0]; + static final DEFAULT_SCROLL:Array = [0, 0]; + static final DEFAULT_FRAMEINDICES:Array = []; + static final DEFAULT_ANIMTYPE:String = "sparrow"; + + static final DEFAULT_CHARACTER_DATA:StageDataCharacter = { + zIndex: DEFAULT_ZINDEX, + position: DEFAULT_POSITION, + } + + /** + * Set unspecified parameters to their defaults. + * If the parameter is mandatory, print an error message. + * @param id + * @param input + * @return The validated stage data + */ + static function validateStageData(id:String, input:StageData):Null + { + if (input == null) + { + trace('[STAGEDATA] ERROR: Could not parse stage data for "${id}".'); + return null; + } + + if (input.version != STAGE_DATA_VERSION) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version'); + return null; + } + + if (input.name == null) + { + trace('[STAGEDATA] WARN: Stage data for "$id" missing name'); + input.name = DEFAULT_NAME; + } + + if (input.cameraZoom == null) + { + input.cameraZoom = DEFAULT_CAMERAZOOM; + } + + if (input.props == null || input.props.length == 0) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props'); + return null; + } + + for (inputProp in input.props) + { + // It's fine for inputProp.name to be null + + if (inputProp.assetPath == null) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing assetPath for prop "${inputProp.name}"'); + return null; + } + + if (inputProp.position == null) + { + inputProp.position = DEFAULT_POSITION; + } + + if (inputProp.zIndex == null) + { + inputProp.zIndex = DEFAULT_ZINDEX; + } + + if (inputProp.isPixel == null) + { + inputProp.isPixel = DEFAULT_ISPIXEL; + } + + if (inputProp.danceEvery == null) + { + inputProp.danceEvery = DEFAULT_DANCEEVERY; + } + + if (inputProp.scale == null) + { + inputProp.scale = DEFAULT_SCALE; + } + + if (inputProp.animType == null) + { + inputProp.animType = DEFAULT_ANIMTYPE; + } + + if (Std.isOfType(inputProp.scale, Float)) + { + inputProp.scale = [inputProp.scale, inputProp.scale]; + } + + if (inputProp.scroll == null) + { + inputProp.scroll = DEFAULT_SCROLL; + } + + if (Std.isOfType(inputProp.scroll, Float)) + { + inputProp.scroll = [inputProp.scroll, inputProp.scroll]; + } + + if (inputProp.animations == null) + { + inputProp.animations = []; + } + + if (inputProp.animations.length == 0 && inputProp.startingAnimation != null) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animations for prop "${inputProp.name}"'); + return null; + } + + for (inputAnimation in inputProp.animations) + { + if (inputAnimation.name == null) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animation name for prop "${inputProp.name}"'); + return null; + } + + if (inputAnimation.frameRate == null) + { + inputAnimation.frameRate = 24; + } + + if (inputAnimation.frameIndices == null) + { + inputAnimation.frameIndices = DEFAULT_FRAMEINDICES; + } + + if (inputAnimation.loop == null) + { + inputAnimation.loop = true; + } + + if (inputAnimation.flipX == null) + { + inputAnimation.flipX = false; + } + + if (inputAnimation.flipY == null) + { + inputAnimation.flipY = false; + } + } + } + + if (input.characters == null) + { + trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing characters'); + return null; + } + + if (input.characters.bf == null) + { + input.characters.bf = DEFAULT_CHARACTER_DATA; + } + if (input.characters.dad == null) + { + input.characters.dad = DEFAULT_CHARACTER_DATA; + } + if (input.characters.gf == null) + { + input.characters.gf = DEFAULT_CHARACTER_DATA; + } + + for (inputCharacter in [input.characters.bf, input.characters.dad, input.characters.gf]) + { + if (inputCharacter.zIndex == null) + { + inputCharacter.zIndex = 0; + } + if (inputCharacter.position == null || inputCharacter.position.length != 2) + { + inputCharacter.position = [0, 0]; + } + } + + // All good! + return input; + } +} + +typedef StageData = +{ + // Uses semantic versioning. + var version:String; + var name:String; + var cameraZoom:Null; + var props:Array; + var characters: + { + bf:StageDataCharacter, + dad:StageDataCharacter, + gf:StageDataCharacter, + }; +}; + +typedef StageDataProp = +{ + /** + * The name of the prop for later lookup by scripts. + * Optional; if unspecified, the prop can't be referenced by scripts. + */ + var name:String; + + /** + * The asset used to display the prop. + */ + var assetPath:String; + + /** + * The position of the prop as an [x, y] array of two floats. + */ + var position:Array; + + /** + * A number determining the stack order of the prop, relative to other props and the characters in the stage. + * Props with lower numbers render below those with higher numbers. + * This is just like CSS, it isn't hard. + * @default 0 + */ + var zIndex:Null; + + /** + * If set to true, anti-aliasing will be forcibly disabled on the sprite. + * This prevents blurry images on pixel-art levels. + * @default false + */ + var isPixel:Null; + + /** + * Either the scale of the prop as a float, or the [w, h] scale as an array of two floats. + * Pro tip: On pixel-art levels, save the sprite small and set this value to 6 or so to save memory. + * @default 1 + */ + var scale:OneOfTwo>; + + /** + * If not zero, this prop will play an animation every X beats of the song. + * This requires animations to be defined. If `danceLeft` and `danceRight` are defined, + * they will alternated between, otherwise the `idle` animation will be used. + * + * @default 0 + */ + var danceEvery:Null; + + /** + * How much the prop scrolls relative to the camera. Used to create a parallax effect. + * Represented as a float or as an [x, y] array of two floats. + * [1, 1] means the prop moves 1:1 with the camera. + * [0.5, 0.5] means the prop half as much as the camera. + * [0, 0] means the prop is not moved. + * @default [0, 0] + */ + var scroll:OneOfTwo>; + + /** + * An optional array of animations which the prop can play. + * @default Prop has no animations. + */ + var animations:Array; + + /** + * If animations are used, this is the name of the animation to play first. + * @default Don't play an animation. + */ + var startingAnimation:String; + + /** + * The animation type to use. + * Options: "sparrow", "packer" + * @default "sparrow" + */ + 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; + + /** + * The speed of the animation in frames per second. + * @default 24 + */ + var frameRate:Null; + + /** + * Whether the animation should loop. + * @default false + */ + var loop:Null; + + /** + * Whether to flip the sprite horizontally while animating. + * @default false + */ + var flipX:Null; + + /** + * Whether to flip the sprite vertically while animating. + * @default false + */ + var flipY:Null; +}; + +typedef StageDataCharacter = +{ + /** + * A number determining the stack order of the character, relative to props and other characters in the stage. + * Again, just like CSS. + * @default 0 + */ + zIndex:Null, + + /** + * The position to render the character at. + */ position:Array +}; diff --git a/source/rendering/MeshRender.hx b/source/funkin/rendering/MeshRender.hx similarity index 98% rename from source/rendering/MeshRender.hx rename to source/funkin/rendering/MeshRender.hx index d981a1303..2865b1a3d 100644 --- a/source/rendering/MeshRender.hx +++ b/source/funkin/rendering/MeshRender.hx @@ -1,4 +1,4 @@ -package rendering; +package funkin.rendering; import flixel.FlxStrip; import flixel.util.FlxColor; diff --git a/source/shaderslmfao/AngleMask.hx b/source/funkin/shaderslmfao/AngleMask.hx similarity index 96% rename from source/shaderslmfao/AngleMask.hx rename to source/funkin/shaderslmfao/AngleMask.hx index 2b276de05..1f16d63ca 100644 --- a/source/shaderslmfao/AngleMask.hx +++ b/source/funkin/shaderslmfao/AngleMask.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; diff --git a/source/BlendModeEffect.hx b/source/funkin/shaderslmfao/BlendModeEffect.hx similarity index 96% rename from source/BlendModeEffect.hx rename to source/funkin/shaderslmfao/BlendModeEffect.hx index 1c9038be7..bc242b3e3 100644 --- a/source/BlendModeEffect.hx +++ b/source/funkin/shaderslmfao/BlendModeEffect.hx @@ -1,4 +1,4 @@ -package; +package funkin.shaderslmfao; import flixel.util.FlxColor; import openfl.display.ShaderParameter; diff --git a/source/shaderslmfao/BuildingShaders.hx b/source/funkin/shaderslmfao/BuildingShaders.hx similarity index 96% rename from source/shaderslmfao/BuildingShaders.hx rename to source/funkin/shaderslmfao/BuildingShaders.hx index a89a3b0df..62b9b0154 100644 --- a/source/shaderslmfao/BuildingShaders.hx +++ b/source/funkin/shaderslmfao/BuildingShaders.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; diff --git a/source/shaderslmfao/ColorSwap.hx b/source/funkin/shaderslmfao/ColorSwap.hx similarity index 99% rename from source/shaderslmfao/ColorSwap.hx rename to source/funkin/shaderslmfao/ColorSwap.hx index e24ac1095..0738789be 100644 --- a/source/shaderslmfao/ColorSwap.hx +++ b/source/funkin/shaderslmfao/ColorSwap.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; import flixel.util.FlxColor; diff --git a/source/shaderslmfao/MultiplyShader.hx b/source/funkin/shaderslmfao/MultiplyShader.hx similarity index 95% rename from source/shaderslmfao/MultiplyShader.hx rename to source/funkin/shaderslmfao/MultiplyShader.hx index e86820ba1..27ae60d3b 100644 --- a/source/shaderslmfao/MultiplyShader.hx +++ b/source/funkin/shaderslmfao/MultiplyShader.hx @@ -1,4 +1,4 @@ -package; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; @@ -28,4 +28,4 @@ class MultiplyShader extends FlxShader { super(); } -} \ No newline at end of file +} diff --git a/source/shaderslmfao/OverlayBlend.hx b/source/funkin/shaderslmfao/OverlayBlend.hx similarity index 97% rename from source/shaderslmfao/OverlayBlend.hx rename to source/funkin/shaderslmfao/OverlayBlend.hx index cf8996346..162726f69 100644 --- a/source/shaderslmfao/OverlayBlend.hx +++ b/source/funkin/shaderslmfao/OverlayBlend.hx @@ -1,8 +1,9 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxShader; +@:keep class OverlayBlend extends FlxShader { // these r copypaste diff --git a/source/shaderslmfao/PureColor.hx b/source/funkin/shaderslmfao/PureColor.hx similarity index 96% rename from source/shaderslmfao/PureColor.hx rename to source/funkin/shaderslmfao/PureColor.hx index 774df767a..3546e7fda 100644 --- a/source/shaderslmfao/PureColor.hx +++ b/source/funkin/shaderslmfao/PureColor.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; import flixel.util.FlxColor; diff --git a/source/shaderslmfao/ScreenWipeShader.hx b/source/funkin/shaderslmfao/ScreenWipeShader.hx similarity index 98% rename from source/shaderslmfao/ScreenWipeShader.hx rename to source/funkin/shaderslmfao/ScreenWipeShader.hx index 5264f7c33..37e6d604d 100644 --- a/source/shaderslmfao/ScreenWipeShader.hx +++ b/source/funkin/shaderslmfao/ScreenWipeShader.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; diff --git a/source/shaderslmfao/StrokeShader.hx b/source/funkin/shaderslmfao/StrokeShader.hx similarity index 98% rename from source/shaderslmfao/StrokeShader.hx rename to source/funkin/shaderslmfao/StrokeShader.hx index a6ba1ccad..575ff9419 100644 --- a/source/shaderslmfao/StrokeShader.hx +++ b/source/funkin/shaderslmfao/StrokeShader.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; import flixel.util.FlxColor; diff --git a/source/shaderslmfao/TitleOutline.hx b/source/funkin/shaderslmfao/TitleOutline.hx similarity index 98% rename from source/shaderslmfao/TitleOutline.hx rename to source/funkin/shaderslmfao/TitleOutline.hx index c8d002d85..cabab767d 100644 --- a/source/shaderslmfao/TitleOutline.hx +++ b/source/funkin/shaderslmfao/TitleOutline.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxShader; diff --git a/source/shaderslmfao/WaveShader.hx b/source/funkin/shaderslmfao/WaveShader.hx similarity index 91% rename from source/shaderslmfao/WaveShader.hx rename to source/funkin/shaderslmfao/WaveShader.hx index 9e7be1adf..14aa2b7b1 100644 --- a/source/shaderslmfao/WaveShader.hx +++ b/source/funkin/shaderslmfao/WaveShader.hx @@ -1,4 +1,4 @@ -package shaderslmfao; +package funkin.shaderslmfao; import flixel.system.FlxAssets.FlxShader; diff --git a/source/WiggleEffect.hx b/source/funkin/shaderslmfao/WiggleEffect.hx similarity index 91% rename from source/WiggleEffect.hx rename to source/funkin/shaderslmfao/WiggleEffect.hx index d11bf6e7a..bae55c6b1 100644 --- a/source/WiggleEffect.hx +++ b/source/funkin/shaderslmfao/WiggleEffect.hx @@ -1,4 +1,4 @@ -package; +package funkin.shaderslmfao; // STOLEN FROM HAXEFLIXEL DEMO LOL import flixel.system.FlxAssets.FlxShader; @@ -20,9 +20,13 @@ class WiggleEffect public var waveFrequency(default, set):Float = 0; public var waveAmplitude(default, set):Float = 0; - public function new():Void + public function new(speed:Float, freq:Float, amplitude:Float, ?effect:WiggleEffectType = DREAMY):Void { shader.uTime.value = [0]; + this.waveSpeed = speed; + this.waveFrequency = freq; + this.waveAmplitude = amplitude; + this.effectType = effect; } public function update(elapsed:Float):Void @@ -57,6 +61,11 @@ class WiggleEffect shader.uWaveAmplitude.value = [waveAmplitude]; return v; } + + function toString() + { + return 'WiggleEffect(${shader.uTime.value[0]})'; + } } class WiggleShader extends FlxShader diff --git a/source/ui/AtlasMenuList.hx b/source/funkin/ui/AtlasMenuList.hx similarity index 86% rename from source/ui/AtlasMenuList.hx rename to source/funkin/ui/AtlasMenuList.hx index 68232da5c..7a217a44c 100644 --- a/source/ui/AtlasMenuList.hx +++ b/source/funkin/ui/AtlasMenuList.hx @@ -1,7 +1,6 @@ -package ui; - -import ui.MenuList; +package funkin.ui; +import funkin.ui.MenuList; import flixel.graphics.frames.FlxAtlasFrames; typedef AtlasAsset = flixel.util.typeLimit.OneOfTwo; @@ -9,24 +8,24 @@ typedef AtlasAsset = flixel.util.typeLimit.OneOfTwo; class AtlasMenuList extends MenuTypedList { public var atlas:FlxAtlasFrames; - - public function new (atlas, navControls:NavControls = Vertical, ?wrapMode) + + public function new(atlas, navControls:NavControls = Vertical, ?wrapMode) { super(navControls, wrapMode); - + if (Std.is(atlas, String)) this.atlas = Paths.getSparrowAtlas(cast atlas); else this.atlas = cast atlas; } - + public function createItem(x = 0.0, y = 0.0, name, callback, fireInstantly = false) { var item = new AtlasMenuItem(x, y, name, atlas, callback); item.fireInstantly = fireInstantly; return addItem(name, item); } - + override function destroy() { super.destroy(); @@ -35,47 +34,48 @@ class AtlasMenuList extends MenuTypedList } class AtlasMenuItem extends MenuItem -{ +{ var atlas:FlxAtlasFrames; - public function new (x = 0.0, y = 0.0, name:String, atlas:FlxAtlasFrames, callback) + + public function new(x = 0.0, y = 0.0, name:String, atlas:FlxAtlasFrames, callback) { this.atlas = atlas; super(x, y, name, callback); } - + override function setData(name:String, ?callback:Void->Void) { frames = atlas; animation.addByPrefix('idle', '$name idle', 24); animation.addByPrefix('selected', '$name selected', 24); - + super.setData(name, callback); } - + function changeAnim(animName:String) { animation.play(animName); updateHitbox(); } - + override function idle() { changeAnim('idle'); } - + override function select() { changeAnim('selected'); } - + override function get_selected() { return animation.curAnim != null && animation.curAnim.name == "selected"; } - + override function destroy() { super.destroy(); atlas = null; } -} \ No newline at end of file +} diff --git a/source/ui/AtlasText.hx b/source/funkin/ui/AtlasText.hx similarity index 80% rename from source/ui/AtlasText.hx rename to source/funkin/ui/AtlasText.hx index 9ac6ae059..6df5f9e22 100644 --- a/source/ui/AtlasText.hx +++ b/source/funkin/ui/AtlasText.hx @@ -1,4 +1,4 @@ -package ui; +package funkin.ui; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; @@ -8,7 +8,7 @@ import flixel.util.FlxStringUtil; @:forward abstract BoldText(AtlasText) from AtlasText to AtlasText { - inline public function new (x = 0.0, y = 0.0, text:String) + inline public function new(x = 0.0, y = 0.0, text:String) { this = new AtlasText(x, y, text, Bold); } @@ -21,58 +21,67 @@ class AtlasText extends FlxTypedSpriteGroup { static var fonts = new Map(); static var casesAllowed = new Map(); + public var text(default, set):String = ""; - + var font:AtlasFontData; - + public var atlas(get, never):FlxAtlasFrames; - inline function get_atlas() return font.atlas; + + inline function get_atlas() + return font.atlas; + public var caseAllowed(get, never):Case; - inline function get_caseAllowed() return font.caseAllowed; + + inline function get_caseAllowed() + return font.caseAllowed; + public var maxHeight(get, never):Float; - inline function get_maxHeight() return font.maxHeight; - - public function new (x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default) + + inline function get_maxHeight() + return font.maxHeight; + + public function new(x = 0.0, y = 0.0, text:String, fontName:AtlasFont = Default) { if (!fonts.exists(fontName)) fonts[fontName] = new AtlasFontData(fontName); font = fonts[fontName]; - + super(x, y); - + this.text = text; } - + function set_text(value:String) { if (value == null) value = ""; - + var caseValue = restrictCase(value); var caseText = restrictCase(this.text); - + this.text = value; if (caseText == caseValue) return value; // cancel redraw - + if (caseValue.indexOf(caseText) == 0) { // new text is just old text with additions at the end, append the difference appendTextCased(caseValue.substr(caseText.length)); return this.text; } - + value = caseValue; - + group.kill(); - + if (value == "") return this.text; - + appendTextCased(caseValue); return this.text; } - + /** * Adds new characters, without needing to redraw the previous characters * @param text The text to add. @@ -82,27 +91,27 @@ class AtlasText extends FlxTypedSpriteGroup { if (text == null) throw "cannot append null"; - + if (text == "") return; - + this.text = this.text + text; } - + /** * Converts all characters to fit the font's `allowedCase`. * @param text */ function restrictCase(text:String) { - return switch(caseAllowed) + return switch (caseAllowed) { case Both: text; case Upper: text.toUpperCase(); case Lower: text.toLowerCase(); } } - + /** * Adds new text on top of the existing text. Helper for other methods; DOESN'T CHANGE `this.text`. * @param text The text to add, assumed to match the font's `caseAllowed`. @@ -121,58 +130,58 @@ class AtlasText extends FlxTypedSpriteGroup xPos = lastChar.x + lastChar.width - x; yPos = lastChar.y + lastChar.height - maxHeight - y; } - + var splitValues = text.split(""); for (i in 0...splitValues.length) { - switch(splitValues[i]) + switch (splitValues[i]) { case " ": - { - xPos += 40; - } - case "\n": - { - xPos = 0; - yPos += maxHeight; - } - case char: - { - var charSprite:AtlasChar; - if (group.members.length <= charCount) - charSprite = new AtlasChar(atlas, char); - else { - charSprite = group.members[charCount]; - charSprite.revive(); - charSprite.char = char; - charSprite.alpha = 1;//gets multiplied when added + xPos += 40; + } + case "\n": + { + xPos = 0; + yPos += maxHeight; + } + case char: + { + var charSprite:AtlasChar; + if (group.members.length <= charCount) + charSprite = new AtlasChar(atlas, char); + else + { + charSprite = group.members[charCount]; + charSprite.revive(); + charSprite.char = char; + charSprite.alpha = 1; // gets multiplied when added + } + charSprite.x = xPos; + charSprite.y = yPos + maxHeight - charSprite.height; + add(charSprite); + + xPos += charSprite.width; + charCount++; } - charSprite.x = xPos; - charSprite.y = yPos + maxHeight - charSprite.height; - add(charSprite); - - xPos += charSprite.width; - charCount++; - } } } } - + override function toString() { - return "InputItem, " + FlxStringUtil.getDebugString( - [ LabelValuePair.weak("x", x) - , LabelValuePair.weak("y", y) - , LabelValuePair.weak("text", text) - ] - ); + return "InputItem, " + FlxStringUtil.getDebugString([ + LabelValuePair.weak("x", x), + LabelValuePair.weak("y", y), + LabelValuePair.weak("text", text) + ]); } } class AtlasChar extends FlxSprite { public var char(default, set):String; + public function new(x = 0.0, y = 0.0, atlas:FlxAtlasFrames, char:String) { super(x, y); @@ -180,7 +189,7 @@ class AtlasChar extends FlxSprite this.char = char; antialiasing = true; } - + function set_char(value:String) { if (this.char != value) @@ -230,31 +239,31 @@ private class AtlasFontData { static public var upperChar = ~/^[A-Z]\d+$/; static public var lowerChar = ~/^[a-z]\d+$/; - + public var atlas:FlxAtlasFrames; public var maxHeight:Float = 0.0; public var caseAllowed:Case = Both; - - public function new (name:AtlasFont) + + public function new(name:AtlasFont) { atlas = Paths.getSparrowAtlas("fonts/" + name.getName().toLowerCase()); atlas.parent.destroyOnNoUse = false; atlas.parent.persist = true; - + var containsUpper = false; var containsLower = false; - + for (frame in atlas.frames) { maxHeight = Math.max(maxHeight, frame.frame.height); - + if (!containsUpper) containsUpper = upperChar.match(frame.name); - + if (!containsLower) containsLower = lowerChar.match(frame.name); } - + if (containsUpper != containsLower) caseAllowed = containsUpper ? Upper : Lower; } @@ -271,4 +280,4 @@ enum AtlasFont { Default; Bold; -} \ No newline at end of file +} diff --git a/source/ui/ColorsMenu.hx b/source/funkin/ui/ColorsMenu.hx similarity index 94% rename from source/ui/ColorsMenu.hx rename to source/funkin/ui/ColorsMenu.hx index cef364c2c..84739058b 100644 --- a/source/ui/ColorsMenu.hx +++ b/source/funkin/ui/ColorsMenu.hx @@ -1,11 +1,12 @@ -package ui; +package funkin.ui; import flixel.addons.effects.chainable.FlxEffectSprite; import flixel.addons.effects.chainable.FlxOutlineEffect; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.util.FlxColor; +import funkin.ui.OptionsState.Page; -class ColorsMenu extends ui.OptionsState.Page +class ColorsMenu extends Page { var curSelected:Int = 0; diff --git a/source/ui/ControlsMenu.hx b/source/funkin/ui/ControlsMenu.hx similarity index 97% rename from source/ui/ControlsMenu.hx rename to source/funkin/ui/ControlsMenu.hx index 7b8d129cf..fb4144b5b 100644 --- a/source/ui/ControlsMenu.hx +++ b/source/funkin/ui/ControlsMenu.hx @@ -1,6 +1,6 @@ -package ui; +package funkin.ui; -import Controls; +import funkin.Controls; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxSprite; @@ -8,11 +8,11 @@ import flixel.group.FlxGroup; import flixel.input.actions.FlxActionInput; import flixel.input.gamepad.FlxGamepadInputID; import flixel.input.keyboard.FlxKey; -import ui.AtlasText; -import ui.MenuList; -import ui.TextMenuList; +import funkin.ui.AtlasText; +import funkin.ui.MenuList; +import funkin.ui.TextMenuList; -class ControlsMenu extends ui.OptionsState.Page +class ControlsMenu extends funkin.ui.OptionsState.Page { inline static public var COLUMNS = 2; static var controlList = Control.createAll(); diff --git a/source/ui/MenuList.hx b/source/funkin/ui/MenuList.hx similarity index 99% rename from source/ui/MenuList.hx rename to source/funkin/ui/MenuList.hx index e8bc63cc3..347415f70 100644 --- a/source/ui/MenuList.hx +++ b/source/funkin/ui/MenuList.hx @@ -1,4 +1,4 @@ -package ui; +package funkin.ui; import flixel.FlxSprite; import flixel.effects.FlxFlicker; diff --git a/source/ui/ModMenu.hx b/source/funkin/ui/ModMenu.hx similarity index 71% rename from source/ui/ModMenu.hx rename to source/funkin/ui/ModMenu.hx index bd87d290d..3d195e861 100644 --- a/source/ui/ModMenu.hx +++ b/source/funkin/ui/ModMenu.hx @@ -1,16 +1,17 @@ -package ui; +package funkin.ui; +import funkin.modding.PolymodHandler; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.text.FlxText; import flixel.util.FlxColor; import polymod.Polymod; -import sys.FileSystem; +import funkin.ui.OptionsState.Page; -class ModMenu extends ui.OptionsState.Page +class ModMenu extends Page { var grpMods:FlxTypedGroup; - var enabledMods:Array = []; - var modFolders:Array = []; + var enabledMods:Array = []; + var detectedMods:Array = []; var curSelected:Int = 0; @@ -62,10 +63,10 @@ class ModMenu extends ui.OptionsState.Page { curSelected += change; - if (curSelected >= modFolders.length) + if (curSelected >= detectedMods.length) curSelected = 0; if (curSelected < 0) - curSelected = modFolders.length - 1; + curSelected = detectedMods.length - 1; for (txt in 0...grpMods.length) { @@ -80,8 +81,6 @@ class ModMenu extends ui.OptionsState.Page organizeByY(); } - inline static var MOD_PATH = "./mods"; - private function refreshModList():Void { while (grpMods.members.length > 0) @@ -90,36 +89,17 @@ class ModMenu extends ui.OptionsState.Page } #if desktop - var modList = []; - modFolders = []; + detectedMods = PolymodHandler.getAllMods(); - trace("mods path:" + FileSystem.absolutePath(MOD_PATH)); - if (!FileSystem.exists(MOD_PATH)) + trace('ModMenu: Detected ${detectedMods.length} mods'); + + for (index in 0...detectedMods.length) { - FlxG.log.warn("missing mods folder, expected: " + FileSystem.absolutePath(MOD_PATH)); - return; - } - - for (file in FileSystem.readDirectory(MOD_PATH)) - { - if (FileSystem.isDirectory(MOD_PATH + file)) - modFolders.push(file); - } - - enabledMods = []; - - modList = Polymod.scan(MOD_PATH); - - trace(modList); - - var loopNum:Int = 0; - for (i in modFolders) - { - var txt:ModMenuItem = new ModMenuItem(0, 10 + (40 * loopNum), 0, i, 32); - txt.text = i; + var modMetadata:ModMetadata = detectedMods[index]; + var modName:String = modMetadata.title; + var txt:ModMenuItem = new ModMenuItem(0, 10 + (40 * index), 0, modName, 32); + txt.text = modName; grpMods.add(txt); - - loopNum++; } #end } diff --git a/source/ui/NgPrompt.hx b/source/funkin/ui/NgPrompt.hx similarity index 52% rename from source/ui/NgPrompt.hx rename to source/funkin/ui/NgPrompt.hx index 4bf75997f..716880b11 100644 --- a/source/ui/NgPrompt.hx +++ b/source/funkin/ui/NgPrompt.hx @@ -1,80 +1,77 @@ -package ui; +package funkin.ui; -import NGio; -import ui.Prompt; +#if newgrounds +import funkin.NGio; +import funkin.ui.Prompt; class NgPrompt extends Prompt { - public function new (text:String, style:ButtonStyle = Yes_No) + public function new(text:String, style:ButtonStyle = Yes_No) { super(text, style); } - + static public function showLogin() { return showLoginPrompt(true); } - + static public function showSavedSessionFailed() { return showLoginPrompt(false); } - + static function showLoginPrompt(fromUi:Bool) { var prompt = new NgPrompt("Talking to server...", None); - prompt.openCallback = NGio.login.bind - ( - function popupLauncher(openPassportUrl) + prompt.openCallback = NGio.login.bind(function popupLauncher(openPassportUrl) + { + var choiceMsg = fromUi ? #if web "Log in to Newgrounds?" #else null #end // User-input needed to allow popups + : "Your session has expired.\n Please login again."; + + if (choiceMsg != null) { - var choiceMsg = fromUi - ? #if web "Log in to Newgrounds?" #else null #end // User-input needed to allow popups - : "Your session has expired.\n Please login again."; - - if (choiceMsg != null) + prompt.setText(choiceMsg); + prompt.setButtons(Yes_No); + #if web + prompt.buttons.getItem("yes").fireInstantly = true; + #end + prompt.onYes = function() { - prompt.setText(choiceMsg); - prompt.setButtons(Yes_No); - #if web - prompt.buttons.getItem("yes").fireInstantly = true; - #end - prompt.onYes = function() - { - prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); - prompt.setButtons(None); - openPassportUrl(); - }; - prompt.onNo = function() - { - prompt.close(); - prompt = null; - NGio.cancelLogin(); - }; - } - else - { - prompt.setText("Connecting..."); + prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); + prompt.setButtons(None); openPassportUrl(); - } - }, - function onLoginComplete(result:ConnectionResult) - { - switch (result) + }; + prompt.onNo = function() { - case Success: + prompt.close(); + prompt = null; + NGio.cancelLogin(); + }; + } + else + { + prompt.setText("Connecting..."); + openPassportUrl(); + } + }, function onLoginComplete(result:ConnectionResult) + { + switch (result) + { + case Success: { prompt.setText("Login Successful"); prompt.setButtons(Ok); prompt.onYes = prompt.close; } - case Fail(msg): + case Fail(msg): { trace("Login Error:" + msg); prompt.setText("Login failed"); prompt.setButtons(Ok); prompt.onYes = prompt.close; } - case Cancelled: + case Cancelled: { if (prompt != null) { @@ -85,13 +82,12 @@ class NgPrompt extends Prompt else trace("Login cancelled via prompt"); } - } } - ); - + }); + return prompt; } - + static public function showLogout() { var user = io.newgrounds.NG.core.user.name; @@ -104,4 +100,5 @@ class NgPrompt extends Prompt prompt.onNo = prompt.close; return prompt; } -} \ No newline at end of file +} +#end diff --git a/source/ui/OptionsState.hx b/source/funkin/ui/OptionsState.hx similarity index 98% rename from source/ui/OptionsState.hx rename to source/funkin/ui/OptionsState.hx index 816834933..1b6d03e93 100644 --- a/source/ui/OptionsState.hx +++ b/source/funkin/ui/OptionsState.hx @@ -1,11 +1,11 @@ -package ui; +package funkin.ui; import flixel.FlxSprite; import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; import flixel.group.FlxGroup; import flixel.util.FlxSignal; -import i18n.FireTongueHandler.t; +import funkin.i18n.FireTongueHandler.t; // typedef OptionsState = OptionsMenu_old; // class OptionsState_new extends MusicBeatState @@ -33,9 +33,7 @@ class OptionsState extends MusicBeatState var controls = addPage(Controls, new ControlsMenu()); // var colors = addPage(Colors, new ColorsMenu()); - #if polymod var mods = addPage(Mods, new ModMenu()); - #end if (options.hasMultipleOptions()) { @@ -43,10 +41,7 @@ class OptionsState extends MusicBeatState controls.onExit.add(switchPage.bind(Options)); // colors.onExit.add(switchPage.bind(Options)); preferences.onExit.add(switchPage.bind(Options)); - - #if polymod mods.onExit.add(switchPage.bind(Options)); - #end } else { @@ -180,9 +175,7 @@ class OptionsMenu extends Page createItem(t("PREFERENCES"), function() switchPage(Preferences)); createItem(t("CONTROLS"), function() switchPage(Controls)); // createItem(t("COLORS"), function() switchPage(Colors)); - #if polymod createItem(t("MODS"), function() switchPage(Mods)); - #end #if CAN_OPEN_LINKS if (showDonate) diff --git a/source/ui/PopUpStuff.hx b/source/funkin/ui/PopUpStuff.hx similarity index 93% rename from source/ui/PopUpStuff.hx rename to source/funkin/ui/PopUpStuff.hx index eeef727f1..02f08687d 100644 --- a/source/ui/PopUpStuff.hx +++ b/source/funkin/ui/PopUpStuff.hx @@ -1,8 +1,9 @@ -package ui; +package funkin.ui; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.tweens.FlxTween; +import funkin.play.PlayState; using StringTools; @@ -21,7 +22,7 @@ class PopUpStuff extends FlxTypedGroup var rating:FlxSprite = new FlxSprite(); var ratingPath:String = daRating; - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; rating.loadGraphic(Paths.image(ratingPath)); @@ -39,7 +40,7 @@ class PopUpStuff extends FlxTypedGroup add(rating); - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) { rating.setGraphicSize(Std.int(rating.width * PlayState.daPixelZoom * 0.7)); } @@ -68,7 +69,7 @@ class PopUpStuff extends FlxTypedGroup var pixelShitPart1:String = ""; var pixelShitPart2:String = ''; - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) { pixelShitPart1 = 'weeb/pixelUI/'; pixelShitPart2 = '-pixel'; @@ -89,7 +90,7 @@ class PopUpStuff extends FlxTypedGroup add(comboSpr); - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) { comboSpr.setGraphicSize(Std.int(comboSpr.width * PlayState.daPixelZoom * 0.7)); } @@ -128,7 +129,7 @@ class PopUpStuff extends FlxTypedGroup var numScore:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2)); numScore.y = comboSpr.y; - if (PlayState.curStage.startsWith('school')) + if (PlayState.curStageId.startsWith('school')) { numScore.setGraphicSize(Std.int(numScore.width * PlayState.daPixelZoom)); } diff --git a/source/ui/PreferencesMenu.hx b/source/funkin/ui/PreferencesMenu.hx similarity index 96% rename from source/ui/PreferencesMenu.hx rename to source/funkin/ui/PreferencesMenu.hx index d77aff357..6fcf7b57a 100644 --- a/source/ui/PreferencesMenu.hx +++ b/source/funkin/ui/PreferencesMenu.hx @@ -1,14 +1,13 @@ -package ui; +package funkin.ui; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxSprite; -import flixel.group.FlxGroup; -import flixel.util.FlxColor; -import ui.AtlasText.AtlasFont; -import ui.TextMenuList.TextMenuItem; +import funkin.ui.AtlasText.AtlasFont; +import funkin.ui.TextMenuList.TextMenuItem; +import funkin.ui.OptionsState.Page; -class PreferencesMenu extends ui.OptionsState.Page +class PreferencesMenu extends Page { public static var preferences:Map = new Map(); diff --git a/source/ui/Prompt.hx b/source/funkin/ui/Prompt.hx similarity index 97% rename from source/ui/Prompt.hx rename to source/funkin/ui/Prompt.hx index acd584e97..1679d3886 100644 --- a/source/ui/Prompt.hx +++ b/source/funkin/ui/Prompt.hx @@ -1,11 +1,11 @@ -package ui; +package funkin.ui; import flixel.FlxSprite; import flixel.graphics.frames.FlxAtlasFrames; import flixel.text.FlxText; import flixel.util.FlxColor; -import ui.AtlasText; -import ui.MenuList; +import funkin.ui.AtlasText; +import funkin.ui.MenuList; class Prompt extends flixel.FlxSubState { diff --git a/source/ui/TextMenuList.hx b/source/funkin/ui/TextMenuList.hx similarity index 94% rename from source/ui/TextMenuList.hx rename to source/funkin/ui/TextMenuList.hx index c507ae864..ae3ed7bff 100644 --- a/source/ui/TextMenuList.hx +++ b/source/funkin/ui/TextMenuList.hx @@ -1,7 +1,7 @@ -package ui; +package funkin.ui; -import ui.AtlasText; -import ui.MenuList; +import funkin.ui.AtlasText; +import funkin.ui.MenuList; class TextMenuList extends MenuTypedList { diff --git a/source/ui/animDebugShit/DebugBoundingState.hx b/source/funkin/ui/animDebugShit/DebugBoundingState.hx similarity index 99% rename from source/ui/animDebugShit/DebugBoundingState.hx rename to source/funkin/ui/animDebugShit/DebugBoundingState.hx index 5a2b1535d..9f49f5cc0 100644 --- a/source/ui/animDebugShit/DebugBoundingState.hx +++ b/source/funkin/ui/animDebugShit/DebugBoundingState.hx @@ -1,4 +1,4 @@ -package ui.animDebugShit; +package funkin.ui.animDebugShit; import flixel.FlxCamera; import flixel.FlxSprite; diff --git a/source/ui/stageBuildShit/CharStage.hx b/source/funkin/ui/stageBuildShit/CharStage.hx similarity index 80% rename from source/ui/stageBuildShit/CharStage.hx rename to source/funkin/ui/stageBuildShit/CharStage.hx index a0e1ed062..740c8de3b 100644 --- a/source/ui/stageBuildShit/CharStage.hx +++ b/source/funkin/ui/stageBuildShit/CharStage.hx @@ -1,4 +1,4 @@ -package ui.stageBuildShit; +package funkin.ui.stageBuildShit; class CharStage extends SprStage { diff --git a/source/ui/stageBuildShit/SprStage.hx b/source/funkin/ui/stageBuildShit/SprStage.hx similarity index 96% rename from source/ui/stageBuildShit/SprStage.hx rename to source/funkin/ui/stageBuildShit/SprStage.hx index 5011355f0..95eba9325 100644 --- a/source/ui/stageBuildShit/SprStage.hx +++ b/source/funkin/ui/stageBuildShit/SprStage.hx @@ -1,4 +1,4 @@ -package ui.stageBuildShit; +package funkin.ui.stageBuildShit; import flixel.FlxSprite; import flixel.input.mouse.FlxMouseEventManager; diff --git a/source/ui/stageBuildShit/StageBuilderState.hx b/source/funkin/ui/stageBuildShit/StageBuilderState.hx similarity index 99% rename from source/ui/stageBuildShit/StageBuilderState.hx rename to source/funkin/ui/stageBuildShit/StageBuilderState.hx index 1ca1f6145..5ebb6df57 100644 --- a/source/ui/stageBuildShit/StageBuilderState.hx +++ b/source/funkin/ui/stageBuildShit/StageBuilderState.hx @@ -1,4 +1,4 @@ -package ui.stageBuildShit; +package funkin.ui.stageBuildShit; import flixel.FlxCamera; import flixel.FlxSprite; diff --git a/source/ui/stageBuildShit/StagetoolBar.hx b/source/funkin/ui/stageBuildShit/StagetoolBar.hx similarity index 91% rename from source/ui/stageBuildShit/StagetoolBar.hx rename to source/funkin/ui/stageBuildShit/StagetoolBar.hx index 74337d64d..521881c9c 100644 --- a/source/ui/stageBuildShit/StagetoolBar.hx +++ b/source/funkin/ui/stageBuildShit/StagetoolBar.hx @@ -1,4 +1,4 @@ -package ui.stageBuildShit; +package funkin.ui.stageBuildShit; import flixel.group.FlxGroup; diff --git a/source/util/SortUtil.hx b/source/funkin/util/SortUtil.hx similarity index 65% rename from source/util/SortUtil.hx rename to source/funkin/util/SortUtil.hx index 0cac21ac8..29f64fb0d 100644 --- a/source/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -1,8 +1,10 @@ -package util; +package funkin.util; +#if !macro import flixel.group.FlxGroup.FlxTypedGroup; import flixel.util.FlxSort; import flixel.FlxObject; +#end class SortUtil { @@ -14,13 +16,4 @@ class SortUtil { return FlxSort.byValues(Order, Obj1.zIndex, Obj2.zIndex); } - - /** - * Sorts the element in an FlxTypedGroup by their z-index values. - * @param group The group to sort. - */ - public static inline function sortByZIndex(group:FlxTypedGroup) - { - group.sort(byZIndex, FlxSort.ASCENDING); - } } diff --git a/source/funkin/util/assets/DataAssets.hx b/source/funkin/util/assets/DataAssets.hx new file mode 100644 index 000000000..d00703068 --- /dev/null +++ b/source/funkin/util/assets/DataAssets.hx @@ -0,0 +1,30 @@ +package funkin.util.assets; + +using StringTools; + +class DataAssets +{ + static function buildDataPath(path:String):String + { + return 'assets/data/${path}'; + } + + public static function listDataFilesInPath(path:String, ?ext:String = '.json'):Array + { + var textAssets = openfl.utils.Assets.list(); + var queryPath = buildDataPath(path); + + var results:Array = []; + for (textPath in textAssets) + { + if (textPath.startsWith(queryPath) && textPath.endsWith(ext)) + { + var pathNoSuffix = textPath.substring(0, textPath.length - ext.length); + var pathNoPrefix = pathNoSuffix.substring(queryPath.length); + results.push(pathNoPrefix); + } + } + + return results; + } +} diff --git a/source/util/macro/FlxMacro.hx b/source/funkin/util/macro/FlxMacro.hx similarity index 97% rename from source/util/macro/FlxMacro.hx rename to source/funkin/util/macro/FlxMacro.hx index f66be4a32..adbee3319 100644 --- a/source/util/macro/FlxMacro.hx +++ b/source/funkin/util/macro/FlxMacro.hx @@ -1,4 +1,4 @@ -package util.macro; +package funkin.util.macro; #if macro class FlxMacro diff --git a/source/funkin/util/macro/MacroUtil.hx b/source/funkin/util/macro/MacroUtil.hx new file mode 100644 index 000000000..ebf5bd5ec --- /dev/null +++ b/source/funkin/util/macro/MacroUtil.hx @@ -0,0 +1,12 @@ +package funkin.util.macro; + +class MacroUtil +{ + public static macro function getDefine(key:String, defaultValue:String = null):haxe.macro.Expr + { + var value = haxe.macro.Context.definedValue(key); + if (value == null) + value = defaultValue; + return macro $v{value}; + } +} diff --git a/source/play/stage/Stage.hx b/source/play/stage/Stage.hx deleted file mode 100644 index b612a97d0..000000000 --- a/source/play/stage/Stage.hx +++ /dev/null @@ -1,9 +0,0 @@ -package play.stage; - -import flixel.FlxObject; -import flixel.group.FlxGroup.FlxTypedGroup; - -/** - * A Stage is a group of objects. - */ -class Stage extends FlxTypedGroup {}