diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..2f5488a9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +ko_fi: codename_engine diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1efcfcfa..2d2dc145 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -7,7 +7,7 @@ jobs: build: name: Linux Build permissions: write-all - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Pulling the source uses: actions/checkout@v2 @@ -35,13 +35,13 @@ jobs: - name: Building the game run: | haxelib run lime build linux - - name: Tar files - run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin . + # - name: Tar files + # run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin . - name: Uploading artifact (entire build) uses: actions/upload-artifact@v4 with: name: Codename Engine - path: CodenameEngine.tar.gz + path: export/release/linux/bin/ # - name: Uploading artifact (executable) # uses: actions/upload-artifact@v4 # with: diff --git a/.gitignore b/.gitignore index 7f430cbe..77035890 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ export/ dump/ -latest/windows/obj/ -latest/windows/haxe/ +latest/* .vscode/* !.vscode/settings.json !.vscode/tasks.json @@ -11,4 +10,6 @@ docs/doc.xml mods/* addons/* !mods/readme.txt +addons/* +!addons/readme.txt dev-libs/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 281a6d2d..4a694405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,13 +16,14 @@ This changelog lists every change that has been added since the last update (inc - Added a function ``WindowUtils.resetAffixes()`` to reset the prefix and suffix of the engine's window title, and fixed a typo ([1d94945](https://github.com/CodenameCrew/CodenameEngine/commit/1d94945faeea86af02bcaecd715f5bea22ef48fd)) - Added a field `extra` to FunkinSprite, which can be used to store any extra data you want (e.g. sprite.extra.set("name", value)) ([fb48570](https://github.com/CodenameCrew/CodenameEngine/commit/fb485701b451d6a7a22a1e696bfc081699f1c6d2)) - Added default icon colors to every base game character ([814bd71](https://github.com/CodenameCrew/CodenameEngine/commit/814bd712fe2ea2dd21116e9903d9c2babe5341b1)) -- Added more features to cne.bat/cne.sh: ([4ef43df](https://github.com/CodenameCrew/CodenameEngine/commit/4ef43dfbecc79283d038d939f94671966802039e), [0dc166c](https://github.com/CodenameCrew/CodenameEngine/commit/0dc166c601b25e3167a1d04542840265484576b4)) +- Added more features to cne.bat/cne.sh: ([4ef43df](https://github.com/CodenameCrew/CodenameEngine/commit/4ef43dfbecc79283d038d939f94671966802039e), [0dc166c](https://github.com/CodenameCrew/CodenameEngine/commit/0dc166c601b25e3167a1d04542840265484576b4), [21bba98](https://github.com/CodenameCrew/CodenameEngine/commit/21bba98923307fe75667e1395781d068a7ec47ad)) - Setup - `--reinstall` to reinstall all libraries - `--no-vscheck` to skip the Visual Studio check - `--silent` to not print the download progress - - project file has conditionals now + - libs file has conditionals now - `--lib=path.xml` to use a specific setup file + - `--fast` for quickly downloading git libraries, but requires you to reinstall it fully when updates happen - Alphabet now allows multiple lines in the text ([d519db0](https://github.com/CodenameCrew/CodenameEngine/commit/d519db011a14aac0ddfc052089836c6b445f7f44)) - Allow importing mod root level modules in hscript, instead of only source/ ([7e66ee8](https://github.com/CodenameCrew/CodenameEngine/commit/7e66ee81e24204cd152de20046fa1500bb9d204b)) - Added a way to force animations in the `Play Animation` event ([d7b6fbe](https://github.com/CodenameCrew/CodenameEngine/commit/d7b6fbe510edeb832112ab5adc8ab19dfe97d77f)) @@ -63,7 +64,6 @@ This changelog lists every change that has been added since the last update (inc - Added `bf` shorthand for `boyfriend` in playstate ([2b81220](https://github.com/CodenameCrew/CodenameEngine/commit/2b81220c15ed77acecd17c09a975ed401677165f)) - Added character script extensions ([682358c](https://github.com/CodenameCrew/CodenameEngine/commit/682358c93a0c17469d1a481de0523e4c4e19c6ec)) - Folder support in the character editor selection ([68c2fea](https://github.com/CodenameCrew/CodenameEngine/commit/68c2feab373db69d4a00b6f274f98afb3d8e3803)) -- cne.bat/cne.sh setup --fast for quickly downloading git libraries ([21bba98](https://github.com/CodenameCrew/CodenameEngine/commit/21bba98923307fe75667e1395781d068a7ec47ad)) - Allow hold notes to be force deleted ([25b5630](https://github.com/CodenameCrew/CodenameEngine/commit/25b563066fc53ac92ebe3064175c80b5f1ae64bc)) - Allow tweening shaders and array values using FlxTween ([3de3c3f](https://github.com/CodenameCrew/CodenameEngine/commit/3de3c3f8286b2b07c9690334e2d85ce052a909c5)) - Made the advanced info show `Mod State: {name}` instead of `State: funkin.backend.scripting.ModState` ([3de3c3f](https://github.com/CodenameCrew/CodenameEngine/commit/3de3c3f8286b2b07c9690334e2d85ce052a909c5)) diff --git a/README.md b/README.md index 4ba32a47..fb3918bb 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,6 @@ If you don't have a github account please go onto https://fnf-cne-devs.github.io

Credits

-- Credits to [Ne_Eo](https://twitter.com/Ne_Eo_Twitch) and the [3D-HaxeFlixel](https://github.com/lunarcleint/3D-HaxeFlixel) repository for Away3D Flixel support - Credits to the [FlxAnimate](https://github.com/Dot-Stuff/flxanimate) team for the Animate Atlas support - Credits to Smokey555 for the backup Animate Atlas to spritesheet code - Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) (video cutscene/mp4 support) and [hxdiscord_rpc](https://github.com/MAJigsaw77/hxdiscord_rpc) (discord rpc integration) diff --git a/addons/readme.txt b/addons/readme.txt new file mode 100644 index 00000000..df84a4b2 --- /dev/null +++ b/addons/readme.txt @@ -0,0 +1,22 @@ +╔═══════════════════════════════════════╗ +║ HOW TO USE ║ +╚═══════════════════════════════════════╝ +DRAG AND DROP YOUR ADDON FOLDER HERE! + +╔═══════════════════════════════════════╗ +║ ZIP ADDONS ║ +╚═══════════════════════════════════════╝ +Zip files are also supported at condition it follows this hierarchy: + +📁addon.zip/ + 📁data + 📁images + 📁... + +Instead of this one + +📁addon.zip + 📁my addon + 📁data + 📁images + 📁... \ No newline at end of file diff --git a/art/FMOD Logo Black - White Background.png b/art/FMOD Logo Black - White Background.png index 273ef6f0..b7cf1d1c 100644 Binary files a/art/FMOD Logo Black - White Background.png and b/art/FMOD Logo Black - White Background.png differ diff --git a/art/FMOD Logo White - Black Background.png b/art/FMOD Logo White - Black Background.png index 1780488d..3e23026c 100644 Binary files a/art/FMOD Logo White - Black Background.png and b/art/FMOD Logo White - Black Background.png differ diff --git a/art/burns.png b/art/burns.png index 89b08ba6..bc004e04 100644 Binary files a/art/burns.png and b/art/burns.png differ diff --git a/art/design ideas/charter events/1.png b/art/design ideas/charter events/1.png index 3340e41f..d63b0d7d 100644 Binary files a/art/design ideas/charter events/1.png and b/art/design ideas/charter events/1.png differ diff --git a/art/design ideas/charter events/2.png b/art/design ideas/charter events/2.png index ac451a03..8e5d1939 100644 Binary files a/art/design ideas/charter events/2.png and b/art/design ideas/charter events/2.png differ diff --git a/art/github/windows-installation-details.png b/art/github/windows-installation-details.png index 5ff9dc19..c1291faf 100644 Binary files a/art/github/windows-installation-details.png and b/art/github/windows-installation-details.png differ diff --git a/art/icon16.png b/art/icon16.png index 2adacd69..ab61bcbe 100644 Binary files a/art/icon16.png and b/art/icon16.png differ diff --git a/art/iconOG.png b/art/iconOG.png index b0268e51..6ec3bcd5 100644 Binary files a/art/iconOG.png and b/art/iconOG.png differ diff --git a/art/preloaderArt.png b/art/preloaderArt.png index 176e51b3..86ddbdf6 100644 Binary files a/art/preloaderArt.png and b/art/preloaderArt.png differ diff --git a/art/teaser discord.png b/art/teaser discord.png index 45fa51ef..58a2e421 100644 Binary files a/art/teaser discord.png and b/art/teaser discord.png differ diff --git a/art/teaser layers/teaser bg.png b/art/teaser layers/teaser bg.png index b4e6af05..a65e5ffe 100644 Binary files a/art/teaser layers/teaser bg.png and b/art/teaser layers/teaser bg.png differ diff --git a/art/teaser layers/teaser checkerboard.png b/art/teaser layers/teaser checkerboard.png index f515995f..7a9e6150 100644 Binary files a/art/teaser layers/teaser checkerboard.png and b/art/teaser layers/teaser checkerboard.png differ diff --git a/art/teaser layers/teaser funkin logo.png b/art/teaser layers/teaser funkin logo.png index 557b740a..102cb82c 100644 Binary files a/art/teaser layers/teaser funkin logo.png and b/art/teaser layers/teaser funkin logo.png differ diff --git a/art/teaser layers/teaser question mark.png b/art/teaser layers/teaser question mark.png index 0cbbec86..30f040df 100644 Binary files a/art/teaser layers/teaser question mark.png and b/art/teaser layers/teaser question mark.png differ diff --git a/art/teaser.png b/art/teaser.png index b185f7ac..82e424ad 100644 Binary files a/art/teaser.png and b/art/teaser.png differ diff --git a/art/thumbnailNewer.png b/art/thumbnailNewer.png index 73f381fc..daea6f62 100644 Binary files a/art/thumbnailNewer.png and b/art/thumbnailNewer.png differ diff --git a/art/veryimportant.png b/art/veryimportant.png index 89910647..2fe31d88 100644 Binary files a/art/veryimportant.png and b/art/veryimportant.png differ diff --git a/assets/data/config/discord.json b/assets/data/config/discord.json index 582e510b..f6ad9636 100644 --- a/assets/data/config/discord.json +++ b/assets/data/config/discord.json @@ -1,4 +1,4 @@ { - "clientID": "1027994136193810442", + "clientID": "1383853614589673472", "logoKey": "icon" } \ No newline at end of file diff --git a/assets/data/config/modpack.ini b/assets/data/config/modpack.ini new file mode 100644 index 00000000..313d86b2 --- /dev/null +++ b/assets/data/config/modpack.ini @@ -0,0 +1,25 @@ +[Common] +Name="baconeggandcheese" +Description="who gaf" +APIVersion="uhmmmm probs like 2" +DownloadedFrom="https://yourmom.org" +Dependencies=[ + "linkA", + "linkB", + "linkC" +] +Icon64="path/to/icon64.png" +Icon32="path/to/icon32.png" +Icon16="path/to/icon16.png" +Icon="path/to/icon.ico" + +[Flags] +UseNoteRendererV2=false +UseAway3D=false +UseDiscordRPCIntegration=true +UseLocalization=false +UseLegacyFlixelAnimateHitbox=false + +[Discord] +ClientID="1027994136193810442", +LogoKey="icon" \ No newline at end of file diff --git a/assets/data/global.hx b/assets/data/global.hx deleted file mode 100644 index 4c49ef69..00000000 --- a/assets/data/global.hx +++ /dev/null @@ -1,3 +0,0 @@ -// -function update(elapsed:Float) - if (FlxG.keys.justPressed.F5) FlxG.resetState(); \ No newline at end of file diff --git a/assets/data/notes/Alt Anim Note.hx b/assets/data/notes/Alt Anim Note.hx index b44b1398..ebc3e361 100644 --- a/assets/data/notes/Alt Anim Note.hx +++ b/assets/data/notes/Alt Anim Note.hx @@ -1,2 +1,2 @@ function onNoteHit(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt"; -function onPlayerMiss(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt"; \ No newline at end of file +function onPlayerMiss(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "miss-alt"; \ No newline at end of file diff --git a/assets/data/scripts/pixel.hx b/assets/data/scripts/pixel.hx index 26d18251..7031838e 100644 --- a/assets/data/scripts/pixel.hx +++ b/assets/data/scripts/pixel.hx @@ -18,9 +18,7 @@ public var daPixelZoom = PlayState.daPixelZoom; * UI */ function onNoteCreation(event) { - if (event.note.strumLine == playerStrums && !pixelNotesForBF) return; - if (event.note.strumLine == cpuStrums && !pixelNotesForDad) return; - + if ((event.note.strumLine == playerStrums && !pixelNotesForBF) || (event.note.strumLine == cpuStrums && !pixelNotesForDad)) return; event.cancel(); var note = event.note; @@ -38,18 +36,14 @@ function onNoteCreation(event) { var strumScale = event.note.strumLine.strumScale; note.scale.set(daPixelZoom*strumScale, daPixelZoom*strumScale); note.updateHitbox(); + note.antialiasing = false; } -function onPostNoteCreation(event) { - var splashes = event.note; - if (pixelSplashes) - splashes.splash = "pixel-default"; -} +function onPostNoteCreation(event) if (pixelSplashes) + event.note.splash = "pixel-default"; function onStrumCreation(event) { - if (event.player == 1 && !pixelNotesForBF) return; - if (event.player == 0 && !pixelNotesForDad) return; - + if ((event.player == 1 && !pixelNotesForBF) || (event.player == 0 && !pixelNotesForDad)) return; event.cancel(); var strum = event.strum; @@ -64,6 +58,7 @@ function onStrumCreation(event) { var strumScale = strumLines.members[event.player].strumScale; strum.scale.set(daPixelZoom*strumScale, daPixelZoom*strumScale); strum.updateHitbox(); + strum.antialiasing = false; } function onCountdown(event) { @@ -94,9 +89,9 @@ function onPlayerHit(event:NoteHitEvent) { * CAMERA HACKS!! */ function postCreate() { - if (enablePauseMenu) { + if (enablePauseMenu) PauseSubState.script = 'data/scripts/week6-pause'; - } + if (enableCameraHacks) { camGame.pixelPerfectRender = true; camGame.antialiasing = false; @@ -105,9 +100,6 @@ function postCreate() { defaultCamZoom /= daPixelZoom; } - iconP1.antialiasing = false; - iconP2.antialiasing = false; - if (enablePixelGameOver) { gameOverSong = "pixel/gameOver"; lossSFX = "pixel/gameOverSFX"; @@ -115,8 +107,8 @@ function postCreate() { } } -function onStartCountdown() { - /*var newNoteCamera = new HudCamera(); +/*function onStartCountdown() { + var newNoteCamera = new HudCamera(); newNoteCamera.bgColor = 0; // transparent FlxG.cameras.add(newNoteCamera, false); @@ -130,8 +122,8 @@ function onStartCountdown() { i++; } } - makeCameraPixely(newNoteCamera);*/ -} + makeCameraPixely(newNoteCamera); +}*/ /** * Use this to make any camera pixelly (you wont be able to zoom with it anymore!) @@ -156,33 +148,29 @@ function destroy() { FlxG.game.stage.quality = oldStageQuality; } -function pixelCam(cam) { +function pixelCam(cam) makeCameraPixely(cam); -} var pixellyCameras = []; var pixellyShaders = []; -function postUpdate(elapsed) { - for(e in pixellyCameras) { - if (Std.isOfType(e, HudCamera)) - e.downscroll = camHUD.downscroll; - } - if (enableCameraHacks) { - for(p in strumLines) - p.notes.forEach(function(n) { - if(n.isSustainNote) return; // hacky fix for hold - n.y -= n.y % daPixelZoom; - n.x -= n.x % daPixelZoom; - }); +function postUpdate() { + for (e in pixellyCameras) if (Std.isOfType(e, HudCamera)) + e.downscroll = camHUD.downscroll; + + if (enableCameraHacks) for (p in strumLines) { + p.notes.forEach(function(n) { + if(n.isSustainNote) return; // hacky fix for hold + n.y -= n.y % daPixelZoom; + n.x -= n.x % daPixelZoom; + }); } var zoom = 1 / daPixelZoom / Math.min(FlxG.scaleMode.scale.x, FlxG.scaleMode.scale.y); - for(e in pixellyCameras) { + for (e in pixellyCameras) { if (!e.exists) continue; e.zoom = zoom; } - for(e in pixellyShaders) { + for (e in pixellyShaders) e.pixelZoom = zoom; - } -} \ No newline at end of file +} diff --git a/assets/data/scripts/week6-pause.hx b/assets/data/scripts/week6-pause.hx index 4eb71349..1538cff7 100644 --- a/assets/data/scripts/week6-pause.hx +++ b/assets/data/scripts/week6-pause.hx @@ -3,7 +3,7 @@ import flixel.text.FlxText; import flixel.text.FlxTextBorderStyle; import flixel.util.FlxAxes; -var pixelScript:Script; +var pixelScript:Script = game.scripts.getByName("pixel.hx"); var pauseCam = new FlxCamera(); var bg:FlxSprite; @@ -19,47 +19,40 @@ function create(event) { event.music = isThorns ? "pixel/LunchboxScary" : "pixel/breakfast"; - cameras = []; - - pixelScript = game.scripts.getByName("pixel.hx"); - pixelScript.call("pixelCam", [pauseCam]); + // allowing this pause script even if the pixel script is not loaded - Nex + pixelScript?.call("pixelCam", [pauseCam]); FlxG.cameras.add(pauseCam, false); + cameras = [pauseCam]; pauseCam.bgColor = isThorns ? 0x88000000 : 0x88FF99CC; pauseCam.alpha = 0; - bg = new FlxSprite(44 * 6, 14 * 6); - bg.loadGraphic(Paths.image('stages/school/pause/bg')); - if (isThorns) - bg.color = 0xFF000000; + bg = new FlxSprite(44 * 6, 14 * 6, Paths.image('stages/school/pause/bg')); + bg.antialiasing = false; + if (isThorns) bg.color = 0xFF000000; bg.scale.set(6, 6); bg.updateHitbox(); bg.scale.y = 4; - bg.cameras = [pauseCam]; add(bg); songText = new FlxText(0, 22 * 6, 0, "Pause", 8, false); confText(songText); add(songText); - var i = 2; - for(e in menuItems) { - text = new FlxText(0, (22 * 6) + (i * 9 * 6), 0, e, 8, false); + for (i=>e in menuItems) { + text = new FlxText(0, (22 * 6) + ((i+2) * 9 * 6), 0, e, 8, false); confText(text); - add(text); - texts.push(text); - i++; } hand = new FlxSprite().loadGraphic(Paths.image('stages/school/ui/hand_textbox')); + hand.antialiasing = false; hand.scale.set(6, 6); hand.updateHitbox(); add(hand); - FlxTween.tween(bg, {"scale.y": 6}, 0.75, {ease: FlxEase.elasticOut}); + FlxTween.tween(bg.scale, {y: 6}, 0.75, {ease: FlxEase.elasticOut}); - cameras = [pauseCam]; FlxG.sound.play(Paths.sound(isThorns ? 'pixel/ANGRY' : 'pixel/clickText')); } @@ -70,55 +63,50 @@ function confText(text) { text.screenCenter(FlxAxes.X); text.borderStyle = FlxTextBorderStyle.OUTLINE; if (!isThorns) text.borderColor = 0xFF953E3E; + + text.antialiasing = false; + texts.push(text); + add(text); } -function destroy() { - if(FlxG.cameras.list.contains(pauseCam)) - FlxG.cameras.remove(pauseCam); -} +function destroy() if (FlxG.cameras.list.contains(pauseCam)) + FlxG.cameras.remove(pauseCam); var canDoShit = true; var time:Float = 0; function update(elapsed) { - pixelScript.call("postUpdate", [elapsed]); + pixelScript?.call("postUpdate", [elapsed]); pauseCam.alpha = lerp(pauseCam.alpha, 1, 0.25); time += elapsed; - var curText = texts[curSelected]; + var curText = texts[curSelected + 1]; hand.setPosition(curText.x - hand.width - 18 + (Math.sin(time * Math.PI * 2) * 12), curText.y + (text.height - hand.height) - 6); hand.x -= hand.x % 6; hand.y -= hand.y % 6; - if (!canDoShit) return; - var oldSec = curSelected; - changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel); - if (oldSec != curSelected) - FlxG.sound.play(Paths.sound(isThorns ? 'pixel/type' : 'pixel/pixelText')); + if (controls.ACCEPT) enterOption(); +} - if (controls.ACCEPT) { - FlxG.sound.play(Paths.sound(isThorns ? 'pixel/ANGRY' : 'pixel/clickText')); - var option = menuItems[curSelected]; - if (option == "Resume" || option == "Exit to menu") { +var scrollSFX = FlxG.sound.load(Paths.sound(isThorns ? 'pixel/type' : 'pixel/pixelText')); +function changeSelection(change) if (canDoShit) { // this overrides the function inside of the normal pause btw, so no event gets called - Nex + curSelected = FlxMath.wrap(curSelected + change, 0, menuItems.length - 1); + if (change != 0) scrollSFX.play(); +} + +var enterSFX = FlxG.sound.load(Paths.sound(isThorns ? 'pixel/ANGRY' : 'pixel/clickText')); +function enterOption() if (canDoShit) { + var option = menuItems[curSelected]; + enterSFX.play(); + + switch(option) { + case "Resume", "Exit to menu": canDoShit = false; for(t in texts) t.visible = false; hand.visible = songText.visible = false; - FlxTween.tween(bg, {"scale.y": 0}, 0.125, {ease: FlxEase.cubeOut, onComplete: function() { - selectOption(); - }}); - } else { - selectOption(); - } + FlxTween.tween(bg.scale, {y: 0}, 0.125, {ease: FlxEase.cubeOut, onComplete: selectOption}); + default: selectOption(); } } - -function changeSelection(change) { // this overrides the function inside of the normal pause btw, so no event gets called - Nex - curSelected += change; - - if (curSelected < 0) - curSelected = menuItems.length - 1; - if (curSelected >= menuItems.length) - curSelected = 0; -} \ No newline at end of file diff --git a/assets/data/scripts/week7-balledLines.hx b/assets/data/scripts/week7-balledLines.hx index 677d3791..d6049a48 100644 --- a/assets/data/scripts/week7-balledLines.hx +++ b/assets/data/scripts/week7-balledLines.hx @@ -3,13 +3,7 @@ var sound:Sound; function create() sound = Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, !Options.naughtyness ? [1, 3, 8, 13, 17, 21] : null)); -function beatHit(cur:Int) { - if(cur != 0) return; - +function beatHit(cur:Int) if (cur == 0) { + FlxG.sound.play(sound, 1, false, null, true, () -> if (!isEnding) FlxG.sound.music.fadeIn(4, 0.2, 1)); FlxG.sound.music.volume = 0.2; - FlxG.sound.play(sound, 1, false, null, true, function() - { - if (!isEnding) - FlxG.sound.music.fadeIn(4, 0.2, 1); - }); } \ No newline at end of file diff --git a/assets/data/stages/tank.hx b/assets/data/stages/tank.hx index 03e213c6..e74b04d9 100644 --- a/assets/data/stages/tank.hx +++ b/assets/data/stages/tank.hx @@ -1,7 +1,7 @@ var tankAngle:Float = FlxG.random.int(-90, 45); var tankSpeed:Float = FlxG.random.float(5, 7); -function postCreate() if(PlayState.isStoryMode) +function postCreate() if (PlayState.isStoryMode) GameOverSubstate.script = 'data/scripts/week7-balledLines'; function update(elapsed:Float) { @@ -12,7 +12,7 @@ function update(elapsed:Float) { } function onPostCountdown(event) { - if(!playCutscenes || event.sprite == null) return; + if (!playCutscenes || event.sprite == null) return; var lastIndex:Int = members.indexOf(event.sprite); remove(event.sprite); insert(lastIndex + 4, event.sprite); diff --git a/assets/data/weeks/characters/spooky.xml b/assets/data/weeks/characters/spooky.xml index e4955b1f..3b32cd36 100644 --- a/assets/data/weeks/characters/spooky.xml +++ b/assets/data/weeks/characters/spooky.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/images/characters/bf-car.png b/assets/images/characters/bf-car.png index 41d64ad2..6623e0ea 100644 Binary files a/assets/images/characters/bf-car.png and b/assets/images/characters/bf-car.png differ diff --git a/assets/images/characters/bf-christmas.png b/assets/images/characters/bf-christmas.png index 98fc69b9..9b6c1649 100644 Binary files a/assets/images/characters/bf-christmas.png and b/assets/images/characters/bf-christmas.png differ diff --git a/assets/images/characters/bf-dead.png b/assets/images/characters/bf-dead.png index 6d129a65..1cff4b78 100644 Binary files a/assets/images/characters/bf-dead.png and b/assets/images/characters/bf-dead.png differ diff --git a/assets/images/characters/bf-holding-gf-dead.png b/assets/images/characters/bf-holding-gf-dead.png index 1d6f3844..7d15df94 100644 Binary files a/assets/images/characters/bf-holding-gf-dead.png and b/assets/images/characters/bf-holding-gf-dead.png differ diff --git a/assets/images/characters/bf-holding-gf.png b/assets/images/characters/bf-holding-gf.png index 0549ae76..d6482460 100644 Binary files a/assets/images/characters/bf-holding-gf.png and b/assets/images/characters/bf-holding-gf.png differ diff --git a/assets/images/characters/bf-pixel-dead.png b/assets/images/characters/bf-pixel-dead.png index ddb688a2..bd8dbcb9 100644 Binary files a/assets/images/characters/bf-pixel-dead.png and b/assets/images/characters/bf-pixel-dead.png differ diff --git a/assets/images/characters/bf-pixel.png b/assets/images/characters/bf-pixel.png index eb78553d..7abde04c 100644 Binary files a/assets/images/characters/bf-pixel.png and b/assets/images/characters/bf-pixel.png differ diff --git a/assets/images/characters/bf.png b/assets/images/characters/bf.png index 2722d1c1..ce924dd2 100644 Binary files a/assets/images/characters/bf.png and b/assets/images/characters/bf.png differ diff --git a/assets/images/characters/da.png b/assets/images/characters/da.png index 9a1a2da5..e711edb9 100644 Binary files a/assets/images/characters/da.png and b/assets/images/characters/da.png differ diff --git a/assets/images/characters/dad.png b/assets/images/characters/dad.png index cd37de1c..5f678a27 100644 Binary files a/assets/images/characters/dad.png and b/assets/images/characters/dad.png differ diff --git a/assets/images/characters/gf-car/spritemap1.png b/assets/images/characters/gf-car/spritemap1.png index c7f64af5..25fbeae7 100644 Binary files a/assets/images/characters/gf-car/spritemap1.png and b/assets/images/characters/gf-car/spritemap1.png differ diff --git a/assets/images/characters/gf-christmas/spritemap1.png b/assets/images/characters/gf-christmas/spritemap1.png index 9b28e8ab..0e81d9b9 100644 Binary files a/assets/images/characters/gf-christmas/spritemap1.png and b/assets/images/characters/gf-christmas/spritemap1.png differ diff --git a/assets/images/characters/gf-pixel.png b/assets/images/characters/gf-pixel.png index fb3d596c..aaaf95ee 100644 Binary files a/assets/images/characters/gf-pixel.png and b/assets/images/characters/gf-pixel.png differ diff --git a/assets/images/characters/gf-tankmen/spritemap1.png b/assets/images/characters/gf-tankmen/spritemap1.png index 7511af39..81f91c24 100644 Binary files a/assets/images/characters/gf-tankmen/spritemap1.png and b/assets/images/characters/gf-tankmen/spritemap1.png differ diff --git a/assets/images/characters/gf/spritemap1.png b/assets/images/characters/gf/spritemap1.png index ac70fc2d..226f417f 100644 Binary files a/assets/images/characters/gf/spritemap1.png and b/assets/images/characters/gf/spritemap1.png differ diff --git a/assets/images/characters/mom-car.png b/assets/images/characters/mom-car.png index 3c9405ac..738df30f 100644 Binary files a/assets/images/characters/mom-car.png and b/assets/images/characters/mom-car.png differ diff --git a/assets/images/characters/mom.png b/assets/images/characters/mom.png index 033929a5..6cc21a8a 100644 Binary files a/assets/images/characters/mom.png and b/assets/images/characters/mom.png differ diff --git a/assets/images/characters/monster-christmas.png b/assets/images/characters/monster-christmas.png index 7dd02243..6537b944 100644 Binary files a/assets/images/characters/monster-christmas.png and b/assets/images/characters/monster-christmas.png differ diff --git a/assets/images/characters/monster.png b/assets/images/characters/monster.png index 451d21b1..98c3eec0 100644 Binary files a/assets/images/characters/monster.png and b/assets/images/characters/monster.png differ diff --git a/assets/images/characters/parents-christmas.png b/assets/images/characters/parents-christmas.png index e13fee01..ced08d00 100644 Binary files a/assets/images/characters/parents-christmas.png and b/assets/images/characters/parents-christmas.png differ diff --git a/assets/images/characters/pico-speakers.png b/assets/images/characters/pico-speakers.png index f20d1a80..b4a2bc26 100644 Binary files a/assets/images/characters/pico-speakers.png and b/assets/images/characters/pico-speakers.png differ diff --git a/assets/images/characters/pico.png b/assets/images/characters/pico.png index 12c083d2..9a211b81 100644 Binary files a/assets/images/characters/pico.png and b/assets/images/characters/pico.png differ diff --git a/assets/images/characters/senpai.png b/assets/images/characters/senpai.png index 1c7f8f52..99600632 100644 Binary files a/assets/images/characters/senpai.png and b/assets/images/characters/senpai.png differ diff --git a/assets/images/characters/spooky.png b/assets/images/characters/spooky.png index f941ec2d..b79f6c8f 100644 Binary files a/assets/images/characters/spooky.png and b/assets/images/characters/spooky.png differ diff --git a/assets/images/characters/tankman.png b/assets/images/characters/tankman.png index edfda54a..b981a173 100644 Binary files a/assets/images/characters/tankman.png and b/assets/images/characters/tankman.png differ diff --git a/assets/images/credits/credit icon example.png b/assets/images/credits/credit icon example.png index adbf906d..cdc0034a 100644 Binary files a/assets/images/credits/credit icon example.png and b/assets/images/credits/credit icon example.png differ diff --git a/assets/images/dialogue/boxes/default.png b/assets/images/dialogue/boxes/default.png index 1c22d73b..3960d1ff 100644 Binary files a/assets/images/dialogue/boxes/default.png and b/assets/images/dialogue/boxes/default.png differ diff --git a/assets/images/dialogue/boxes/pixel/dialogueBox-evil.png b/assets/images/dialogue/boxes/pixel/dialogueBox-evil.png index fcbbfec3..18dcec5a 100644 Binary files a/assets/images/dialogue/boxes/pixel/dialogueBox-evil.png and b/assets/images/dialogue/boxes/pixel/dialogueBox-evil.png differ diff --git a/assets/images/dialogue/boxes/pixel/dialogueBox.png b/assets/images/dialogue/boxes/pixel/dialogueBox.png index 73afeea9..09d4c490 100644 Binary files a/assets/images/dialogue/boxes/pixel/dialogueBox.png and b/assets/images/dialogue/boxes/pixel/dialogueBox.png differ diff --git a/assets/images/dialogue/characters/boyfriend.png b/assets/images/dialogue/characters/boyfriend.png index 341ba166..6ccc2829 100644 Binary files a/assets/images/dialogue/characters/boyfriend.png and b/assets/images/dialogue/characters/boyfriend.png differ diff --git a/assets/images/dialogue/characters/dad.png b/assets/images/dialogue/characters/dad.png index de81cf3c..be0e5b77 100644 Binary files a/assets/images/dialogue/characters/dad.png and b/assets/images/dialogue/characters/dad.png differ diff --git a/assets/images/dialogue/characters/niko.png b/assets/images/dialogue/characters/niko.png index 81acec6e..b7fde571 100644 Binary files a/assets/images/dialogue/characters/niko.png and b/assets/images/dialogue/characters/niko.png differ diff --git a/assets/images/editors/autosave-delete.png b/assets/images/editors/autosave-delete.png index 18ab7de0..3e4b8ab2 100644 Binary files a/assets/images/editors/autosave-delete.png and b/assets/images/editors/autosave-delete.png differ diff --git a/assets/images/editors/autosave-icons.png b/assets/images/editors/autosave-icons.png index 26f99bc2..6b5136f4 100644 Binary files a/assets/images/editors/autosave-icons.png and b/assets/images/editors/autosave-icons.png differ diff --git a/assets/images/editors/bgs/charter.png b/assets/images/editors/bgs/charter.png index e1a6f93a..03ea9580 100644 Binary files a/assets/images/editors/bgs/charter.png and b/assets/images/editors/bgs/charter.png differ diff --git a/assets/images/editors/character/WOAH.png b/assets/images/editors/character/WOAH.png index 8821bc1d..8d4b3b56 100644 Binary files a/assets/images/editors/character/WOAH.png and b/assets/images/editors/character/WOAH.png differ diff --git a/assets/images/editors/character/edit-button.png b/assets/images/editors/character/edit-button.png index c0ad7449..8b439a63 100644 Binary files a/assets/images/editors/character/edit-button.png and b/assets/images/editors/character/edit-button.png differ diff --git a/assets/images/editors/charter/add-button.png b/assets/images/editors/charter/add-button.png index 906d8ca3..defdb6ef 100644 Binary files a/assets/images/editors/charter/add-button.png and b/assets/images/editors/charter/add-button.png differ diff --git a/assets/images/editors/charter/event-icons/Add Camera Zoom.png b/assets/images/editors/charter/event-icons/Add Camera Zoom.png index 48ce845c..044b1196 100644 Binary files a/assets/images/editors/charter/event-icons/Add Camera Zoom.png and b/assets/images/editors/charter/event-icons/Add Camera Zoom.png differ diff --git a/assets/images/editors/charter/event-icons/Alt Animation Toggle.png b/assets/images/editors/charter/event-icons/Alt Animation Toggle.png index e982158a..a2046320 100644 Binary files a/assets/images/editors/charter/event-icons/Alt Animation Toggle.png and b/assets/images/editors/charter/event-icons/Alt Animation Toggle.png differ diff --git a/assets/images/editors/charter/event-icons/BPM Change.png b/assets/images/editors/charter/event-icons/BPM Change.png index b95e0d36..40dba09a 100644 Binary files a/assets/images/editors/charter/event-icons/BPM Change.png and b/assets/images/editors/charter/event-icons/BPM Change.png differ diff --git a/assets/images/editors/charter/event-icons/Camera Flash.png b/assets/images/editors/charter/event-icons/Camera Flash.png index 22903bea..3abfd1cc 100644 Binary files a/assets/images/editors/charter/event-icons/Camera Flash.png and b/assets/images/editors/charter/event-icons/Camera Flash.png differ diff --git a/assets/images/editors/charter/event-icons/Camera Modulo Change.png b/assets/images/editors/charter/event-icons/Camera Modulo Change.png index 5eb5f064..27353aeb 100644 Binary files a/assets/images/editors/charter/event-icons/Camera Modulo Change.png and b/assets/images/editors/charter/event-icons/Camera Modulo Change.png differ diff --git a/assets/images/editors/charter/event-icons/Camera Movement.png b/assets/images/editors/charter/event-icons/Camera Movement.png index 3126c502..74ba908a 100644 Binary files a/assets/images/editors/charter/event-icons/Camera Movement.png and b/assets/images/editors/charter/event-icons/Camera Movement.png differ diff --git a/assets/images/editors/charter/event-icons/HScript Call.png b/assets/images/editors/charter/event-icons/HScript Call.png index 5aa4095a..f63e4a2b 100644 Binary files a/assets/images/editors/charter/event-icons/HScript Call.png and b/assets/images/editors/charter/event-icons/HScript Call.png differ diff --git a/assets/images/editors/charter/event-icons/Play Animation.png b/assets/images/editors/charter/event-icons/Play Animation.png index 1092f05f..54da0eb4 100644 Binary files a/assets/images/editors/charter/event-icons/Play Animation.png and b/assets/images/editors/charter/event-icons/Play Animation.png differ diff --git a/assets/images/editors/charter/event-icons/Scroll Speed Change.png b/assets/images/editors/charter/event-icons/Scroll Speed Change.png index 4b06071c..97b6fc95 100644 Binary files a/assets/images/editors/charter/event-icons/Scroll Speed Change.png and b/assets/images/editors/charter/event-icons/Scroll Speed Change.png differ diff --git a/assets/images/editors/charter/event-icons/Unknown.png b/assets/images/editors/charter/event-icons/Unknown.png index 26ac16c6..5fbbdbb8 100644 Binary files a/assets/images/editors/charter/event-icons/Unknown.png and b/assets/images/editors/charter/event-icons/Unknown.png differ diff --git a/assets/images/editors/charter/event-spr-add.png b/assets/images/editors/charter/event-spr-add.png index 21cc06d9..5898ea3b 100644 Binary files a/assets/images/editors/charter/event-spr-add.png and b/assets/images/editors/charter/event-spr-add.png differ diff --git a/assets/images/editors/charter/event-spr.png b/assets/images/editors/charter/event-spr.png index 45149581..1d006cff 100644 Binary files a/assets/images/editors/charter/event-spr.png and b/assets/images/editors/charter/event-spr.png differ diff --git a/assets/images/editors/charter/event.png b/assets/images/editors/charter/event.png index 8fe12787..30860be9 100644 Binary files a/assets/images/editors/charter/event.png and b/assets/images/editors/charter/event.png differ diff --git a/assets/images/editors/charter/events-grid.png b/assets/images/editors/charter/events-grid.png index 3a4f90e6..a49b3d72 100644 Binary files a/assets/images/editors/charter/events-grid.png and b/assets/images/editors/charter/events-grid.png differ diff --git a/assets/images/editors/charter/lock-strumline.png b/assets/images/editors/charter/lock-strumline.png index 637e5f64..3175325c 100644 Binary files a/assets/images/editors/charter/lock-strumline.png and b/assets/images/editors/charter/lock-strumline.png differ diff --git a/assets/images/editors/charter/strumline-drag.png b/assets/images/editors/charter/strumline-drag.png index e886483f..e9f8b907 100644 Binary files a/assets/images/editors/charter/strumline-drag.png and b/assets/images/editors/charter/strumline-drag.png differ diff --git a/assets/images/editors/charter/strumline-info-bg.png b/assets/images/editors/charter/strumline-info-bg.png index 74d7d906..01bf0072 100644 Binary files a/assets/images/editors/charter/strumline-info-bg.png and b/assets/images/editors/charter/strumline-info-bg.png differ diff --git a/assets/images/editors/delete-button.png b/assets/images/editors/delete-button.png index 4a80427c..386c6662 100644 Binary files a/assets/images/editors/delete-button.png and b/assets/images/editors/delete-button.png differ diff --git a/assets/images/editors/deleter circle.png b/assets/images/editors/deleter circle.png index c4120b13..db63b885 100644 Binary files a/assets/images/editors/deleter circle.png and b/assets/images/editors/deleter circle.png differ diff --git a/assets/images/editors/deleter.png b/assets/images/editors/deleter.png index c3c39bcc..a872a17a 100644 Binary files a/assets/images/editors/deleter.png and b/assets/images/editors/deleter.png differ diff --git a/assets/images/editors/new.png b/assets/images/editors/new.png index 461fd2ea..5a062188 100644 Binary files a/assets/images/editors/new.png and b/assets/images/editors/new.png differ diff --git a/assets/images/editors/ui/audio-buttons.png b/assets/images/editors/ui/audio-buttons.png index 6bd347a8..7719e8eb 100644 Binary files a/assets/images/editors/ui/audio-buttons.png and b/assets/images/editors/ui/audio-buttons.png differ diff --git a/assets/images/editors/ui/audio-icon.png b/assets/images/editors/ui/audio-icon.png index 97eb7763..575450ba 100644 Binary files a/assets/images/editors/ui/audio-icon.png and b/assets/images/editors/ui/audio-icon.png differ diff --git a/assets/images/editors/ui/audio-time-empty.png b/assets/images/editors/ui/audio-time-empty.png index b08c0154..77f8fd21 100644 Binary files a/assets/images/editors/ui/audio-time-empty.png and b/assets/images/editors/ui/audio-time-empty.png differ diff --git a/assets/images/editors/ui/audio-time-full.png b/assets/images/editors/ui/audio-time-full.png index 85c4e77c..bd5ecd44 100644 Binary files a/assets/images/editors/ui/audio-time-full.png and b/assets/images/editors/ui/audio-time-full.png differ diff --git a/assets/images/editors/ui/audio-volume-empty.png b/assets/images/editors/ui/audio-volume-empty.png index 290f8099..c876657b 100644 Binary files a/assets/images/editors/ui/audio-volume-empty.png and b/assets/images/editors/ui/audio-volume-empty.png differ diff --git a/assets/images/editors/ui/audio-volume-full.png b/assets/images/editors/ui/audio-volume-full.png index eebab268..0304b3e3 100644 Binary files a/assets/images/editors/ui/audio-volume-full.png and b/assets/images/editors/ui/audio-volume-full.png differ diff --git a/assets/images/editors/ui/button.png b/assets/images/editors/ui/button.png index dc270bee..3107e42c 100644 Binary files a/assets/images/editors/ui/button.png and b/assets/images/editors/ui/button.png differ diff --git a/assets/images/editors/ui/checkbox.png b/assets/images/editors/ui/checkbox.png index 95ef7ccb..e4bdd9bb 100644 Binary files a/assets/images/editors/ui/checkbox.png and b/assets/images/editors/ui/checkbox.png differ diff --git a/assets/images/editors/ui/context-bg.png b/assets/images/editors/ui/context-bg.png index 4e46b537..1e4c74b3 100644 Binary files a/assets/images/editors/ui/context-bg.png and b/assets/images/editors/ui/context-bg.png differ diff --git a/assets/images/editors/ui/context-icons.png b/assets/images/editors/ui/context-icons.png index 733a8523..b7411d02 100644 Binary files a/assets/images/editors/ui/context-icons.png and b/assets/images/editors/ui/context-icons.png differ diff --git a/assets/images/editors/ui/inputbox.png b/assets/images/editors/ui/inputbox.png index f3b1df34..6c38457b 100644 Binary files a/assets/images/editors/ui/inputbox.png and b/assets/images/editors/ui/inputbox.png differ diff --git a/assets/images/editors/ui/menu-item.png b/assets/images/editors/ui/menu-item.png index 5babc03a..4a95616f 100644 Binary files a/assets/images/editors/ui/menu-item.png and b/assets/images/editors/ui/menu-item.png differ diff --git a/assets/images/editors/ui/normal-popup.png b/assets/images/editors/ui/normal-popup.png index 1227df6d..a4a0746b 100644 Binary files a/assets/images/editors/ui/normal-popup.png and b/assets/images/editors/ui/normal-popup.png differ diff --git a/assets/images/editors/ui/scrollbar-bg.png b/assets/images/editors/ui/scrollbar-bg.png index 1f49a24a..55fded94 100644 Binary files a/assets/images/editors/ui/scrollbar-bg.png and b/assets/images/editors/ui/scrollbar-bg.png differ diff --git a/assets/images/editors/ui/scrollbar-icon.png b/assets/images/editors/ui/scrollbar-icon.png index f2a5c0ec..feb29469 100644 Binary files a/assets/images/editors/ui/scrollbar-icon.png and b/assets/images/editors/ui/scrollbar-icon.png differ diff --git a/assets/images/editors/ui/selection.png b/assets/images/editors/ui/selection.png index 456897fe..2d95a2d1 100644 Binary files a/assets/images/editors/ui/selection.png and b/assets/images/editors/ui/selection.png differ diff --git a/assets/images/editors/ui/slider.png b/assets/images/editors/ui/slider.png index 82b6c80b..13758fbe 100644 Binary files a/assets/images/editors/ui/slider.png and b/assets/images/editors/ui/slider.png differ diff --git a/assets/images/editors/ui/textbox.png b/assets/images/editors/ui/textbox.png index a66637a4..2ada0766 100644 Binary files a/assets/images/editors/ui/textbox.png and b/assets/images/editors/ui/textbox.png differ diff --git a/assets/images/editors/ui/topmenu.png b/assets/images/editors/ui/topmenu.png index 4f010db4..3824c37f 100644 Binary files a/assets/images/editors/ui/topmenu.png and b/assets/images/editors/ui/topmenu.png differ diff --git a/assets/images/editors/ui/upload-button.png b/assets/images/editors/ui/upload-button.png index bfdba151..f3700640 100644 Binary files a/assets/images/editors/ui/upload-button.png and b/assets/images/editors/ui/upload-button.png differ diff --git a/assets/images/editors/warnings/error.png b/assets/images/editors/warnings/error.png index 07282f8b..aef47776 100644 Binary files a/assets/images/editors/warnings/error.png and b/assets/images/editors/warnings/error.png differ diff --git a/assets/images/editors/warnings/warning.png b/assets/images/editors/warnings/warning.png index 1d501046..44e8b560 100644 Binary files a/assets/images/editors/warnings/warning.png and b/assets/images/editors/warnings/warning.png differ diff --git a/assets/images/game/cutscenes/tank/guns-tankman/spritemap1.png b/assets/images/game/cutscenes/tank/guns-tankman/spritemap1.png index 337491b6..ccfe25ee 100644 Binary files a/assets/images/game/cutscenes/tank/guns-tankman/spritemap1.png and b/assets/images/game/cutscenes/tank/guns-tankman/spritemap1.png differ diff --git a/assets/images/game/cutscenes/tank/stress-tankman/spritemap1.png b/assets/images/game/cutscenes/tank/stress-tankman/spritemap1.png index 7ed88503..3c19a130 100644 Binary files a/assets/images/game/cutscenes/tank/stress-tankman/spritemap1.png and b/assets/images/game/cutscenes/tank/stress-tankman/spritemap1.png differ diff --git a/assets/images/game/cutscenes/tank/ugh-tankman/spritemap1.png b/assets/images/game/cutscenes/tank/ugh-tankman/spritemap1.png index 957cae53..787b49aa 100644 Binary files a/assets/images/game/cutscenes/tank/ugh-tankman/spritemap1.png and b/assets/images/game/cutscenes/tank/ugh-tankman/spritemap1.png differ diff --git a/assets/images/game/cutscenes/weeb/senpaiCrazy.png b/assets/images/game/cutscenes/weeb/senpaiCrazy.png index 1091f92a..752aa3d1 100644 Binary files a/assets/images/game/cutscenes/weeb/senpaiCrazy.png and b/assets/images/game/cutscenes/weeb/senpaiCrazy.png differ diff --git a/assets/images/game/cutscenes/weeb/spiritFaceForward.png b/assets/images/game/cutscenes/weeb/spiritFaceForward.png index 93c7ed04..9bf698ed 100644 Binary files a/assets/images/game/cutscenes/weeb/spiritFaceForward.png and b/assets/images/game/cutscenes/weeb/spiritFaceForward.png differ diff --git a/assets/images/game/go.png b/assets/images/game/go.png index 78cc7f68..d82c6b6d 100644 Binary files a/assets/images/game/go.png and b/assets/images/game/go.png differ diff --git a/assets/images/game/healthBar.png b/assets/images/game/healthBar.png index 8bfec941..08babb89 100644 Binary files a/assets/images/game/healthBar.png and b/assets/images/game/healthBar.png differ diff --git a/assets/images/game/notes/default.png b/assets/images/game/notes/default.png index 6cd38f99..3201fe30 100644 Binary files a/assets/images/game/notes/default.png and b/assets/images/game/notes/default.png differ diff --git a/assets/images/game/ready.png b/assets/images/game/ready.png index 1ee103df..d6e8d062 100644 Binary files a/assets/images/game/ready.png and b/assets/images/game/ready.png differ diff --git a/assets/images/game/restart.png b/assets/images/game/restart.png index 82c210d0..6ae807a3 100644 Binary files a/assets/images/game/restart.png and b/assets/images/game/restart.png differ diff --git a/assets/images/game/score/bad.png b/assets/images/game/score/bad.png index 7b7c7398..47d81323 100644 Binary files a/assets/images/game/score/bad.png and b/assets/images/game/score/bad.png differ diff --git a/assets/images/game/score/combo.png b/assets/images/game/score/combo.png index ffc94f44..450b7a11 100644 Binary files a/assets/images/game/score/combo.png and b/assets/images/game/score/combo.png differ diff --git a/assets/images/game/score/good.png b/assets/images/game/score/good.png index d6c8f386..0a9f4551 100644 Binary files a/assets/images/game/score/good.png and b/assets/images/game/score/good.png differ diff --git a/assets/images/game/score/num0.png b/assets/images/game/score/num0.png index 83958371..b3513a75 100644 Binary files a/assets/images/game/score/num0.png and b/assets/images/game/score/num0.png differ diff --git a/assets/images/game/score/num1.png b/assets/images/game/score/num1.png index 52452ae1..3255408c 100644 Binary files a/assets/images/game/score/num1.png and b/assets/images/game/score/num1.png differ diff --git a/assets/images/game/score/num2.png b/assets/images/game/score/num2.png index 2db35d5b..11df1c9a 100644 Binary files a/assets/images/game/score/num2.png and b/assets/images/game/score/num2.png differ diff --git a/assets/images/game/score/num3.png b/assets/images/game/score/num3.png index a6b32303..c2842efa 100644 Binary files a/assets/images/game/score/num3.png and b/assets/images/game/score/num3.png differ diff --git a/assets/images/game/score/num4.png b/assets/images/game/score/num4.png index 24f99bda..72234aef 100644 Binary files a/assets/images/game/score/num4.png and b/assets/images/game/score/num4.png differ diff --git a/assets/images/game/score/num5.png b/assets/images/game/score/num5.png index 542423eb..1b16e1a8 100644 Binary files a/assets/images/game/score/num5.png and b/assets/images/game/score/num5.png differ diff --git a/assets/images/game/score/num6.png b/assets/images/game/score/num6.png index c6044194..498ce3b0 100644 Binary files a/assets/images/game/score/num6.png and b/assets/images/game/score/num6.png differ diff --git a/assets/images/game/score/num7.png b/assets/images/game/score/num7.png index 8b2d5498..4a2ce04a 100644 Binary files a/assets/images/game/score/num7.png and b/assets/images/game/score/num7.png differ diff --git a/assets/images/game/score/num8.png b/assets/images/game/score/num8.png index db61c3e9..c7a337ea 100644 Binary files a/assets/images/game/score/num8.png and b/assets/images/game/score/num8.png differ diff --git a/assets/images/game/score/num9.png b/assets/images/game/score/num9.png index 607f9ea7..e3ae145c 100644 Binary files a/assets/images/game/score/num9.png and b/assets/images/game/score/num9.png differ diff --git a/assets/images/game/score/shit.png b/assets/images/game/score/shit.png index 346141ad..4b2450b8 100644 Binary files a/assets/images/game/score/shit.png and b/assets/images/game/score/shit.png differ diff --git a/assets/images/game/score/sick.png b/assets/images/game/score/sick.png index 5cbba1e2..2a3aacf4 100644 Binary files a/assets/images/game/score/sick.png and b/assets/images/game/score/sick.png differ diff --git a/assets/images/game/set.png b/assets/images/game/set.png index a0450751..09eabfa0 100644 Binary files a/assets/images/game/set.png and b/assets/images/game/set.png differ diff --git a/assets/images/game/splashes/default.png b/assets/images/game/splashes/default.png index e93fdcb7..3cd0fc4a 100644 Binary files a/assets/images/game/splashes/default.png and b/assets/images/game/splashes/default.png differ diff --git a/assets/images/game/splashes/secret.png b/assets/images/game/splashes/secret.png index f0eccb47..5e9cdf2d 100644 Binary files a/assets/images/game/splashes/secret.png and b/assets/images/game/splashes/secret.png differ diff --git a/assets/images/icons/bf-old.png b/assets/images/icons/bf-old.png deleted file mode 100644 index b8369aca..00000000 Binary files a/assets/images/icons/bf-old.png and /dev/null differ diff --git a/assets/images/icons/bf-old/icon.png b/assets/images/icons/bf-old/icon.png new file mode 100644 index 00000000..df350d4a Binary files /dev/null and b/assets/images/icons/bf-old/icon.png differ diff --git a/assets/images/icons/bf-pixel.png b/assets/images/icons/bf-pixel.png deleted file mode 100644 index 1351f810..00000000 Binary files a/assets/images/icons/bf-pixel.png and /dev/null differ diff --git a/assets/images/icons/bf-pixel/data.xml b/assets/images/icons/bf-pixel/data.xml new file mode 100644 index 00000000..57653cad --- /dev/null +++ b/assets/images/icons/bf-pixel/data.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assets/images/icons/bf-pixel/icon.png b/assets/images/icons/bf-pixel/icon.png new file mode 100644 index 00000000..d1ae0f87 Binary files /dev/null and b/assets/images/icons/bf-pixel/icon.png differ diff --git a/assets/images/icons/bf.png b/assets/images/icons/bf.png deleted file mode 100644 index ab0903a7..00000000 Binary files a/assets/images/icons/bf.png and /dev/null differ diff --git a/assets/images/icons/bf/icon.png b/assets/images/icons/bf/icon.png new file mode 100644 index 00000000..53f9ccaa Binary files /dev/null and b/assets/images/icons/bf/icon.png differ diff --git a/assets/images/icons/dad.png b/assets/images/icons/dad.png deleted file mode 100644 index ffdf4ac1..00000000 Binary files a/assets/images/icons/dad.png and /dev/null differ diff --git a/assets/images/icons/dad/icon.png b/assets/images/icons/dad/icon.png new file mode 100644 index 00000000..cffed6f4 Binary files /dev/null and b/assets/images/icons/dad/icon.png differ diff --git a/assets/images/icons/face.png b/assets/images/icons/face.png deleted file mode 100644 index 622eb6dc..00000000 Binary files a/assets/images/icons/face.png and /dev/null differ diff --git a/assets/images/icons/face/icon.png b/assets/images/icons/face/icon.png new file mode 100644 index 00000000..bc24a5f8 Binary files /dev/null and b/assets/images/icons/face/icon.png differ diff --git a/assets/images/icons/gf-pixel.png b/assets/images/icons/gf-pixel.png deleted file mode 100644 index 5122ddc0..00000000 Binary files a/assets/images/icons/gf-pixel.png and /dev/null differ diff --git a/assets/images/icons/gf-pixel/data.xml b/assets/images/icons/gf-pixel/data.xml new file mode 100644 index 00000000..57653cad --- /dev/null +++ b/assets/images/icons/gf-pixel/data.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assets/images/icons/gf-pixel/icon.png b/assets/images/icons/gf-pixel/icon.png new file mode 100644 index 00000000..c2a6bdf6 Binary files /dev/null and b/assets/images/icons/gf-pixel/icon.png differ diff --git a/assets/images/icons/gf.png b/assets/images/icons/gf.png deleted file mode 100644 index ee837c1d..00000000 Binary files a/assets/images/icons/gf.png and /dev/null differ diff --git a/assets/images/icons/gf/icon.png b/assets/images/icons/gf/icon.png new file mode 100644 index 00000000..1493d177 Binary files /dev/null and b/assets/images/icons/gf/icon.png differ diff --git a/assets/images/icons/mom.png b/assets/images/icons/mom.png deleted file mode 100644 index 1179e39b..00000000 Binary files a/assets/images/icons/mom.png and /dev/null differ diff --git a/assets/images/icons/mom/icon.png b/assets/images/icons/mom/icon.png new file mode 100644 index 00000000..793d846c Binary files /dev/null and b/assets/images/icons/mom/icon.png differ diff --git a/assets/images/icons/monster.png b/assets/images/icons/monster.png deleted file mode 100644 index a7195ca3..00000000 Binary files a/assets/images/icons/monster.png and /dev/null differ diff --git a/assets/images/icons/monster/icon.png b/assets/images/icons/monster/icon.png new file mode 100644 index 00000000..525effd8 Binary files /dev/null and b/assets/images/icons/monster/icon.png differ diff --git a/assets/images/icons/parents-christmas.png b/assets/images/icons/parents-christmas.png deleted file mode 100644 index 64efb7fd..00000000 Binary files a/assets/images/icons/parents-christmas.png and /dev/null differ diff --git a/assets/images/icons/parents-christmas/icon.png b/assets/images/icons/parents-christmas/icon.png new file mode 100644 index 00000000..74e06a3d Binary files /dev/null and b/assets/images/icons/parents-christmas/icon.png differ diff --git a/assets/images/icons/pico.png b/assets/images/icons/pico.png deleted file mode 100644 index c159e236..00000000 Binary files a/assets/images/icons/pico.png and /dev/null differ diff --git a/assets/images/icons/pico/icon.png b/assets/images/icons/pico/icon.png new file mode 100644 index 00000000..3a6322e7 Binary files /dev/null and b/assets/images/icons/pico/icon.png differ diff --git a/assets/images/icons/senpai.png b/assets/images/icons/senpai.png deleted file mode 100644 index 22d0881b..00000000 Binary files a/assets/images/icons/senpai.png and /dev/null differ diff --git a/assets/images/icons/senpai/data.xml b/assets/images/icons/senpai/data.xml new file mode 100644 index 00000000..57653cad --- /dev/null +++ b/assets/images/icons/senpai/data.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assets/images/icons/senpai/icon.png b/assets/images/icons/senpai/icon.png new file mode 100644 index 00000000..0064cf42 Binary files /dev/null and b/assets/images/icons/senpai/icon.png differ diff --git a/assets/images/icons/spirit.png b/assets/images/icons/spirit.png deleted file mode 100644 index c4ccb2a9..00000000 Binary files a/assets/images/icons/spirit.png and /dev/null differ diff --git a/assets/images/icons/spirit/data.xml b/assets/images/icons/spirit/data.xml new file mode 100644 index 00000000..57653cad --- /dev/null +++ b/assets/images/icons/spirit/data.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/assets/images/icons/spirit/icon.png b/assets/images/icons/spirit/icon.png new file mode 100644 index 00000000..e4cb0fd7 Binary files /dev/null and b/assets/images/icons/spirit/icon.png differ diff --git a/assets/images/icons/spooky.png b/assets/images/icons/spooky.png deleted file mode 100644 index 062eb196..00000000 Binary files a/assets/images/icons/spooky.png and /dev/null differ diff --git a/assets/images/icons/spooky/icon.png b/assets/images/icons/spooky/icon.png new file mode 100644 index 00000000..01534020 Binary files /dev/null and b/assets/images/icons/spooky/icon.png differ diff --git a/assets/images/icons/tankman.png b/assets/images/icons/tankman.png deleted file mode 100644 index d5d4ba46..00000000 Binary files a/assets/images/icons/tankman.png and /dev/null differ diff --git a/assets/images/icons/tankman/icon.png b/assets/images/icons/tankman/icon.png new file mode 100644 index 00000000..dafda943 Binary files /dev/null and b/assets/images/icons/tankman/icon.png differ diff --git a/assets/images/menus/alphabet-bold.png b/assets/images/menus/alphabet-bold.png index 012721ce..531254d2 100644 Binary files a/assets/images/menus/alphabet-bold.png and b/assets/images/menus/alphabet-bold.png differ diff --git a/assets/images/menus/alphabet.png b/assets/images/menus/alphabet.png index 545e3505..a35a7449 100644 Binary files a/assets/images/menus/alphabet.png and b/assets/images/menus/alphabet.png differ diff --git a/assets/images/menus/mainmenu/freeplay.png b/assets/images/menus/mainmenu/freeplay.png index 361ddfa7..2e4bb436 100644 Binary files a/assets/images/menus/mainmenu/freeplay.png and b/assets/images/menus/mainmenu/freeplay.png differ diff --git a/assets/images/menus/mainmenu/options.png b/assets/images/menus/mainmenu/options.png index 20688ecc..abaa54ef 100644 Binary files a/assets/images/menus/mainmenu/options.png and b/assets/images/menus/mainmenu/options.png differ diff --git a/assets/images/menus/menuBG.png b/assets/images/menus/menuBG.png index 35458e80..896f0c11 100644 Binary files a/assets/images/menus/menuBG.png and b/assets/images/menus/menuBG.png differ diff --git a/assets/images/menus/menuBGBlue.png b/assets/images/menus/menuBGBlue.png index d3b42842..273fc2e3 100644 Binary files a/assets/images/menus/menuBGBlue.png and b/assets/images/menus/menuBGBlue.png differ diff --git a/assets/images/menus/menuBGMagenta.png b/assets/images/menus/menuBGMagenta.png index 96794ec8..2046067f 100644 Binary files a/assets/images/menus/menuBGMagenta.png and b/assets/images/menus/menuBGMagenta.png differ diff --git a/assets/images/menus/menuDesat.png b/assets/images/menus/menuDesat.png index 65759840..9f440340 100644 Binary files a/assets/images/menus/menuDesat.png and b/assets/images/menus/menuDesat.png differ diff --git a/assets/images/menus/menuEditors.png b/assets/images/menus/menuEditors.png index d6afefd1..84d40b18 100644 Binary files a/assets/images/menus/menuEditors.png and b/assets/images/menus/menuEditors.png differ diff --git a/assets/images/menus/menuTransparent.png b/assets/images/menus/menuTransparent.png index 8ea4883b..d6b275fb 100644 Binary files a/assets/images/menus/menuTransparent.png and b/assets/images/menus/menuTransparent.png differ diff --git a/assets/images/menus/options/checkboxThingie.png b/assets/images/menus/options/checkboxThingie.png index ffdf3d30..cf089f6c 100644 Binary files a/assets/images/menus/options/checkboxThingie.png and b/assets/images/menus/options/checkboxThingie.png differ diff --git a/assets/images/menus/pauseAlt/bfLol.png b/assets/images/menus/pauseAlt/bfLol.png index bfbdb86a..993618ba 100644 Binary files a/assets/images/menus/pauseAlt/bfLol.png and b/assets/images/menus/pauseAlt/bfLol.png differ diff --git a/assets/images/menus/pauseAlt/pauseBG.png b/assets/images/menus/pauseAlt/pauseBG.png index 5b5a77c6..32d0d92c 100644 Binary files a/assets/images/menus/pauseAlt/pauseBG.png and b/assets/images/menus/pauseAlt/pauseBG.png differ diff --git a/assets/images/menus/pauseAlt/pauseUI.png b/assets/images/menus/pauseAlt/pauseUI.png index b395863b..63b22e75 100644 Binary files a/assets/images/menus/pauseAlt/pauseUI.png and b/assets/images/menus/pauseAlt/pauseUI.png differ diff --git a/assets/images/menus/storymenu/assets.png b/assets/images/menus/storymenu/assets.png index 914229b6..097c8391 100644 Binary files a/assets/images/menus/storymenu/assets.png and b/assets/images/menus/storymenu/assets.png differ diff --git a/assets/images/menus/storymenu/characters/bf.png b/assets/images/menus/storymenu/characters/bf.png index 74713c3d..48da8dff 100644 Binary files a/assets/images/menus/storymenu/characters/bf.png and b/assets/images/menus/storymenu/characters/bf.png differ diff --git a/assets/images/menus/storymenu/characters/dad.png b/assets/images/menus/storymenu/characters/dad.png index d63e4537..d571774f 100644 Binary files a/assets/images/menus/storymenu/characters/dad.png and b/assets/images/menus/storymenu/characters/dad.png differ diff --git a/assets/images/menus/storymenu/characters/gf.png b/assets/images/menus/storymenu/characters/gf.png index 8ad08471..ae3093ef 100644 Binary files a/assets/images/menus/storymenu/characters/gf.png and b/assets/images/menus/storymenu/characters/gf.png differ diff --git a/assets/images/menus/storymenu/characters/mom.png b/assets/images/menus/storymenu/characters/mom.png index 5ff1badf..57c4ed03 100644 Binary files a/assets/images/menus/storymenu/characters/mom.png and b/assets/images/menus/storymenu/characters/mom.png differ diff --git a/assets/images/menus/storymenu/characters/parents-christmas.png b/assets/images/menus/storymenu/characters/parents-christmas.png index 7f081ede..a8b27578 100644 Binary files a/assets/images/menus/storymenu/characters/parents-christmas.png and b/assets/images/menus/storymenu/characters/parents-christmas.png differ diff --git a/assets/images/menus/storymenu/characters/senpai.png b/assets/images/menus/storymenu/characters/senpai.png index 8fe57d22..ab2366fa 100644 Binary files a/assets/images/menus/storymenu/characters/senpai.png and b/assets/images/menus/storymenu/characters/senpai.png differ diff --git a/assets/images/menus/storymenu/characters/spooky.png b/assets/images/menus/storymenu/characters/spooky.png index e9bc3a15..8b6fc801 100644 Binary files a/assets/images/menus/storymenu/characters/spooky.png and b/assets/images/menus/storymenu/characters/spooky.png differ diff --git a/assets/images/menus/storymenu/characters/tankman.png b/assets/images/menus/storymenu/characters/tankman.png index 26f26dca..ec9fc55a 100644 Binary files a/assets/images/menus/storymenu/characters/tankman.png and b/assets/images/menus/storymenu/characters/tankman.png differ diff --git a/assets/images/menus/storymenu/difficulties/easy.png b/assets/images/menus/storymenu/difficulties/easy.png index e73783ad..b0941dd8 100644 Binary files a/assets/images/menus/storymenu/difficulties/easy.png and b/assets/images/menus/storymenu/difficulties/easy.png differ diff --git a/assets/images/menus/storymenu/difficulties/hard.png b/assets/images/menus/storymenu/difficulties/hard.png index 8c90f062..4c167dbc 100644 Binary files a/assets/images/menus/storymenu/difficulties/hard.png and b/assets/images/menus/storymenu/difficulties/hard.png differ diff --git a/assets/images/menus/storymenu/difficulties/normal.png b/assets/images/menus/storymenu/difficulties/normal.png index 8acd14e9..81958d5d 100644 Binary files a/assets/images/menus/storymenu/difficulties/normal.png and b/assets/images/menus/storymenu/difficulties/normal.png differ diff --git a/assets/images/menus/storymenu/weeks/tutorial.png b/assets/images/menus/storymenu/weeks/tutorial.png index 7ef58a55..17c960f8 100644 Binary files a/assets/images/menus/storymenu/weeks/tutorial.png and b/assets/images/menus/storymenu/weeks/tutorial.png differ diff --git a/assets/images/menus/storymenu/weeks/week1.png b/assets/images/menus/storymenu/weeks/week1.png index 9421bd85..f3246757 100644 Binary files a/assets/images/menus/storymenu/weeks/week1.png and b/assets/images/menus/storymenu/weeks/week1.png differ diff --git a/assets/images/menus/storymenu/weeks/week2.png b/assets/images/menus/storymenu/weeks/week2.png index 0b76c032..20edbaae 100644 Binary files a/assets/images/menus/storymenu/weeks/week2.png and b/assets/images/menus/storymenu/weeks/week2.png differ diff --git a/assets/images/menus/storymenu/weeks/week3.png b/assets/images/menus/storymenu/weeks/week3.png index 4a331b15..c1b43251 100644 Binary files a/assets/images/menus/storymenu/weeks/week3.png and b/assets/images/menus/storymenu/weeks/week3.png differ diff --git a/assets/images/menus/storymenu/weeks/week4.png b/assets/images/menus/storymenu/weeks/week4.png index 7122f979..654acecb 100644 Binary files a/assets/images/menus/storymenu/weeks/week4.png and b/assets/images/menus/storymenu/weeks/week4.png differ diff --git a/assets/images/menus/storymenu/weeks/week5.png b/assets/images/menus/storymenu/weeks/week5.png index 1a3a02a8..fe548757 100644 Binary files a/assets/images/menus/storymenu/weeks/week5.png and b/assets/images/menus/storymenu/weeks/week5.png differ diff --git a/assets/images/menus/storymenu/weeks/week6.png b/assets/images/menus/storymenu/weeks/week6.png index 0e02a8b1..1eb70de1 100644 Binary files a/assets/images/menus/storymenu/weeks/week6.png and b/assets/images/menus/storymenu/weeks/week6.png differ diff --git a/assets/images/menus/storymenu/weeks/week7.png b/assets/images/menus/storymenu/weeks/week7.png index 6b526d3f..8960961f 100644 Binary files a/assets/images/menus/storymenu/weeks/week7.png and b/assets/images/menus/storymenu/weeks/week7.png differ diff --git a/assets/images/menus/titlescreen/gf.png b/assets/images/menus/titlescreen/gf.png index 2705f13d..2208cd48 100644 Binary files a/assets/images/menus/titlescreen/gf.png and b/assets/images/menus/titlescreen/gf.png differ diff --git a/assets/images/menus/titlescreen/newgrounds_logo.png b/assets/images/menus/titlescreen/newgrounds_logo.png index 26948b66..00e18517 100644 Binary files a/assets/images/menus/titlescreen/newgrounds_logo.png and b/assets/images/menus/titlescreen/newgrounds_logo.png differ diff --git a/assets/images/menus/titlescreen/titleEnter.png b/assets/images/menus/titlescreen/titleEnter.png index ad1a9f5f..957e5b75 100644 Binary files a/assets/images/menus/titlescreen/titleEnter.png and b/assets/images/menus/titlescreen/titleEnter.png differ diff --git a/assets/images/menus/transitionSpr.png b/assets/images/menus/transitionSpr.png index 0044c064..3b973a46 100644 Binary files a/assets/images/menus/transitionSpr.png and b/assets/images/menus/transitionSpr.png differ diff --git a/assets/images/menus/update/bf.png b/assets/images/menus/update/bf.png index 975f3d1a..bba8bbf6 100644 Binary files a/assets/images/menus/update/bf.png and b/assets/images/menus/update/bf.png differ diff --git a/assets/images/stages/default/stageback.png b/assets/images/stages/default/stageback.png index f0e69407..3baea3d2 100644 Binary files a/assets/images/stages/default/stageback.png and b/assets/images/stages/default/stageback.png differ diff --git a/assets/images/stages/default/stagecurtains.png b/assets/images/stages/default/stagecurtains.png index de33c06e..58574ae4 100644 Binary files a/assets/images/stages/default/stagecurtains.png and b/assets/images/stages/default/stagecurtains.png differ diff --git a/assets/images/stages/default/stagefront.png b/assets/images/stages/default/stagefront.png index 14790298..8176a71f 100644 Binary files a/assets/images/stages/default/stagefront.png and b/assets/images/stages/default/stagefront.png differ diff --git a/assets/images/stages/limo/bgLimo.png b/assets/images/stages/limo/bgLimo.png index 579862d0..54b58d04 100644 Binary files a/assets/images/stages/limo/bgLimo.png and b/assets/images/stages/limo/bgLimo.png differ diff --git a/assets/images/stages/limo/dumb.png b/assets/images/stages/limo/dumb.png index 25ea6651..62516171 100644 Binary files a/assets/images/stages/limo/dumb.png and b/assets/images/stages/limo/dumb.png differ diff --git a/assets/images/stages/limo/fastCarLol.png b/assets/images/stages/limo/fastCarLol.png index 392bc8e4..66be24ad 100644 Binary files a/assets/images/stages/limo/fastCarLol.png and b/assets/images/stages/limo/fastCarLol.png differ diff --git a/assets/images/stages/limo/limoDrive.png b/assets/images/stages/limo/limoDrive.png index 7ba8dd90..a7fd6db6 100644 Binary files a/assets/images/stages/limo/limoDrive.png and b/assets/images/stages/limo/limoDrive.png differ diff --git a/assets/images/stages/limo/limoOverlay.png b/assets/images/stages/limo/limoOverlay.png index 24ede11c..d44db7ca 100644 Binary files a/assets/images/stages/limo/limoOverlay.png and b/assets/images/stages/limo/limoOverlay.png differ diff --git a/assets/images/stages/limo/limoSunset.png b/assets/images/stages/limo/limoSunset.png index dddf07b2..e1c2c23a 100644 Binary files a/assets/images/stages/limo/limoSunset.png and b/assets/images/stages/limo/limoSunset.png differ diff --git a/assets/images/stages/mall/bgEscalator.png b/assets/images/stages/mall/bgEscalator.png index 8cff7edf..6cbae26c 100644 Binary files a/assets/images/stages/mall/bgEscalator.png and b/assets/images/stages/mall/bgEscalator.png differ diff --git a/assets/images/stages/mall/bgWalls.png b/assets/images/stages/mall/bgWalls.png index 0222b8cf..b17430a2 100644 Binary files a/assets/images/stages/mall/bgWalls.png and b/assets/images/stages/mall/bgWalls.png differ diff --git a/assets/images/stages/mall/bottomBop.png b/assets/images/stages/mall/bottomBop.png index d952e8e8..30cfe478 100644 Binary files a/assets/images/stages/mall/bottomBop.png and b/assets/images/stages/mall/bottomBop.png differ diff --git a/assets/images/stages/mall/christmasWall.png b/assets/images/stages/mall/christmasWall.png index 0ac5c218..906162e4 100644 Binary files a/assets/images/stages/mall/christmasWall.png and b/assets/images/stages/mall/christmasWall.png differ diff --git a/assets/images/stages/mall/evilBG.png b/assets/images/stages/mall/evilBG.png index ea087576..e7250675 100644 Binary files a/assets/images/stages/mall/evilBG.png and b/assets/images/stages/mall/evilBG.png differ diff --git a/assets/images/stages/mall/evilSnow.png b/assets/images/stages/mall/evilSnow.png index ff420ce6..912030a8 100644 Binary files a/assets/images/stages/mall/evilSnow.png and b/assets/images/stages/mall/evilSnow.png differ diff --git a/assets/images/stages/mall/fgSnow.png b/assets/images/stages/mall/fgSnow.png index 66091f02..c65ea057 100644 Binary files a/assets/images/stages/mall/fgSnow.png and b/assets/images/stages/mall/fgSnow.png differ diff --git a/assets/images/stages/mall/santa.png b/assets/images/stages/mall/santa.png index 58c1efad..4399b51c 100644 Binary files a/assets/images/stages/mall/santa.png and b/assets/images/stages/mall/santa.png differ diff --git a/assets/images/stages/mall/upperBop.png b/assets/images/stages/mall/upperBop.png index d3741eb3..2296eb0b 100644 Binary files a/assets/images/stages/mall/upperBop.png and b/assets/images/stages/mall/upperBop.png differ diff --git a/assets/images/stages/philly/behindTrain.png b/assets/images/stages/philly/behindTrain.png index fe5affe5..4df2fe41 100644 Binary files a/assets/images/stages/philly/behindTrain.png and b/assets/images/stages/philly/behindTrain.png differ diff --git a/assets/images/stages/philly/city.png b/assets/images/stages/philly/city.png index 46c44b20..e54e214a 100644 Binary files a/assets/images/stages/philly/city.png and b/assets/images/stages/philly/city.png differ diff --git a/assets/images/stages/philly/sky.png b/assets/images/stages/philly/sky.png index cf502063..95bf430e 100644 Binary files a/assets/images/stages/philly/sky.png and b/assets/images/stages/philly/sky.png differ diff --git a/assets/images/stages/philly/street.png b/assets/images/stages/philly/street.png index 14fd87bd..93082101 100644 Binary files a/assets/images/stages/philly/street.png and b/assets/images/stages/philly/street.png differ diff --git a/assets/images/stages/philly/train.png b/assets/images/stages/philly/train.png index cbee2da7..85b43f99 100644 Binary files a/assets/images/stages/philly/train.png and b/assets/images/stages/philly/train.png differ diff --git a/assets/images/stages/philly/win.png b/assets/images/stages/philly/win.png index 4e0091d6..dd8a43f7 100644 Binary files a/assets/images/stages/philly/win.png and b/assets/images/stages/philly/win.png differ diff --git a/assets/images/stages/school/animatedEvilSchool.png b/assets/images/stages/school/animatedEvilSchool.png index bc98fffc..9b2a1348 100644 Binary files a/assets/images/stages/school/animatedEvilSchool.png and b/assets/images/stages/school/animatedEvilSchool.png differ diff --git a/assets/images/stages/school/bgFreaks.png b/assets/images/stages/school/bgFreaks.png index db53cb20..253ed427 100644 Binary files a/assets/images/stages/school/bgFreaks.png and b/assets/images/stages/school/bgFreaks.png differ diff --git a/assets/images/stages/school/pause/bg.png b/assets/images/stages/school/pause/bg.png index 44d136cc..80c41a70 100644 Binary files a/assets/images/stages/school/pause/bg.png and b/assets/images/stages/school/pause/bg.png differ diff --git a/assets/images/stages/school/petals.png b/assets/images/stages/school/petals.png index 9ec86f70..49c4a766 100644 Binary files a/assets/images/stages/school/petals.png and b/assets/images/stages/school/petals.png differ diff --git a/assets/images/stages/school/ui/arrowEnds.png b/assets/images/stages/school/ui/arrowEnds.png index 9d4dc874..6c645fb1 100644 Binary files a/assets/images/stages/school/ui/arrowEnds.png and b/assets/images/stages/school/ui/arrowEnds.png differ diff --git a/assets/images/stages/school/ui/arrows-pixels.png b/assets/images/stages/school/ui/arrows-pixels.png index 235cdf2e..a1a7fd09 100644 Binary files a/assets/images/stages/school/ui/arrows-pixels.png and b/assets/images/stages/school/ui/arrows-pixels.png differ diff --git a/assets/images/stages/school/ui/bad.png b/assets/images/stages/school/ui/bad.png index 364c8ac4..f74abdb6 100644 Binary files a/assets/images/stages/school/ui/bad.png and b/assets/images/stages/school/ui/bad.png differ diff --git a/assets/images/stages/school/ui/combo.png b/assets/images/stages/school/ui/combo.png index 154244a1..ee368626 100644 Binary files a/assets/images/stages/school/ui/combo.png and b/assets/images/stages/school/ui/combo.png differ diff --git a/assets/images/stages/school/ui/go.png b/assets/images/stages/school/ui/go.png index ee331059..c754a97d 100644 Binary files a/assets/images/stages/school/ui/go.png and b/assets/images/stages/school/ui/go.png differ diff --git a/assets/images/stages/school/ui/good.png b/assets/images/stages/school/ui/good.png index 302986dc..a5eba2b7 100644 Binary files a/assets/images/stages/school/ui/good.png and b/assets/images/stages/school/ui/good.png differ diff --git a/assets/images/stages/school/ui/hand_textbox.png b/assets/images/stages/school/ui/hand_textbox.png index 5b4f1361..0ba7fd43 100644 Binary files a/assets/images/stages/school/ui/hand_textbox.png and b/assets/images/stages/school/ui/hand_textbox.png differ diff --git a/assets/images/stages/school/ui/num0.png b/assets/images/stages/school/ui/num0.png index d002c36c..e3ed8016 100644 Binary files a/assets/images/stages/school/ui/num0.png and b/assets/images/stages/school/ui/num0.png differ diff --git a/assets/images/stages/school/ui/num1.png b/assets/images/stages/school/ui/num1.png index 45a78d5b..a92cac9a 100644 Binary files a/assets/images/stages/school/ui/num1.png and b/assets/images/stages/school/ui/num1.png differ diff --git a/assets/images/stages/school/ui/num2.png b/assets/images/stages/school/ui/num2.png index 29396f94..12941da6 100644 Binary files a/assets/images/stages/school/ui/num2.png and b/assets/images/stages/school/ui/num2.png differ diff --git a/assets/images/stages/school/ui/num3.png b/assets/images/stages/school/ui/num3.png index 00dde82e..3e60295e 100644 Binary files a/assets/images/stages/school/ui/num3.png and b/assets/images/stages/school/ui/num3.png differ diff --git a/assets/images/stages/school/ui/num4.png b/assets/images/stages/school/ui/num4.png index d3be8b8c..1f63ea86 100644 Binary files a/assets/images/stages/school/ui/num4.png and b/assets/images/stages/school/ui/num4.png differ diff --git a/assets/images/stages/school/ui/num5.png b/assets/images/stages/school/ui/num5.png index cde2a204..79bf20f1 100644 Binary files a/assets/images/stages/school/ui/num5.png and b/assets/images/stages/school/ui/num5.png differ diff --git a/assets/images/stages/school/ui/num6.png b/assets/images/stages/school/ui/num6.png index cea53eb9..76603795 100644 Binary files a/assets/images/stages/school/ui/num6.png and b/assets/images/stages/school/ui/num6.png differ diff --git a/assets/images/stages/school/ui/num7.png b/assets/images/stages/school/ui/num7.png index f43639e7..0306f3ec 100644 Binary files a/assets/images/stages/school/ui/num7.png and b/assets/images/stages/school/ui/num7.png differ diff --git a/assets/images/stages/school/ui/num8.png b/assets/images/stages/school/ui/num8.png index 63531c58..ed05add5 100644 Binary files a/assets/images/stages/school/ui/num8.png and b/assets/images/stages/school/ui/num8.png differ diff --git a/assets/images/stages/school/ui/num9.png b/assets/images/stages/school/ui/num9.png index 91b05b66..ec888d2e 100644 Binary files a/assets/images/stages/school/ui/num9.png and b/assets/images/stages/school/ui/num9.png differ diff --git a/assets/images/stages/school/ui/pixel-secret-splashes.png b/assets/images/stages/school/ui/pixel-secret-splashes.png index b686e686..53f3c9be 100644 Binary files a/assets/images/stages/school/ui/pixel-secret-splashes.png and b/assets/images/stages/school/ui/pixel-secret-splashes.png differ diff --git a/assets/images/stages/school/ui/pixel-splashes.png b/assets/images/stages/school/ui/pixel-splashes.png index 78a06c6a..3b864bee 100644 Binary files a/assets/images/stages/school/ui/pixel-splashes.png and b/assets/images/stages/school/ui/pixel-splashes.png differ diff --git a/assets/images/stages/school/ui/ready.png b/assets/images/stages/school/ui/ready.png index b2f1c039..ec220e74 100644 Binary files a/assets/images/stages/school/ui/ready.png and b/assets/images/stages/school/ui/ready.png differ diff --git a/assets/images/stages/school/ui/set.png b/assets/images/stages/school/ui/set.png index b73b8449..79f2166c 100644 Binary files a/assets/images/stages/school/ui/set.png and b/assets/images/stages/school/ui/set.png differ diff --git a/assets/images/stages/school/ui/shit.png b/assets/images/stages/school/ui/shit.png index 46f415eb..601266dd 100644 Binary files a/assets/images/stages/school/ui/shit.png and b/assets/images/stages/school/ui/shit.png differ diff --git a/assets/images/stages/school/ui/sick.png b/assets/images/stages/school/ui/sick.png index 3d7de624..f19dcded 100644 Binary files a/assets/images/stages/school/ui/sick.png and b/assets/images/stages/school/ui/sick.png differ diff --git a/assets/images/stages/school/weebSchool.png b/assets/images/stages/school/weebSchool.png index a54e6cc7..fb44fea7 100644 Binary files a/assets/images/stages/school/weebSchool.png and b/assets/images/stages/school/weebSchool.png differ diff --git a/assets/images/stages/school/weebSky.png b/assets/images/stages/school/weebSky.png index 4d7201d4..0e4933a8 100644 Binary files a/assets/images/stages/school/weebSky.png and b/assets/images/stages/school/weebSky.png differ diff --git a/assets/images/stages/school/weebStreet.png b/assets/images/stages/school/weebStreet.png index b8fc1247..cf90f5f4 100644 Binary files a/assets/images/stages/school/weebStreet.png and b/assets/images/stages/school/weebStreet.png differ diff --git a/assets/images/stages/school/weebTrees.png b/assets/images/stages/school/weebTrees.png index b62368f2..efb9e031 100644 Binary files a/assets/images/stages/school/weebTrees.png and b/assets/images/stages/school/weebTrees.png differ diff --git a/assets/images/stages/school/weebTreesBack.png b/assets/images/stages/school/weebTreesBack.png index 1a5fcbc2..78460fa2 100644 Binary files a/assets/images/stages/school/weebTreesBack.png and b/assets/images/stages/school/weebTreesBack.png differ diff --git a/assets/images/stages/spooky/halloween_bg.png b/assets/images/stages/spooky/halloween_bg.png index 2105bc83..8c85c8a7 100644 Binary files a/assets/images/stages/spooky/halloween_bg.png and b/assets/images/stages/spooky/halloween_bg.png differ diff --git a/assets/images/stages/tank/smokeLeft.png b/assets/images/stages/tank/smokeLeft.png index 37dc5781..98aee0ca 100644 Binary files a/assets/images/stages/tank/smokeLeft.png and b/assets/images/stages/tank/smokeLeft.png differ diff --git a/assets/images/stages/tank/smokeRight.png b/assets/images/stages/tank/smokeRight.png index d02890a3..a7cb19a7 100644 Binary files a/assets/images/stages/tank/smokeRight.png and b/assets/images/stages/tank/smokeRight.png differ diff --git a/assets/images/stages/tank/tankClouds.png b/assets/images/stages/tank/tankClouds.png index c0112da0..b6d5b7ce 100644 Binary files a/assets/images/stages/tank/tankClouds.png and b/assets/images/stages/tank/tankClouds.png differ diff --git a/assets/images/stages/tank/tankGround.png b/assets/images/stages/tank/tankGround.png index 956b9e9e..6b2c3231 100644 Binary files a/assets/images/stages/tank/tankGround.png and b/assets/images/stages/tank/tankGround.png differ diff --git a/assets/images/stages/tank/tankMountains.png b/assets/images/stages/tank/tankMountains.png index 48e4158b..bc361ab0 100644 Binary files a/assets/images/stages/tank/tankMountains.png and b/assets/images/stages/tank/tankMountains.png differ diff --git a/assets/images/stages/tank/tankRuins.png b/assets/images/stages/tank/tankRuins.png index 1169d7f4..59551099 100644 Binary files a/assets/images/stages/tank/tankRuins.png and b/assets/images/stages/tank/tankRuins.png differ diff --git a/assets/images/stages/tank/tankSky.png b/assets/images/stages/tank/tankSky.png index 9a0a6207..87cedea8 100644 Binary files a/assets/images/stages/tank/tankSky.png and b/assets/images/stages/tank/tankSky.png differ diff --git a/assets/images/stages/tank/tankmanKilled1.png b/assets/images/stages/tank/tankmanKilled1.png index c6553402..9ecf9ad4 100644 Binary files a/assets/images/stages/tank/tankmanKilled1.png and b/assets/images/stages/tank/tankmanKilled1.png differ diff --git a/assets/paths.json b/assets/paths.json deleted file mode 100644 index e69de29b..00000000 diff --git a/assets/songs/winter horrorland/charts/easy.json b/assets/songs/winter horrorland/charts/easy.json index 4ed4f5d9..983b5955 100644 --- a/assets/songs/winter horrorland/charts/easy.json +++ b/assets/songs/winter horrorland/charts/easy.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"Monster-christmas","speed":1.0,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23396.2266,0,0.0],[23773.584,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24905.66,2,0.0],[25283.02,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26415.0938,3,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29433.9629,0,0.0],[29811.32,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30943.3965,2,0.0],[31320.7539,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32452.83,3,0.0],[32830.1875,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33584.9063,2,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36603.7734,2,0.0],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39622.64,2,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42641.5078,2,0.0],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[46037.7344,2,0.0],[46415.0938,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,2,283.01886],[48301.8867,6,849.0566],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[52075.4727,2,0.0],[52452.832,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,283.01886],[54716.98,0,188.679245],[55094.34,3,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[58113.207,2,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[61132.0742,2,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,0,0.0],[66415.09,4,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,5,754.717],[67735.85,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[70188.68,1,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71320.76,1,0.0],[71698.11,2,0.0],[72075.47,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[76226.4141,1,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77358.49,1,0.0],[77735.85,2,0.0],[78113.21,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81886.79,3,0.0],[82264.15,0,0.0],[82641.51,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83396.23,3,0.0],[83773.5859,0,0.0],[84150.9453,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[88113.21,1,283.01886],[88301.88,4,0.0],[88679.24,7,283.01886],[88679.24,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,2,566.0377],[92075.47,6,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[94150.9453,7,0.0],[94339.625,0,283.01886],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,4,283.01886],[95849.0547,2,283.01886],[96226.4141,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96792.45,7,94.33962],[96981.13,6,0.0],[97169.81,3,94.33962],[97358.49,4,283.01886],[97358.49,2,0.0],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[100188.68,7,0.0],[100377.359,0,283.01886],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,4,283.01886],[101886.789,2,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103207.547,7,94.33962],[103396.227,0,283.01886],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,5,283.01886],[104150.945,0,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106415.094,2,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109433.961,2,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,4,0.0],[113207.547,3,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,4,0.0],[114716.984,0,0.0],[115283.016,3,0.0],[115283.016,7,0.0],[115660.375,6,0.0],[115660.375,2,0.0],[116037.734,1,754.717],[116037.734,5,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118490.563,1,0.0],[118867.922,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121509.438,1,0.0],[121886.789,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,2,0.0],[125283.016,5,283.01886],[125660.375,1,283.01886],[126037.734,3,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,6,0.0],[127735.852,2,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"monster-christmas","speed":1.0,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23396.2266,0,0.0],[23773.584,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24905.66,2,0.0],[25283.02,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26415.0938,3,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29433.9629,0,0.0],[29811.32,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30943.3965,2,0.0],[31320.7539,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32452.83,3,0.0],[32830.1875,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33584.9063,2,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36603.7734,2,0.0],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39622.64,2,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42641.5078,2,0.0],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[46037.7344,2,0.0],[46415.0938,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,2,283.01886],[48301.8867,6,849.0566],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[52075.4727,2,0.0],[52452.832,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,283.01886],[54716.98,0,188.679245],[55094.34,3,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[58113.207,2,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[61132.0742,2,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,0,0.0],[66415.09,4,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,5,754.717],[67735.85,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[70188.68,1,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71320.76,1,0.0],[71698.11,2,0.0],[72075.47,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[76226.4141,1,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77358.49,1,0.0],[77735.85,2,0.0],[78113.21,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81886.79,3,0.0],[82264.15,0,0.0],[82641.51,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83396.23,3,0.0],[83773.5859,0,0.0],[84150.9453,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[88113.21,1,283.01886],[88301.88,4,0.0],[88679.24,7,283.01886],[88679.24,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,2,566.0377],[92075.47,6,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[94150.9453,7,0.0],[94339.625,0,283.01886],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,4,283.01886],[95849.0547,2,283.01886],[96226.4141,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96792.45,7,94.33962],[96981.13,6,0.0],[97169.81,3,94.33962],[97358.49,4,283.01886],[97358.49,2,0.0],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[100188.68,7,0.0],[100377.359,0,283.01886],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,4,283.01886],[101886.789,2,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103207.547,7,94.33962],[103396.227,0,283.01886],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,5,283.01886],[104150.945,0,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106415.094,2,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109433.961,2,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,4,0.0],[113207.547,3,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,4,0.0],[114716.984,0,0.0],[115283.016,3,0.0],[115283.016,7,0.0],[115660.375,6,0.0],[115660.375,2,0.0],[116037.734,1,754.717],[116037.734,5,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118490.563,1,0.0],[118867.922,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121509.438,1,0.0],[121886.789,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,2,0.0],[125283.016,5,283.01886],[125660.375,1,283.01886],[126037.734,3,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,6,0.0],[127735.852,2,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} diff --git a/assets/songs/winter horrorland/charts/hard.json b/assets/songs/winter horrorland/charts/hard.json index fe118e9c..ce698c12 100644 --- a/assets/songs/winter horrorland/charts/hard.json +++ b/assets/songs/winter horrorland/charts/hard.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"Monster-christmas","speed":1.3,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886],[10188.68,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886],[13207.5469,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14150.9434,2,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886],[16226.415,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886],[19245.2832,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20188.68,2,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21509.4336,3,283.01886],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23207.5469,0,0.0],[23301.8867,2,0.0],[23396.2266,0,0.0],[23584.9063,3,0.0],[23773.584,0,0.0],[23962.2637,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24716.98,0,0.0],[24905.66,2,0.0],[25094.34,0,0.0],[25283.02,2,0.0],[25471.6973,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26226.416,2,0.0],[26415.0938,3,0.0],[26603.7734,2,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27547.17,3,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29245.2832,0,0.0],[29339.623,2,0.0],[29433.9629,0,0.0],[29622.64,3,0.0],[29811.32,0,0.0],[30000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30754.7168,0,0.0],[30943.3965,2,0.0],[31132.0762,0,0.0],[31320.7539,2,0.0],[31509.4336,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32264.15,2,0.0],[32452.83,3,0.0],[32641.51,2,0.0],[32830.1875,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33396.2266,3,0.0],[33490.5664,3,0.0],[33584.9063,2,0.0],[33773.5859,1,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36415.0938,3,94.33962],[36603.7734,2,94.33962],[36792.4531,1,94.33962],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39433.96,3,0.0],[39528.3,3,0.0],[39622.64,2,0.0],[39811.32,1,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42452.832,3,94.33962],[42641.5078,2,94.33962],[42830.1875,1,94.33962],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[45660.38,1,283.01886],[46037.7344,2,0.0],[46226.4141,3,0.0],[46415.0938,2,0.0],[46603.7734,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,6,849.0566],[48301.8867,2,283.01886],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49245.28,0,0.0],[49339.62,0,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[51698.1133,1,283.01886],[52075.4727,2,0.0],[52264.1523,3,0.0],[52452.832,2,0.0],[52641.5078,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,188.679245],[54716.98,0,188.679245],[55094.34,3,0.0],[55283.02,0,0.0],[55377.36,0,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[57924.5273,2,0.0],[58018.8672,3,0.0],[58113.207,2,0.0],[58301.8867,0,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59056.6055,0,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[59811.32,3,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[60943.3945,2,0.0],[61037.7344,3,0.0],[61132.0742,2,0.0],[61320.7539,0,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62075.4727,0,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[62830.1875,3,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63584.9063,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65094.34,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[65849.0547,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,4,0.0],[66415.09,0,0.0],[66603.77,5,0.0],[66603.77,1,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,1,754.717],[67735.85,5,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[69716.9844,0,0.0],[69811.32,3,0.0],[70188.68,1,0.0],[70377.36,0,0.0],[70566.04,2,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71226.4141,3,0.0],[71320.76,1,0.0],[71509.44,0,0.0],[71698.11,2,0.0],[71886.79,3,0.0],[72075.47,0,0.0],[72264.15,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74528.3047,0,0.0],[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[75754.72,0,0.0],[75849.0547,3,0.0],[76226.4141,1,0.0],[76415.09,0,0.0],[76603.77,2,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77264.15,3,0.0],[77358.49,1,0.0],[77547.17,0,0.0],[77735.85,2,0.0],[77924.53,3,0.0],[78113.21,0,0.0],[78301.88,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80566.04,0,0.0],[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81698.11,1,0.0],[81886.79,3,0.0],[82075.47,3,0.0],[82264.15,0,0.0],[82452.83,3,0.0],[82641.51,2,0.0],[82830.19,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83207.55,1,0.0],[83396.23,3,0.0],[83584.91,3,0.0],[83773.5859,0,0.0],[83962.2656,3,0.0],[84150.9453,2,0.0],[84339.625,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84716.9844,3,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85471.6953,3,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86226.4141,3,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[86981.13,3,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[87924.53,0,94.33962],[88113.21,1,283.01886],[88301.88,4,0.0],[88490.56,0,0.0],[88490.56,7,0.0],[88679.24,1,0.0],[88679.24,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90000.0,4,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,6,566.0377],[92075.47,2,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[93867.92,3,0.0],[93962.2656,2,0.0],[94150.9453,1,0.0],[94150.9453,7,0.0],[94245.28,7,0.0],[94339.625,0,283.01886],[94339.625,6,0.0],[94528.3047,5,0.0],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,2,283.01886],[95849.0547,4,283.01886],[96226.4141,0,283.01886],[96226.4141,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96603.77,3,0.0],[96792.45,7,94.33962],[96981.13,6,94.33962],[97169.81,5,94.33962],[97169.81,3,94.33962],[97358.49,4,283.01886],[97358.49,2,94.33962],[97547.17,1,94.33962],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[99905.6641,3,0.0],[100000.0,2,0.0],[100188.68,7,0.0],[100188.68,1,0.0],[100283.016,7,0.0],[100377.359,6,0.0],[100377.359,0,283.01886],[100566.039,5,0.0],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,2,283.01886],[101886.789,4,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103018.867,2,94.33962],[103207.547,7,94.33962],[103207.547,1,94.33962],[103396.227,0,283.01886],[103396.227,6,94.33962],[103584.906,5,94.33962],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,0,283.01886],[104150.945,5,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106226.414,2,0.0],[106320.758,3,0.0],[106415.094,2,0.0],[106603.773,0,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107358.492,0,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108113.211,3,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109245.281,2,0.0],[109339.625,3,0.0],[109433.961,2,0.0],[109622.641,0,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110377.359,0,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111132.078,3,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[111886.789,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,4,0.0],[113207.547,3,0.0],[113396.227,3,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114150.945,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,0,0.0],[114716.984,4,0.0],[114905.664,1,0.0],[114905.664,5,0.0],[115283.016,3,0.0],[115283.016,7,0.0],[115660.375,2,0.0],[115660.375,6,0.0],[116037.734,5,754.717],[116037.734,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118301.883,3,0.0],[118490.563,1,0.0],[118679.242,0,0.0],[118867.922,2,0.0],[119056.6,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121320.758,3,0.0],[121509.438,1,0.0],[121698.109,0,0.0],[121886.789,2,0.0],[122075.469,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124339.625,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,5,283.01886],[125283.016,2,0.0],[125471.695,3,0.0],[125660.375,1,283.01886],[126037.734,3,0.0],[126226.414,0,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[126981.133,1,0.0],[126981.133,5,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,2,0.0],[127735.852,6,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0],[129622.641,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"monster-christmas","speed":1.3,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886],[10188.68,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886],[13207.5469,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14150.9434,2,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886],[16226.415,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886],[19245.2832,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20188.68,2,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21509.4336,3,283.01886],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23207.5469,0,0.0],[23301.8867,2,0.0],[23396.2266,0,0.0],[23584.9063,3,0.0],[23773.584,0,0.0],[23962.2637,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24716.98,0,0.0],[24905.66,2,0.0],[25094.34,0,0.0],[25283.02,2,0.0],[25471.6973,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26226.416,2,0.0],[26415.0938,3,0.0],[26603.7734,2,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27547.17,3,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29245.2832,0,0.0],[29339.623,2,0.0],[29433.9629,0,0.0],[29622.64,3,0.0],[29811.32,0,0.0],[30000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30754.7168,0,0.0],[30943.3965,2,0.0],[31132.0762,0,0.0],[31320.7539,2,0.0],[31509.4336,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32264.15,2,0.0],[32452.83,3,0.0],[32641.51,2,0.0],[32830.1875,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33396.2266,3,0.0],[33490.5664,3,0.0],[33584.9063,2,0.0],[33773.5859,1,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36415.0938,3,94.33962],[36603.7734,2,94.33962],[36792.4531,1,94.33962],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39433.96,3,0.0],[39528.3,3,0.0],[39622.64,2,0.0],[39811.32,1,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42452.832,3,94.33962],[42641.5078,2,94.33962],[42830.1875,1,94.33962],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[45660.38,1,283.01886],[46037.7344,2,0.0],[46226.4141,3,0.0],[46415.0938,2,0.0],[46603.7734,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,6,849.0566],[48301.8867,2,283.01886],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49245.28,0,0.0],[49339.62,0,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[51698.1133,1,283.01886],[52075.4727,2,0.0],[52264.1523,3,0.0],[52452.832,2,0.0],[52641.5078,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,188.679245],[54716.98,0,188.679245],[55094.34,3,0.0],[55283.02,0,0.0],[55377.36,0,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[57924.5273,2,0.0],[58018.8672,3,0.0],[58113.207,2,0.0],[58301.8867,0,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59056.6055,0,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[59811.32,3,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[60943.3945,2,0.0],[61037.7344,3,0.0],[61132.0742,2,0.0],[61320.7539,0,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62075.4727,0,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[62830.1875,3,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63584.9063,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65094.34,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[65849.0547,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,4,0.0],[66415.09,0,0.0],[66603.77,5,0.0],[66603.77,1,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,1,754.717],[67735.85,5,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[69716.9844,0,0.0],[69811.32,3,0.0],[70188.68,1,0.0],[70377.36,0,0.0],[70566.04,2,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71226.4141,3,0.0],[71320.76,1,0.0],[71509.44,0,0.0],[71698.11,2,0.0],[71886.79,3,0.0],[72075.47,0,0.0],[72264.15,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74528.3047,0,0.0],[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[75754.72,0,0.0],[75849.0547,3,0.0],[76226.4141,1,0.0],[76415.09,0,0.0],[76603.77,2,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77264.15,3,0.0],[77358.49,1,0.0],[77547.17,0,0.0],[77735.85,2,0.0],[77924.53,3,0.0],[78113.21,0,0.0],[78301.88,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80566.04,0,0.0],[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81698.11,1,0.0],[81886.79,3,0.0],[82075.47,3,0.0],[82264.15,0,0.0],[82452.83,3,0.0],[82641.51,2,0.0],[82830.19,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83207.55,1,0.0],[83396.23,3,0.0],[83584.91,3,0.0],[83773.5859,0,0.0],[83962.2656,3,0.0],[84150.9453,2,0.0],[84339.625,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84716.9844,3,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85471.6953,3,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86226.4141,3,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[86981.13,3,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[87924.53,0,94.33962],[88113.21,1,283.01886],[88301.88,4,0.0],[88490.56,0,0.0],[88490.56,7,0.0],[88679.24,1,0.0],[88679.24,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90000.0,4,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,6,566.0377],[92075.47,2,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[93867.92,3,0.0],[93962.2656,2,0.0],[94150.9453,1,0.0],[94150.9453,7,0.0],[94245.28,7,0.0],[94339.625,0,283.01886],[94339.625,6,0.0],[94528.3047,5,0.0],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,2,283.01886],[95849.0547,4,283.01886],[96226.4141,0,283.01886],[96226.4141,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96603.77,3,0.0],[96792.45,7,94.33962],[96981.13,6,94.33962],[97169.81,5,94.33962],[97169.81,3,94.33962],[97358.49,4,283.01886],[97358.49,2,94.33962],[97547.17,1,94.33962],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[99905.6641,3,0.0],[100000.0,2,0.0],[100188.68,7,0.0],[100188.68,1,0.0],[100283.016,7,0.0],[100377.359,6,0.0],[100377.359,0,283.01886],[100566.039,5,0.0],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,2,283.01886],[101886.789,4,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103018.867,2,94.33962],[103207.547,7,94.33962],[103207.547,1,94.33962],[103396.227,0,283.01886],[103396.227,6,94.33962],[103584.906,5,94.33962],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,0,283.01886],[104150.945,5,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106226.414,2,0.0],[106320.758,3,0.0],[106415.094,2,0.0],[106603.773,0,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107358.492,0,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108113.211,3,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109245.281,2,0.0],[109339.625,3,0.0],[109433.961,2,0.0],[109622.641,0,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110377.359,0,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111132.078,3,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[111886.789,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,4,0.0],[113207.547,3,0.0],[113396.227,3,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114150.945,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,0,0.0],[114716.984,4,0.0],[114905.664,1,0.0],[114905.664,5,0.0],[115283.016,3,0.0],[115283.016,7,0.0],[115660.375,2,0.0],[115660.375,6,0.0],[116037.734,5,754.717],[116037.734,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118301.883,3,0.0],[118490.563,1,0.0],[118679.242,0,0.0],[118867.922,2,0.0],[119056.6,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121320.758,3,0.0],[121509.438,1,0.0],[121698.109,0,0.0],[121886.789,2,0.0],[122075.469,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124339.625,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,5,283.01886],[125283.016,2,0.0],[125471.695,3,0.0],[125660.375,1,283.01886],[126037.734,3,0.0],[126226.414,0,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[126981.133,1,0.0],[126981.133,5,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,2,0.0],[127735.852,6,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0],[129622.641,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} diff --git a/assets/songs/winter horrorland/charts/normal.json b/assets/songs/winter horrorland/charts/normal.json index a00dcf64..b1c4eda2 100644 --- a/assets/songs/winter horrorland/charts/normal.json +++ b/assets/songs/winter horrorland/charts/normal.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"Monster-christmas","speed":1.1,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886],[10188.68,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886],[13207.5469,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886],[16226.415,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886],[19245.2832,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21509.4336,3,283.01886],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23207.5469,0,0.0],[23396.2266,0,0.0],[23773.584,0,0.0],[23962.2637,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24905.66,2,0.0],[25283.02,2,0.0],[25471.6973,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26226.416,2,0.0],[26415.0938,3,0.0],[26603.7734,2,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27547.17,3,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29245.2832,0,0.0],[29433.9629,0,0.0],[29811.32,0,0.0],[30000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30943.3965,2,0.0],[31320.7539,2,0.0],[31509.4336,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32264.15,2,0.0],[32452.83,3,0.0],[32641.51,2,0.0],[32830.1875,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33396.2266,3,0.0],[33584.9063,2,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886],[35849.0547,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36603.7734,2,0.0],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39433.96,3,0.0],[39622.64,2,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886],[41886.793,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42641.5078,2,0.0],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[45660.38,1,283.01886],[46037.7344,2,0.0],[46415.0938,2,0.0],[46603.7734,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,2,283.01886],[48301.8867,6,849.0566],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49245.28,0,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[51698.1133,1,283.01886],[52075.4727,2,0.0],[52452.832,2,0.0],[52641.5078,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,188.679245],[54716.98,0,188.679245],[55094.34,3,0.0],[55283.02,0,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[57924.5273,2,0.0],[58113.207,2,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[60943.3945,2,0.0],[61132.0742,2,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,4,0.0],[66415.09,0,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,5,754.717],[67735.85,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[69811.32,3,0.0],[70188.68,1,0.0],[70566.04,2,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71320.76,1,0.0],[71698.11,2,0.0],[72075.47,0,0.0],[72264.15,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74528.3047,0,0.0],[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[75849.0547,3,0.0],[76226.4141,1,0.0],[76603.77,2,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77358.49,1,0.0],[77735.85,2,0.0],[78113.21,0,0.0],[78301.88,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80566.04,0,0.0],[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81886.79,3,0.0],[82264.15,0,0.0],[82641.51,2,0.0],[82830.19,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83396.23,3,0.0],[83773.5859,0,0.0],[84150.9453,2,0.0],[84339.625,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[87924.53,0,94.33962],[88113.21,1,283.01886],[88301.88,4,0.0],[88679.24,1,0.0],[88679.24,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,2,566.0377],[92075.47,6,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[93962.2656,2,0.0],[94150.9453,7,0.0],[94339.625,6,0.0],[94339.625,0,283.01886],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,2,283.01886],[95849.0547,4,283.01886],[96226.4141,0,283.01886],[96226.4141,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96603.77,3,0.0],[96981.13,6,0.0],[97358.49,2,0.0],[97358.49,4,283.01886],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[100000.0,2,0.0],[100188.68,7,0.0],[100377.359,0,283.01886],[100377.359,6,0.0],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,2,283.01886],[101886.789,4,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103018.867,2,94.33962],[103207.547,7,94.33962],[103396.227,0,283.01886],[103396.227,6,94.33962],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,0,283.01886],[104150.945,5,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106226.414,2,0.0],[106415.094,2,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109245.281,2,0.0],[109433.961,2,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,3,0.0],[113207.547,4,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,4,0.0],[114716.984,0,0.0],[115283.016,7,0.0],[115283.016,3,0.0],[115660.375,6,0.0],[115660.375,2,0.0],[116037.734,5,754.717],[116037.734,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118490.563,1,0.0],[118867.922,2,0.0],[119056.6,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121509.438,1,0.0],[121886.789,2,0.0],[122075.469,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,5,283.01886],[125283.016,2,0.0],[125660.375,1,283.01886],[126037.734,3,0.0],[126226.414,0,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,2,0.0],[127735.852,6,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"song":{"gf":"gf-christmas","stage":"mall-evil","song":"Winter Horrorland","bpm":159.0,"needsVoices":true,"player1":"bf-christmas","player2":"monster-christmas","speed":1.1,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9433.962,0,283.01886],[9811.32,2,283.01886],[10188.68,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[10566.0381,0,660.3774],[11320.7549,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[12452.83,0,283.01886],[12830.1885,2,283.01886],[13207.5469,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[13584.9053,1,0.0],[14339.623,3,283.01886],[14716.9814,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[15094.34,5,754.717],[15471.6982,0,283.01886],[15849.0566,2,283.01886],[16226.415,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16603.7734,0,660.3774],[17358.49,2,660.3774]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[18490.5664,0,283.01886],[18867.9238,2,283.01886],[19245.2832,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[19622.64,1,0.0],[20377.36,3,283.01886],[20754.7168,2,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21132.0762,1,283.01886],[21132.0762,5,754.717],[21509.4336,3,283.01886],[21886.793,2,283.01886],[22264.15,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22641.51,1,471.69812],[23207.5469,0,0.0],[23396.2266,0,0.0],[23773.584,0,0.0],[23962.2637,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24150.9434,3,283.01886],[24528.3027,2,0.0],[24905.66,2,0.0],[25283.02,2,0.0],[25471.6973,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[25660.377,1,471.69812],[26226.416,2,0.0],[26415.0938,3,0.0],[26603.7734,2,0.0],[26792.4531,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27169.81,1,283.01886],[27547.17,3,283.01886],[27924.5273,2,283.01886],[28301.8867,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28679.2461,1,471.69812],[29245.2832,0,0.0],[29433.9629,0,0.0],[29811.32,0,0.0],[30000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[30188.68,3,283.01886],[30566.0371,2,0.0],[30943.3965,2,0.0],[31320.7539,2,0.0],[31509.4336,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31698.1133,1,471.69812],[32264.15,2,0.0],[32452.83,3,0.0],[32641.51,2,0.0],[32830.1875,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33396.2266,3,0.0],[33584.9063,2,0.0],[33962.2656,0,283.01886],[34528.3,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[35094.34,2,283.01886],[35471.7,0,283.01886],[35849.0547,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[36603.7734,2,0.0],[36981.1328,0,283.01886],[37358.4922,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[37735.8477,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[39433.96,3,0.0],[39622.64,2,0.0],[40000.0,0,283.01886],[40566.04,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[41132.0742,2,283.01886],[41509.4336,0,283.01886],[41886.793,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[42641.5078,2,0.0],[43018.8672,0,283.01886],[43396.2266,1,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43773.5859,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45283.02,0,283.01886],[45660.38,1,283.01886],[46037.7344,2,0.0],[46415.0938,2,0.0],[46603.7734,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46792.4531,5,283.01886],[47169.8125,0,0.0],[47547.168,3,0.0],[47924.5273,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48301.8867,2,283.01886],[48301.8867,6,849.0566],[48679.2461,0,188.679245],[49056.6055,3,0.0],[49245.28,0,0.0],[49433.96,3,0.0],[49622.64,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49937.1055,2,0.0],[50188.68,2,0.0],[50440.25,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[51320.7539,0,283.01886],[51698.1133,1,283.01886],[52075.4727,2,0.0],[52452.832,2,0.0],[52641.5078,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52830.1875,5,283.01886],[53207.5469,0,0.0],[53584.9063,3,0.0],[53962.2656,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54339.62,6,1132.07544],[54339.62,2,188.679245],[54716.98,0,188.679245],[55094.34,3,0.0],[55283.02,0,0.0],[55471.7,3,0.0],[55660.38,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56037.7344,2,0.0],[56415.0938,2,0.0],[56603.7734,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57547.168,2,0.0],[57924.5273,2,0.0],[58113.207,2,0.0],[58490.5664,2,0.0],[58679.2461,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[58867.9258,1,0.0],[59245.28,3,283.01886],[59622.64,2,0.0],[60000.0,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60566.04,2,0.0],[60943.3945,2,0.0],[61132.0742,2,0.0],[61509.4336,2,0.0],[61698.1133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[61886.793,1,0.0],[62264.1523,3,283.01886],[62641.5078,2,0.0],[63018.8672,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[63396.2266,3,0.0],[63773.5859,1,283.01886],[64150.94,0,0.0],[64528.3,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64905.66,4,0.0],[64905.66,3,0.0],[65283.02,1,283.01886],[65660.375,0,0.0],[66037.7344,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[66415.09,4,0.0],[66415.09,0,0.0],[66981.13,7,0.0],[66981.13,3,0.0],[67358.49,6,0.0],[67358.49,2,0.0],[67735.85,5,754.717],[67735.85,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[69622.64,0,0.0],[69811.32,3,0.0],[70188.68,1,0.0],[70566.04,2,0.0],[70754.72,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[71132.08,2,0.0],[71320.76,1,0.0],[71698.11,2,0.0],[72075.47,0,0.0],[72264.15,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72452.83,2,283.01886],[72830.19,0,283.01886],[73207.55,3,283.01886],[73584.91,0,0.0],[73773.5859,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[74528.3047,0,0.0],[74716.9844,3,0.0],[74905.6641,0,0.0],[75094.3359,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75660.375,0,0.0],[75849.0547,3,0.0],[76226.4141,1,0.0],[76603.77,2,0.0],[76792.45,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[77169.81,2,0.0],[77358.49,1,0.0],[77735.85,2,0.0],[78113.21,0,0.0],[78301.88,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78490.56,2,283.01886],[78867.92,0,283.01886],[79245.28,3,283.01886],[79622.64,0,0.0],[79811.32,1,566.0377]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80566.04,0,0.0],[80754.72,3,0.0],[80943.4,0,0.0],[81132.08,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[81509.44,1,0.0],[81886.79,3,0.0],[82264.15,0,0.0],[82641.51,2,0.0],[82830.19,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83018.87,1,0.0],[83396.23,3,0.0],[83773.5859,0,0.0],[84150.9453,2,0.0],[84339.625,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[84528.3047,2,0.0],[84905.6641,2,0.0],[85094.3359,0,0.0],[85283.0156,2,0.0],[85660.375,2,0.0],[85849.0547,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[86037.7344,2,0.0],[86415.09,2,0.0],[86603.77,0,0.0],[86792.45,2,0.0],[87169.81,2,0.0],[87358.49,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[87547.17,3,283.01886],[87924.53,0,94.33962],[88113.21,1,283.01886],[88301.88,4,0.0],[88679.24,1,0.0],[88679.24,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89056.6,5,283.01886],[89056.6,3,0.0],[89433.96,3,0.0],[89811.32,1,283.01886],[89811.32,7,0.0],[90188.68,6,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[90566.04,5,283.01886],[90566.04,0,0.0],[91132.08,3,0.0],[91698.11,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92075.47,2,566.0377],[92075.47,6,566.0377],[92830.19,3,566.0377],[92830.19,4,566.0377]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[93773.5859,3,0.0],[93962.2656,2,0.0],[94150.9453,7,0.0],[94339.625,6,0.0],[94339.625,0,283.01886],[94716.9844,4,283.01886],[94905.6641,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[95283.0156,3,283.01886],[95471.6953,6,283.01886],[95849.0547,2,283.01886],[95849.0547,4,283.01886],[96226.4141,0,283.01886],[96226.4141,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[96603.77,3,0.0],[96981.13,6,0.0],[97358.49,2,0.0],[97358.49,4,283.01886],[97735.85,0,283.01886],[97735.85,5,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98113.21,5,283.01886],[98113.21,0,283.01886],[98490.56,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[99811.32,3,0.0],[100000.0,2,0.0],[100188.68,7,0.0],[100377.359,0,283.01886],[100377.359,6,0.0],[100754.719,4,283.01886],[100943.4,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[101320.758,3,283.01886],[101509.438,6,283.01886],[101886.789,2,283.01886],[101886.789,4,283.01886],[102264.148,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[102830.188,3,94.33962],[103018.867,2,94.33962],[103207.547,7,94.33962],[103396.227,0,283.01886],[103396.227,6,94.33962],[103773.586,1,283.01886],[103773.586,4,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[104150.945,0,283.01886],[104150.945,5,283.01886],[104528.305,7,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[105849.055,2,0.0],[106226.414,2,0.0],[106415.094,2,0.0],[106792.453,2,0.0],[106981.133,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[107169.813,1,0.0],[107547.172,3,283.01886],[107924.531,2,0.0],[108301.883,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[108867.922,2,0.0],[109245.281,2,0.0],[109433.961,2,0.0],[109811.32,2,0.0],[110000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[110188.68,1,0.0],[110566.039,3,283.01886],[110943.4,2,0.0],[111320.758,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[111698.109,3,0.0],[112075.469,1,283.01886],[112452.828,0,0.0],[112830.188,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[113207.547,3,0.0],[113207.547,4,0.0],[113584.906,1,283.01886],[113962.266,0,0.0],[114339.625,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[114716.984,4,0.0],[114716.984,0,0.0],[115283.016,7,0.0],[115283.016,3,0.0],[115660.375,6,0.0],[115660.375,2,0.0],[116037.734,5,754.717],[116037.734,1,754.717]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[117924.531,0,0.0],[118490.563,1,0.0],[118867.922,2,0.0],[119056.6,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[119245.281,2,283.01886],[119622.641,3,283.01886],[120000.0,0,283.01886],[120377.359,3,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[120754.719,6,754.717],[120943.4,0,0.0],[121509.438,1,0.0],[121886.789,2,0.0],[122075.469,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[122264.148,2,283.01886],[122641.508,3,283.01886],[123018.867,0,283.01886],[123396.227,3,754.717]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[123773.586,1,94.33962],[124150.945,3,0.0],[124528.305,2,283.01886],[124905.664,0,283.01886]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[125283.016,5,283.01886],[125283.016,2,0.0],[125660.375,1,283.01886],[126037.734,3,0.0],[126226.414,0,0.0],[126415.094,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[126792.453,0,0.0],[126792.453,4,0.0],[127358.492,3,0.0],[127358.492,7,0.0],[127735.852,2,0.0],[127735.852,6,0.0],[128113.211,1,377.3585],[128113.211,5,377.3585]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129056.6,0,0.0],[129245.281,3,0.0],[129433.961,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[129811.32,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} diff --git a/project.xml b/project.xml index bb7a17cf..8b56b56f 100644 --- a/project.xml +++ b/project.xml @@ -5,7 +5,7 @@ title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main" file="CodenameEngine" packageName="com.yoshman29.codenameengine" package="com.yoshman29.codenameengine" - version="0.1.0" company="Yoshman29" /> + version="0.1.0-legacy" company="Yoshman29" /> - diff --git a/setup-dev.xml b/setup-dev.xml index e68ef224..09bc60dd 100644 --- a/setup-dev.xml +++ b/setup-dev.xml @@ -14,7 +14,7 @@ haxelib dev hxcpp dev-libs/hxcpp - + diff --git a/source/funkin/Types.hx b/source/funkin/Types.hx new file mode 100644 index 00000000..08f0b17c --- /dev/null +++ b/source/funkin/Types.hx @@ -0,0 +1,4 @@ +package funkin; + +@:dox(hide) +typedef SingleOrFloat = #if (java || hl || cpp) Single #else Float #end; \ No newline at end of file diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index ff00f55b..d37bf843 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -85,25 +85,34 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I moves = false; } - public static function copyFrom(source:FunkinSprite) + /** + * Gets the graphics and copies other properties from another sprite (Works both for `FlxSprite` and `FunkinSprite`!). + */ + public static function copyFrom(source:FlxSprite):FunkinSprite { var spr = new FunkinSprite(); + var casted:FunkinSprite = null; + if (source is FunkinSprite) + casted = cast source; + @:privateAccess { spr.setPosition(source.x, source.y); spr.frames = source.frames; - if (source.animateAtlas != null && source.atlasPath != null) - spr.loadSprite(source.atlasPath); + if (casted != null && casted.animateAtlas != null && casted.atlasPath != null) + spr.loadSprite(casted.atlasPath); spr.animation.copyFrom(source.animation); spr.visible = source.visible; spr.alpha = source.alpha; spr.antialiasing = source.antialiasing; spr.scale.set(source.scale.x, source.scale.y); spr.scrollFactor.set(source.scrollFactor.x, source.scrollFactor.y); - spr.skew.set(source.skew.x, source.skew.y); - spr.transformMatrix = source.transformMatrix; - spr.matrixExposed = source.matrixExposed; - spr.zoomFactor = source.zoomFactor; - spr.animOffsets = source.animOffsets.copy(); + + if (casted != null) { + spr.skew.set(casted.skew.x, casted.skew.y); + spr.transformMatrix = casted.transformMatrix; + spr.matrixExposed = casted.matrixExposed; + spr.animOffsets = casted.animOffsets.copy(); + } } return spr; } diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx index 5ff8b6df..38d51281 100644 --- a/source/funkin/backend/MusicBeatState.hx +++ b/source/funkin/backend/MusicBeatState.hx @@ -105,6 +105,8 @@ class MusicBeatState extends FlxState implements IBeatReceiver public static var skipTransOut:Bool = false; public static var skipTransIn:Bool = false; + public static var ALLOW_DEV_RELOAD:Bool = true; + inline function get_controls():Controls return PlayerSettings.solo.controls; inline function get_controlsP1():Controls @@ -153,16 +155,20 @@ class MusicBeatState extends FlxState implements IBeatReceiver call("postUpdate", [elapsed]); } - if (_requestSubStateReset) - { + if (_requestSubStateReset) { _requestSubStateReset = false; resetSubState(); } - if (subState != null) - { - subState.tryUpdate(elapsed); + + if (/*subState == null && */(ALLOW_DEV_RELOAD && controls.DEV_RELOAD)) { + Logs.trace("Reloading Current State...", INFO, YELLOW); + FlxG.resetState(); } + + if (subState != null) + subState.tryUpdate(elapsed); } + override function create() { loadScript(); @@ -223,12 +229,6 @@ class MusicBeatState extends FlxState implements IBeatReceiver override function update(elapsed:Float) { - // TODO: DEBUG MODE!! - #if EXPERMENTAL_SCRIPT_RELOADING - if (FlxG.keys.justPressed.F5) { - loadScript(); - } - #end call("update", [elapsed]); super.update(elapsed); diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx index 6cb0d33a..5ad900d4 100644 --- a/source/funkin/backend/MusicBeatSubstate.hx +++ b/source/funkin/backend/MusicBeatSubstate.hx @@ -136,15 +136,19 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver call("postUpdate", [elapsed]); } - if (_requestSubStateReset) - { + // if (subState == null && (MusicBeatState.ALLOW_DEV_RELOAD && controls.DEV_RELOAD)) { + // Logs.trace("Reloading Current SubState...", INFO, YELLOW); + // var test = Type.createInstance(Type.getClass(this), [this.scriptsAllowed, this.scriptName]); + // parent.openSubState(test); + // } + + if (_requestSubStateReset) { _requestSubStateReset = false; resetSubState(); } + if (subState != null) - { subState.tryUpdate(elapsed); - } } override function close() { @@ -181,12 +185,6 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver override function update(elapsed:Float) { - #if EXPERMENTAL_SCRIPT_RELOADING - // TODO: DEBUG MODE!! - if (FlxG.keys.justPressed.F5) { - loadScript(); - } - #end call("update", [elapsed]); super.update(elapsed); } diff --git a/source/funkin/backend/assets/AssetsLibraryList.hx b/source/funkin/backend/assets/AssetsLibraryList.hx index d7ff2737..b235638a 100644 --- a/source/funkin/backend/assets/AssetsLibraryList.hx +++ b/source/funkin/backend/assets/AssetsLibraryList.hx @@ -63,7 +63,7 @@ class AssetsLibraryList extends AssetLibrary { if (l is IModsAssetLibrary) { var lib = cast(l, IModsAssetLibrary); for(e in lib.getFiles(folder)) - content.push(e); + content.pushOnce(e); } #end } diff --git a/source/funkin/backend/assets/ModsFolder.hx b/source/funkin/backend/assets/ModsFolder.hx index cd5cf515..850fdd2b 100644 --- a/source/funkin/backend/assets/ModsFolder.hx +++ b/source/funkin/backend/assets/ModsFolder.hx @@ -86,6 +86,11 @@ class ModsFolder { public static function getModsList():Array { var mods:Array = []; #if MOD_SUPPORT + if (!FileSystem.exists(modsPath)) { + // Mods directory does not exist yet, create it + FileSystem.createDirectory(modsPath); + } + final modsList:Array = FileSystem.readDirectory(modsPath); if (modsList == null || modsList.length <= 0) diff --git a/source/funkin/backend/assets/ModsFolderLibrary.hx b/source/funkin/backend/assets/ModsFolderLibrary.hx index c30b7270..94fcfc7e 100644 --- a/source/funkin/backend/assets/ModsFolderLibrary.hx +++ b/source/funkin/backend/assets/ModsFolderLibrary.hx @@ -25,10 +25,7 @@ class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary { this.folderPath = folderPath; this.libName = libName; this.prefix = 'assets/$libName/'; - if(modName == null) - this.modName = libName; - else - this.modName = modName; + this.modName = modName == null ? libName : modName; super(); } diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 227edb89..4b725a5b 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -71,16 +71,14 @@ class Paths inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (ext == null) ext = Flags.SOUND_EXT; - song = song.toLowerCase(); - var diff = getPath('songs/$song/song/Voices$prefix-${difficulty.toLowerCase()}.${ext}', null); + var diff = getPath('songs/$song/song/Voices$prefix-${difficulty}.${ext}', null); return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null); } inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (ext == null) ext = Flags.SOUND_EXT; - song = song.toLowerCase(); - var diff = getPath('songs/$song/song/Inst$prefix-${difficulty.toLowerCase()}.${ext}', null); + var diff = getPath('songs/$song/song/Inst$prefix-${difficulty}.${ext}', null); return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null); } @@ -111,8 +109,7 @@ class Paths static public function chart(song:String, ?difficulty:String):String { - difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY).toLowerCase(); - song = song.toLowerCase(); + difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY); return getPath('songs/$song/charts/$difficulty.json', null); } @@ -193,6 +190,29 @@ class Paths return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext); } + /** + * Checks if the images needed for using getFrames() exist. + * @param key Path to the image + * @param checkAtlas Whenever to check for the Animation.json file (used in FlxAnimate) + * @param assetsPath Whenever to use the raw path or to pass it through Paths.image() + * @param library (Additional) library to load the frames from. + * @return True if the images exist, false otherwise. + **/ + public static function framesExists(key:String, checkAtlas:Bool = false, checkMulti:Bool = true, assetsPath:Bool = false, ?library:String) { + var path = assetsPath ? key : Paths.image(key, library, true); + var noExt = Path.withoutExtension(path); + if(checkAtlas && Assets.exists('$noExt/Animation.json')) + return true; + if(checkMulti && Assets.exists('$noExt/1.png')) + return true; + if(Assets.exists('$noExt.xml')) + return true; + if(Assets.exists('$noExt.txt')) + return true; + if(Assets.exists('$noExt.json')) + return true; + return false; + } /** * Loads frames from a specific image path. Supports Sparrow Atlases, Packer Atlases, and multiple spritesheets. @@ -247,13 +267,13 @@ class Paths } return content; } - static public function getFolderContent(key:String, addPath:Bool = false, source:AssetsLibraryList.AssetSource = BOTH):Array { + static public function getFolderContent(key:String, addPath:Bool = false, source:AssetsLibraryList.AssetSource = BOTH, noExtension:Bool = false):Array { // designed to work both on windows and web if (!key.endsWith("/")) key += "/"; var content = assetsTree.getFiles('assets/$key', source); - if (addPath) { - for(k=>e in content) - content[k] = '$key$e'; + for (k => e in content) { + if (noExtension) e = Path.withoutExtension(e); + content[k] = addPath ? '$key$e' : e; } return content; } diff --git a/source/funkin/backend/chart/BaseGameParser.hx b/source/funkin/backend/chart/BaseGameParser.hx deleted file mode 100644 index 02c89c81..00000000 --- a/source/funkin/backend/chart/BaseGameParser.hx +++ /dev/null @@ -1,176 +0,0 @@ -package funkin.backend.chart; - -import funkin.backend.chart.ChartData.ChartMetaData; - -// These new structures are kinda a mess to port, i love and hate them at the same time; why the hell are every difficulty in the same file??? - Nex -class BaseGameParser { - public static function parseChart(data:Dynamic, metaData:Dynamic, result:ChartData) { - // TO DO - } - - public static function parseMeta(data:Dynamic, result:ChartMetaData) { - var data:SwagMetadata = data; - result.name = data.songName; - result.artist = data.artist; - - result.customValues = {}; - result.customValues.timeChanges = data.timeChanges; - var firstTimeChange:SwagTimeChange = data.timeChanges[0]; - result.bpm = firstTimeChange.bpm; - result.beatsPerMeasure = firstTimeChange.b != null ? firstTimeChange.b : Flags.DEFAULT_BEATS_PER_MEASURE; - - result.difficulties = data.playData.difficulties.concat(data.playData.songVariations); - Reflect.deleteField(data.playData, "difficulties"); - - for (field in Reflect.fields(data.playData)) - Reflect.setProperty(result.customValues, field, Reflect.getProperty(data.playData, field)); - } - - public static function encodeMeta(meta:ChartMetaData, ?chart:ChartData):SwagMetadata { - var addVars:Dynamic = meta.customValues; - var defStage:String = addVars.stage != null ? addVars.stage : Flags.DEFAULT_STAGE; - var defChars:SwagCharactersList = addVars.characters != null ? addVars.characters : {player: Flags.DEFAULT_CHARACTER, girlfriend: Flags.DEFAULT_GIRLFRIEND, opponent: Flags.DEFAULT_OPPONENT}; - var defTimeCh:Array; - - if (addVars.timeChanges != null && addVars.timeChanges.length > 0) { - defTimeCh = addVars.timeChanges; - defTimeCh[0].bpm = meta.bpm; - } - else defTimeCh = [{bpm: meta.bpm, t: -1}]; - - if (chart != null) { - defStage = chart.stage; - - var done:Array = [false, false, false]; - for (strumLine in chart.strumLines) switch (strumLine.type) { - case OPPONENT: - if (!done[0]) { - done[0] = true; - defChars.opponent = strumLine.characters.getDefault([defChars.opponent])[0]; - } - case PLAYER: - if (!done[1]) { - done[1] = true; - defChars.player = strumLine.characters.getDefault([defChars.player])[0]; - } - case ADDITIONAL: - if (!done[3]) { - done[3] = true; - defChars.girlfriend = strumLine.characters.getDefault([defChars.girlfriend])[0]; - } - } - } - - var result:SwagMetadata = { - songName: meta.name, - timeFormat: MILLISECONDS, - artist: meta.artist, - timeChanges: defTimeCh, - looped: false, - generatedBy: 'Basegame Chart Importer (Codename Engine)', - version: Flags.BASEGAME_SONG_METADATA_VERSION, - playData: { - stage: defStage, - characters: defChars, - songVariations: addVars.songVariations != null ? [for (i in 0...addVars.songVariations.length) {meta.difficulties.remove(addVars.songVariations[i]); addVars.songVariations[i];}] : [], - difficulties: meta.difficulties, - noteStyle: addVars.noteStyle != null ? addVars.noteStyle : Flags.BASEGAME_DEFAULT_NOTE_STYLE, - album: addVars.album != null ? addVars.album : Flags.BASEGAME_DEFAULT_ALBUM_ID, - previewStart: addVars.previewStart != null ? addVars.previewStart : Flags.BASEGAME_DEFAULT_PREVIEW_START, - previewEnd: addVars.previewEnd != null ? addVars.previewEnd : Flags.BASEGAME_DEFAULT_PREVIEW_END - }, - }; - - return result; - return null; - } - - public static function encodeChart(chart:ChartData):NewSwagSong { - // TO DO - return null; - } -} - -// METADATA STRUCTURES -typedef SwagMetadata = -{ - var timeFormat:SwagTimeFormat; - var artist:String; - var songName:String; - var playData:SwagPlayData; - var timeChanges:Array; - var generatedBy:String; - var looped:Bool; - var version:String; - - var ?divisions:Int; - var ?offsets:SwagSongOffsets; -} - -enum abstract SwagTimeFormat(String) from String to String -{ - var TICKS = 'ticks'; - var FLOAT = 'float'; - var MILLISECONDS = 'ms'; -} - -typedef SwagTimeChange = -{ - var ?d:Int; // Time Signature Den - var ?n:Int; // Time Signature Num - var t:Int; // Time Stamp - var ?b:Int; // Beat Time - var ?bt:Array; // Beat Tuplets - var bpm:Float; -} - -typedef SwagPlayData = -{ - var album:String; - var previewStart:Float; - var previewEnd:Float; - var stage:String; - var characters:SwagCharactersList; - var songVariations:Array; - var difficulties:Array; - var noteStyle:String; -} - -typedef SwagCharactersList = -{ - var player:String; - var girlfriend:String; - var opponent:String; -} - -typedef SwagSongOffsets = -{ - var ?instrumental:Float; - var ?altInstrumentals:Dynamic; - var ?vocals:Dynamic; -} - -// CHART STRUCTURE -typedef NewSwagSong = -{ - var version:String; - var scrollSpeed:Dynamic; // Map - var events:Array; - var notes:Dynamic; // Map> - var generatedBy:String; -} - -typedef SwagEvent = -{ - var t:Float; // Time - var e:String; // Event Kind - var v:Dynamic; // Value (Map) -} - -typedef SwagNote = -{ - var t:Float; // Time - var d:Int; // Data - var l:Float; // Length - var k:String; // Kind -} \ No newline at end of file diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index dace2cfd..8735fc81 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -72,7 +72,7 @@ class Chart { } public static function loadEventsJson(songName:String) { - var path = Paths.file('songs/${songName.toLowerCase()}/events.json'); + var path = Paths.file('songs/${songName}/events.json'); var data:Array = null; if (Assets.exists(path)) { try { @@ -83,31 +83,29 @@ class Chart { return data; } - public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true) { + public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true):ChartMetaData { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; - var songNameLower = songName.toLowerCase(); - var metaPath = Paths.file('songs/${songNameLower}/meta.json'); - var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json'); + var metaPath = Paths.file('songs/${songName}/meta.json'); + var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json'); var data:ChartMetaData = null; var fromMods:Bool = fromMods; - for(path in [metaDiffPath, metaPath]) { - if (Assets.exists(path)) { - fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS); - try { - data = Json.parse(Assets.getText(path)); - } catch(e) { - Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR); - } - if (data != null) break; - } + for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) { + fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS); + try { + var tempData = Json.parse(Assets.getText(path)); + tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR); + data = tempData; + } catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR); + if (data != null) break; } - if (data == null) - data = { - name: songName - }; + if (data == null) data = { + name: songName, + bpm: 100 + }; + data.setFieldDefault("name", songName); data.setFieldDefault("bpm", Flags.DEFAULT_BPM); data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE); @@ -118,24 +116,19 @@ class Chart { data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED); data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED); data.setFieldDefault("displayName", data.name); - data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(Flags.DEFAULT_COLOR)); if (data.difficulties.length <= 0) { - data.difficulties = [for(f in Paths.getFolderContent('songs/${songNameLower}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)]; + data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)]; + var tempDiffs = []; if (data.difficulties.length == 3) { - var hasHard = false, hasNormal = false, hasEasy = false; for(d in data.difficulties) { - switch(d) { - case "EASY": hasEasy = true; - case "NORMAL": hasNormal = true; - case "HARD": hasHard = true; + switch(d.toLowerCase()) { + case "easy": tempDiffs.insert(0, d); + case "normal": tempDiffs.insert(1, d); + case "hard": tempDiffs.insert(2, d); } } - if (hasHard && hasNormal && hasEasy) { - data.difficulties[0] = "EASY"; - data.difficulties[1] = "NORMAL"; - data.difficulties[2] = "HARD"; - } + data.difficulties = tempDiffs; } } @@ -165,11 +158,9 @@ class Chart { valid = false; } var data:Dynamic = null; - try { - if (valid) - data = Json.parse(Assets.getText(chartPath)); - } catch(e) { - Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED); + if (valid) { + try data = Json.parse(Assets.getText(chartPath)) + catch(e) Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED); } /** @@ -178,7 +169,7 @@ class Chart { #if REGION if (data != null) switch (detectChartFormat(data)) { case CODENAME: - // backward compat on events since its caused problems + // backward compat on events since it caused problems var eventTypesToString:Map = [ -1 => "HScript Call", 0 => "Unknown", @@ -196,7 +187,7 @@ class Chart { base = data; case PSYCH_NEW: PsychParser.parse(data, base); - case VSLICE: // TODO + case VSLICE: Logs.trace("Couldn't parse V-Slice chart because it's not supported on runtime, it MUST be imported and converted in the new song editor window.", ERROR, RED); case LEGACY: FNFLegacyParser.parse(data, base); } #end @@ -238,8 +229,7 @@ class Chart { return 0; default: var index = chart.noteTypes.indexOf(noteTypeName); - if (index > -1) - return index+1; + if (index > -1) return index + 1; chart.noteTypes.push(noteTypeName); return chart.noteTypes.length; } @@ -275,9 +265,8 @@ class Chart { CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null)); - // idk how null reacts to it so better be sure if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath)) - CoolUtil.safeSaveFile(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null)); + CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta)); #end return filteredChart; } @@ -306,13 +295,19 @@ class Chart { data.fromMods = null; var sortedData:Dynamic = {}; - for(f in Reflect.fields(data)) { + for (f in Reflect.fields(data)) { var v = Reflect.field(data, f); if (v != null) Reflect.setField(sortedData, f, v); } return sortedData; } + + public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String { + var data:Dynamic = Reflect.copy(meta); + if (data.color != null) data.color = new FlxColor(data.color).toWebString(); // dont even ask me - Nex + return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null); + } } typedef ChartSaveSettings = { diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx index 399b9ee2..3c0a57a7 100644 --- a/source/funkin/backend/chart/ChartData.hx +++ b/source/funkin/backend/chart/ChartData.hx @@ -22,16 +22,12 @@ typedef ChartMetaData = { public var ?beatsPerMeasure:Float; public var ?stepsPerBeat:Int; public var ?icon:String; - public var ?color:Dynamic; + public var ?color:FlxColor; public var ?difficulties:Array; public var ?coopAllowed:Bool; public var ?opponentModeAllowed:Bool; public var ?customValues:Dynamic; public var ?needsVoices:Bool; - public var ?artist:String; - - // NOT TO BE EXPORTED - public var ?parsedColor:FlxColor; } typedef ChartStrumLine = { diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index e19523ed..9a733ed7 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -9,18 +9,71 @@ import openfl.Assets; using StringTools; -final class EventsData { - public static var defaultEventsList:Array = ["HScript Call", "Camera Movement", "Add Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"]; +class EventsData { + public static var defaultEventsList:Array = ["HScript Call", "Camera Movement", "Camera Position", "Add Camera Zoom", "Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"]; public static var defaultEventsParams:Map> = [ "HScript Call" => [ {name: "Function Name", type: TString, defValue: "myFunc"}, {name: "Function Parameters (String split with commas)", type: TString, defValue: ""} ], - "Camera Movement" => [{name: "Camera Target", type: TStrumLine, defValue: 0}], + "Camera Movement" => [ + {name: "Camera Target", type: TStrumLine, defValue: 0}, + {name: "Tween Movement?", type: TBool, defValue: true, saveIfDefault: false}, + {name: "Tween Time (Steps, IF NOT CLASSIC)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4, saveIfDefault: false}, + { // since its the most used event even by default, we'll set saveIfDefault false to avoid filling up with unnecessary parameters the files - Nex + name: "Tween Ease (ex: circ, quad, cube)", + type: TDropDown(['CLASSIC', 'linear', 'back', 'bounce', 'circ', 'cube', 'elastic', 'expo', 'quad', 'quart', 'quint', 'sine', 'smoothStep', 'smootherStep']), + defValue: "CLASSIC", + saveIfDefault: false + }, + { + name: "Tween Type (excluded if CLASSIC or linear, ex: InOut)", + type: TDropDown(['In', 'Out', 'InOut']), + defValue: "In", + saveIfDefault: false + } + ], + "Camera Position" => [ + {name: "X", type: TFloat(null, null, 10, 3), defValue: 0}, + {name: "Y", type: TFloat(null, null, 10, 3), defValue: 0}, + {name: "Tween Movement?", type: TBool, defValue: true, saveIfDefault: false}, + {name: "Tween Time (Steps, IF NOT CLASSIC)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4, saveIfDefault: false}, + { + name: "Tween Ease (ex: circ, quad, cube)", + type: TDropDown(['CLASSIC', 'linear', 'back', 'bounce', 'circ', 'cube', 'elastic', 'expo', 'quad', 'quart', 'quint', 'sine', 'smoothStep', 'smootherStep']), + defValue: "CLASSIC", + saveIfDefault: false + }, + { + name: "Tween Type (excluded if CLASSIC or linear, ex: InOut)", + type: TDropDown(['In', 'Out', 'InOut']), + defValue: "In", + saveIfDefault: false + }, + {name: "Is Offset?", type: TBool, defValue: false, saveIfDefault: false} + ], "Add Camera Zoom" => [ {name: "Amount", type: TFloat(-10, 10, 0.01, 2), defValue: 0.05}, {name: "Camera", type: TDropDown(['camGame', 'camHUD']), defValue: "camGame"} ], + "Camera Zoom" => [ + {name: "Tween Zoom?", type: TBool, defValue: true}, + {name: "New Zoom", type: TFloat(-10, 10, 0.01, 2), defValue: 1}, + {name: "Camera", type: TDropDown(['camGame', 'camHUD']), defValue: "camGame"}, + {name: "Tween Time (Steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}, + { + name: "Tween Ease (ex: circ, quad, cube)", + type: TDropDown(['linear', 'back', 'bounce', 'circ', 'cube', 'elastic', 'expo', 'quad', 'quart', 'quint', 'sine', 'smoothStep', 'smootherStep']), + defValue: "linear" + }, + { + name: "Tween Type (excluded if linear, ex: InOut)", + type: TDropDown(['In', 'Out', 'InOut']), + defValue: "In" + }, + {name: "Mode", type: TDropDown(['direct', 'stage']), defValue: "direct"}, + {name: "Multiplicative?", type: TBool, defValue: true} + ], "Camera Modulo Change" => [ {name: "Modulo Interval (Beats)", type: TInt(1, 9999999, 1), defValue: 4}, {name: "Bump Strength", type: TFloat(0.1, 10, 0.01, 2), defValue: 1} @@ -42,13 +95,23 @@ final class EventsData { defValue: "linear" }, { - name: "Tween Type (ex: InOut)", + name: "Tween Type (excluded if linear, ex: InOut)", type: TDropDown(['In', 'Out', 'InOut']), defValue: "In" - } + }, + {name: "Multiplicative?", type: TBool, defValue: false} ], "Alt Animation Toggle" => [{name: "Enable On Sing Poses", type: TBool, defValue: true}, {name: "Enable On Idle", type: TBool, defValue: true}, {name: "Strumline", type: TStrumLine, defValue: 0}], - "Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}, {name: "Is forced?", type: TBool, defValue: true}], + "Play Animation" => [ + {name: "Character", type: TStrumLine, defValue: 0}, + {name: "Animation", type: TString, defValue: "animation"}, + {name: "Is forced?", type: TBool, defValue: true}, + { + name: "Animation Context", + type: TDropDown(["NONE", "SING", "DANCE", "MISS", "LOCK"]), + defValue: "NONE" + } + ], ]; public static var eventsList:Array = defaultEventsList.copy(); @@ -136,6 +199,7 @@ typedef EventParamInfo = { var name:String; var type:EventParamType; var defValue:Dynamic; + var ?saveIfDefault:Bool; } enum EventParamType { diff --git a/source/funkin/backend/chart/VSliceParser.hx b/source/funkin/backend/chart/VSliceParser.hx new file mode 100644 index 00000000..b58ab9c7 --- /dev/null +++ b/source/funkin/backend/chart/VSliceParser.hx @@ -0,0 +1,362 @@ +package funkin.backend.chart; + +import funkin.backend.chart.ChartData.ChartMetaData; + +typedef ChartDataWithInfo = { + var diffName:String; + var chart:ChartData; +} + +// These new structures are kinda a mess to port, i love and hate them at the same time; why the hell are every difficulty in the same file??? - Nex +class VSliceParser { + public static function parse(metaData:Dynamic, chartData:Dynamic, resultMeta:ChartMetaData, resultCharts:Array, ?songID:String) { + var metaData:SwagMetadata = metaData; + var chartData:NewSwagSong = chartData; + + parseMeta(metaData, resultMeta, songID); + + for (diff in Reflect.fields(chartData.notes)) + { + var base:ChartData = { + strumLines: [], + noteTypes: [], + events: [], + meta: {name: null}, + scrollSpeed: Reflect.field(chartData.scrollSpeed, diff), + stage: Flags.DEFAULT_STAGE, + codenameChart: true + }; + parseChart(Reflect.field(chartData.notes, diff), metaData, chartData.events, base); + resultCharts.push({diffName: diff, chart: base}); + } + } + + public static function parseChart(data:Dynamic, metaData:Dynamic, events:Dynamic, result:ChartData) { + // vslice chart parsing + var data:Array = data; + var metadata:SwagMetadata = metaData; + var events:Array = events; + + result.stage = metadata.playData.stage; + + var p2isGF:Bool = false; + result.strumLines.push({ + characters: [metadata.playData.characters.opponent], + type: 0, + position: (p2isGF = metadata.playData.characters.opponent.startsWith("gf")) ? "girlfriend" : "dad", + notes: [], + vocalsSuffix: "-Opponent" + }); + result.strumLines.push({ + characters: [metadata.playData.characters.player], + type: 1, + position: "boyfriend", + notes: [], + vocalsSuffix: "-Player" + }); + var gfName = metadata.playData.characters.girlfriend; + if (!p2isGF && gfName != "none") { + result.strumLines.push({ + characters: [gfName], + type: 2, + position: "girlfriend", + notes: [], + visible: false, + }); + } + + var timeChanges = metadata.timeChanges; + result.meta.bpm = timeChanges[0].bpm; + result.meta.needsVoices = true; + + for (note in data) + { + var daNoteType:Null = null; + if (note.k != null) + daNoteType = Chart.addNoteType(result, note.k == "alt" ? "Alt Anim Note" : note.k); // they hardcoded "alt" for converting old charts BUT THEN WHY THE HELL WOULD YOU CALL "MOM" THE NOTE KIND IN WEEK5 GRAHHH - Nex + + var daNoteData:Int = Std.int(note.d % 8); + var isMustHit:Bool = Math.floor(daNoteData / 4) == 0; + + result.strumLines[isMustHit ? 1 : 0].notes.push({ + time: note.t, + id: daNoteData % 4, + type: daNoteType, + sLen: note.l + }); + } + + var curBPM = result.meta.bpm; + for (i in 1...timeChanges.length) // starting from 1 on purpose - Nex + { + var curChange = timeChanges[i]; + if (curBPM != curChange.bpm) + { + curBPM = curChange.bpm; + result.events.push({ + time: curChange.t, + name: "BPM Change", + params: [curBPM] + }); + } + } + + for (event in events) + { + var values = event.v; + switch (event.e) + { + case "FocusCamera": + var isPosOnly = false; + var arr:Array = [switch(values.char) { + // more cases here in the future if they add em? (i hope not) - Nex + case 0: 1; + case 1: 0; + case -1: + isPosOnly = true; + -1; + default: 2; + }]; + if (values.ease != "CLASSIC" && values.ease != null) { + if (values.ease == "INSTANT") arr = arr.concat([false]); + else { + var cneEase = parseEase(values.ease); + arr = arr.concat([true, values.duration == null ? 4 : values.duration, cneEase[0], cneEase[1]]); + } + } + if (isPosOnly || ((values.x != null && values.x != 0) || (values.y != null && values.y != 0))) { + var useNull = arr.length <= 2; + result.events.push({ + time: event.t, + name: "Camera Position", + params: [values.x, values.y, useNull ? null : arr[1], useNull ? null : arr[2], useNull ? null : arr[3], useNull ? null : arr[4], true] + }); + } + if (!isPosOnly) result.events.push({ + time: event.t, + name: "Camera Movement", + params: arr + }); + case "PlayAnimation": + result.events.push({ + time: event.t, + name: "Play Animation", + params: [switch(values.target) { + case 'boyfriend' | 'bf' | 'player': 1; + case 'dad' | 'opponent': 0; + default /*case 'girlfriend' | 'gf'*/: 2; // usually the default should be the stage prop but we dont have that sooo - Nex + }, values.anim, values.force == null ? false : values.force] + }); + case "ScrollSpeed": + var cneEase = values.ease == null || values.ease == "INSTANT" ? ["linear", null] : parseEase(values.ease); + result.events.push({ + time: event.t, + name: "Scroll Speed Change", // we dont support the strumline value and also i will put the whole ease name into a single parameter since it works anyways - Nex + params: [values.ease != "INSTANT", values.scroll == null ? 1 : values.scroll, values.duration == null ? 4 : values.duration, cneEase[0], cneEase[1], values.absolute != true] + }); + case "SetCameraBop": + result.events.push({ + time: event.t, + name: "Camera Modulo Change", + params: [values.rate == null ? 4 : values.rate, values.intensity == null ? 1 : values.intensity] + }); + case "ZoomCamera": + var cneEase = values.ease == null || values.ease == "INSTANT" ? ["linear", null] : parseEase(values.ease); + result.events.push({ + time: event.t, + name: "Camera Zoom", // we dont support the direct mode since welp, its kind of useless here - Nex + params: [values.ease != "INSTANT", values.zoom == null ? 1 : values.zoom, "camGame", values.duration == null ? 4 : values.duration, cneEase[0], cneEase[1], values.mode, false] + }); + } + } + } + + public static function parseMeta(data:Dynamic, result:ChartMetaData, ?songID:String) { + var data:SwagMetadata = data; + var songName = data.songName; + var songID:String = songID == null ? songName.toLowerCase().replace(" ", "-") : songID; + var firstTimeChange:SwagTimeChange = data.timeChanges[0]; + + result.name = songID; + result.bpm = firstTimeChange.bpm.getDefault(Flags.DEFAULT_BPM); + result.beatsPerMeasure = firstTimeChange.n.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE); + result.stepsPerBeat = firstTimeChange.d.getDefault(Flags.DEFAULT_STEPS_PER_BEAT); + result.displayName = songName; + result.icon = Flags.DEFAULT_HEALTH_ICON; + result.color = 0xFFFFFF; + result.opponentModeAllowed = true; + result.coopAllowed = true; + result.difficulties = data.playData.difficulties.concat(data.playData.songVariations.getDefault([])); + + if (result.customValues == null) result.customValues = {}; + result.customValues.artist = data.artist; + if (data.charter != null) result.customValues.charter = data.charter; + for (field in Reflect.fields(data.playData)) if (field != "difficulties" && field != "songVariations" && field != "characters" && field != "stage") + Reflect.setProperty(result.customValues, field, Reflect.getProperty(data.playData, field)); + } + + public static function encodeMeta(meta:ChartMetaData, ?chart:ChartData):SwagMetadata { + var addVars:Dynamic = meta.customValues; + var defStage:String = addVars.stage != null ? addVars.stage : Flags.DEFAULT_STAGE; + var defChars:SwagCharactersList = addVars.characters != null ? addVars.characters : {player: Flags.DEFAULT_CHARACTER, girlfriend: Flags.DEFAULT_GIRLFRIEND, opponent: Flags.DEFAULT_OPPONENT, playerVocals: [], opponentVocals: [], instrumental: '', altInstrumentals: []}; + var defTimeCh:Array; + + if (addVars.timeChanges != null && addVars.timeChanges.length > 0) { + defTimeCh = addVars.timeChanges; + defTimeCh[0].bpm = meta.bpm; + } + else defTimeCh = [{bpm: meta.bpm, t: -1}]; + + if (chart != null) { + defStage = chart.stage; + + var done:Array = [false, false, false]; + for (strumLine in chart.strumLines) switch (strumLine.type) { + case OPPONENT: + if (!done[0]) { + done[0] = true; + defChars.opponent = strumLine.characters.getDefault([defChars.opponent])[0]; + } + case PLAYER: + if (!done[1]) { + done[1] = true; + defChars.player = strumLine.characters.getDefault([defChars.player])[0]; + } + case ADDITIONAL: + if (!done[3]) { + done[3] = true; + defChars.girlfriend = strumLine.characters.getDefault([defChars.girlfriend])[0]; + } + } + } + + var result:SwagMetadata = { + songName: meta.name, + timeFormat: MILLISECONDS, + artist: "", + timeChanges: defTimeCh, + looped: false, + generatedBy: 'V-Slice Chart Importer (Codename Engine)', + version: Flags.VSLICE_SONG_METADATA_VERSION, + playData: { + stage: defStage, + characters: defChars, + songVariations: addVars.songVariations != null ? [for (i in 0...addVars.songVariations.length) {meta.difficulties.remove(addVars.songVariations[i]); addVars.songVariations[i];}] : [], + difficulties: meta.difficulties, + noteStyle: addVars.noteStyle != null ? addVars.noteStyle : Flags.VSLICE_DEFAULT_NOTE_STYLE, + album: addVars.album != null ? addVars.album : Flags.VSLICE_DEFAULT_ALBUM_ID, + previewStart: addVars.previewStart != null ? addVars.previewStart : Flags.VSLICE_DEFAULT_PREVIEW_START, + previewEnd: addVars.previewEnd != null ? addVars.previewEnd : Flags.VSLICE_DEFAULT_PREVIEW_END + }, + }; + + return result; + } + + public static function encodeChart(chart:ChartData):NewSwagSong { + // TO DO + return null; + } + + public static function parseEase(vsliceEase:String):Array { + for (key in ['InOut', 'In', 'Out']) if (vsliceEase.endsWith(key)) return [vsliceEase.substr(0, vsliceEase.length - key.length), key]; + return [vsliceEase]; + } +} + +// METADATA STRUCTURES +typedef SwagMetadata = +{ + var version:String; + var songName:String; + var artist:String; + var ?charter:String; + var ?divisions:Int; + var ?looped:Bool; + var ?offsets:SwagSongOffsets; + var playData:SwagPlayData; + var generatedBy:String; + var ?timeFormat:SwagTimeFormat; + var timeChanges:Array; +} + +enum abstract SwagTimeFormat(String) from String to String +{ + var TICKS = 'ticks'; + var FLOAT = 'float'; + var MILLISECONDS = 'ms'; +} + +typedef SwagTimeChange = +{ + var t:Int; // Time Stamp + var ?b:Int; // Beat Time + var bpm:Float; + var ?n:Int; // Time Signature Num + var ?d:Int; // Time Signature Den + var ?bt:Array; // Beat Tuplets +} + +typedef SwagSongOffsets = +{ + var ?instrumental:Float; + var ?altInstrumentals:Dynamic; + var ?vocals:Dynamic; + var ?altVocals:Dynamic; +} + +typedef SwagPlayData = +{ + var ?songVariations:Array; + var difficulties:Array; + var characters:SwagCharactersList; + var stage:String; + var noteStyle:String; + var ?ratings:Dynamic; + var ?album:String; + var ?previewStart:Int; + var ?previewEnd:Int; +} + +typedef SwagCharactersList = +{ + var ?player:String; + var ?girlfriend:String; + var ?opponent:String; + var ?instrumental:String; + var ?altInstrumentals:Array; + var ?opponentVocals:Array; + var ?playerVocals:Array; +} + +// CHART STRUCTURE +typedef NewSwagSong = +{ + var version:String; + var scrollSpeed:Dynamic; // Map + var events:Array; + var notes:Dynamic; // Map> + var generatedBy:String; +} + +typedef SwagEvent = +{ + var t:Float; // Time + var e:String; // Event Kind + var ?v:Dynamic; // Value (Map) +} + +typedef SwagNote = +{ + var t:Float; // Time + var d:Int; // Data + var ?l:Float; // Length + var ?k:String; // Kind + var ?p:Array; // Params +} + +typedef SwagNoteParamsData = +{ + var n:String; // Name + var v:Dynamic; // Value +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/GlobalScript.hx b/source/funkin/backend/scripting/GlobalScript.hx index a7586c70..a604c457 100644 --- a/source/funkin/backend/scripting/GlobalScript.hx +++ b/source/funkin/backend/scripting/GlobalScript.hx @@ -4,6 +4,8 @@ import flixel.FlxState; import funkin.backend.assets.ModsFolder; import funkin.backend.scripting.events.CancellableEvent; import funkin.backend.system.Conductor; +import funkin.options.PlayerSettings; + #if GLOBAL_SCRIPT /** * Class for THE Global Script, aka script that runs in the background at all times. @@ -12,6 +14,8 @@ class GlobalScript { public static var scripts:ScriptPack; private static var initialized:Bool = false; + private static var reloading:Bool = false; + private static var _lastAllow_Reload:Bool = false; public static function init() { if(initialized) return; @@ -46,19 +50,13 @@ class GlobalScript { }); FlxG.signals.postUpdate.add(function() { call("postUpdate", [FlxG.elapsed]); - #if EXPERMENTAL_SCRIPT_RELOADING - if (FlxG.keys.justPressed.F5) { - if (scripts.scripts.length > 0) { - Logs.trace('Reloading global script...', WARNING, YELLOW); - scripts.reload(); - Logs.trace('Global script successfully reloaded.', WARNING, GREEN); - } else { - Logs.trace('Loading global script...', WARNING, YELLOW); - onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end); - } + + if (reloading) { + reloading = false; + MusicBeatState.ALLOW_DEV_RELOAD = _lastAllow_Reload; } - #end - if (FlxG.keys.justPressed.F2) + + if (PlayerSettings.solo.controls.DEV_CONSOLE) NativeAPI.allocConsole(); }); FlxG.signals.preDraw.add(function() { @@ -76,13 +74,26 @@ class GlobalScript { FlxG.signals.preStateSwitch.add(function() { call("preStateSwitch", []); }); + FlxG.signals.preUpdate.add(function() { call("preUpdate", [FlxG.elapsed]); call("update", [FlxG.elapsed]); - }); - // Commented this out, because it might be the cause why global runs twice - Neo - //onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end); + if (FlxG.keys.pressed.SHIFT) { + // If we want, we could just make reseting GlobalScript it's own keybind, but for now this works. + if (PlayerSettings.solo.controls.DEV_RELOAD) { + reloading = true; + Logs.trace("Reloading Global Scripts...", INFO, YELLOW); + + // yeah its a bit messy, sorry. This just prevents actually reloading the actual state. + _lastAllow_Reload = MusicBeatState.ALLOW_DEV_RELOAD; + MusicBeatState.ALLOW_DEV_RELOAD = false; + + // Would be better to just re-initalize GlobalScript so there aren't any lose ends. + onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end); + } + } + }); } public static function event(name:String, event:T):T { @@ -95,6 +106,7 @@ class GlobalScript { if (scripts != null) scripts.call(name, args); } + public static function onModSwitch(newMod:String) { call("destroy"); scripts = FlxDestroyUtil.destroy(scripts); diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx index 1727750d..f2f41040 100644 --- a/source/funkin/backend/scripting/HScript.hx +++ b/source/funkin/backend/scripting/HScript.hx @@ -128,6 +128,10 @@ class HScript extends Script { interp.execute(expr); call("new", []); } + + #if GLOBAL_SCRIPT + funkin.backend.scripting.GlobalScript.call("onScriptSetup", [this, "hscript"]); + #end } public override function reload() { diff --git a/source/funkin/backend/scripting/ScriptPack.hx b/source/funkin/backend/scripting/ScriptPack.hx index 4e9af4f7..dfd8e491 100644 --- a/source/funkin/backend/scripting/ScriptPack.hx +++ b/source/funkin/backend/scripting/ScriptPack.hx @@ -83,7 +83,7 @@ class ScriptPack extends Script { **/ public override function call(func:String, ?parameters:Array):Dynamic { for(e in scripts) - if(e.active) + if (e.active) e.call(func, parameters); return null; } diff --git a/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx b/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx index 59a3305d..06a8a53a 100644 --- a/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx @@ -2,12 +2,12 @@ package funkin.backend.scripting.events.gameplay; final class RatingUpdateEvent extends CancellableEvent { /** - New combo + New combo (may be null if no ratings were found) **/ - public var rating:ComboRating; + public var rating:Null; /** Old combo (may be null) **/ - public var oldRating:ComboRating; + public var oldRating:Null; } \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx b/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx index dbb4cb68..95ab6a8d 100644 --- a/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx +++ b/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx @@ -1,10 +1,12 @@ package funkin.backend.scripting.events.healthicon; +import flixel.util.typeLimit.OneOfTwo; + final class HealthIconChangeEvent extends CancellableEvent { /** - * Amount + * Animation State */ - public var amount:Int; + public var anim:OneOfTwo; /** * The health icon diff --git a/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx b/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx index c9afe956..d9431b66 100644 --- a/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx +++ b/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx @@ -1,6 +1,6 @@ package funkin.backend.scripting.events.menu.storymenu; -import funkin.menus.StoryMenuState.WeekData; +import funkin.backend.week.WeekData; final class WeekSelectEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/note/NoteHitEvent.hx b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx index 5614b75c..c9f8c4c9 100644 --- a/source/funkin/backend/scripting/events/note/NoteHitEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx @@ -1,6 +1,7 @@ package funkin.backend.scripting.events.note; import funkin.game.Character; +import funkin.game.HealthIcon; import funkin.game.Note; final class NoteHitEvent extends CancellableEvent { @@ -113,6 +114,10 @@ final class NoteHitEvent extends CancellableEvent { * Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one). */ public var forceAnim:Null = true; + /** + * The attached healthIcon used distinction for icons amongst others + */ + public var healthIcon:HealthIcon; /** * Prevents the default sing animation from being played. diff --git a/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx b/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx index 6cdea0ee..1973f3c2 100644 --- a/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx +++ b/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx @@ -1,5 +1,6 @@ package funkin.backend.scripting.events.splash; +import funkin.game.Splash; import funkin.game.SplashGroup; import funkin.game.Strum; @@ -11,7 +12,7 @@ final class SplashShowEvent extends CancellableEvent { /** Splash that is shown **/ - public var splash:FunkinSprite; + public var splash:Splash; /** Strum that the splash is shown on **/ diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx index fb3f9201..281616b0 100644 --- a/source/funkin/backend/shaders/FunkinShader.hx +++ b/source/funkin/backend/shaders/FunkinShader.hx @@ -1,5 +1,6 @@ package funkin.backend.shaders; +import flixel.graphics.FlxGraphic; import flixel.system.FlxAssets.FlxShader; import flixel.util.FlxSignal.FlxTypedSignal; import haxe.Exception; @@ -555,34 +556,44 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { return val; } - var field:Dynamic = Reflect.field(data, name); - var cl:String = Type.getClassName(Type.getClass(field)); + var field = Reflect.field(data, name); + var cl = Type.getClassName(Type.getClass(field)); + var isNotNull = val != null; // cant do "field is ShaderInput" for some reason if (cl.startsWith("openfl.display.ShaderParameter")) { if (field.__length <= 1) { - if ((val is Array)) - return field.value = val; // that means we wait for a single number, instead of an array - if (field.__isInt && !(val is Int)) + if (field.__isInt && isNotNull && !(val is Int)) { throw new ShaderTypeException(name, Type.getClass(val), 'Int'); - else if (field.__isBool && !(val is Bool)) + return null; + } else + if (field.__isBool && isNotNull && !(val is Bool)) { throw new ShaderTypeException(name, Type.getClass(val), 'Bool'); - else if (field.__isFloat && !(val is Float)) + return null; + } else + if (field.__isFloat && isNotNull && !(val is Float)) { throw new ShaderTypeException(name, Type.getClass(val), 'Float'); - - return field.value = [val]; + return null; + } + return field.value = isNotNull ? [val] : null; } else { - if (!(val is Array)) + if (isNotNull && !(val is Array)) { throw new ShaderTypeException(name, Type.getClass(val), Array); - + return null; + } return field.value = val; } } else if (cl.startsWith("openfl.display.ShaderInput")) { // shader input!! - if (!(val is BitmapData)) { + var bitmap:BitmapData; + if (!isNotNull) bitmap = null; + else if (val is BitmapData) bitmap = val; + else if (val is FlxGraphic) bitmap = val.bitmap; + else { throw new ShaderTypeException(name, Type.getClass(val), BitmapData); + return null; } - field.input = cast val; + field.input = bitmap; } return val; diff --git a/source/funkin/backend/system/Controls.hx b/source/funkin/backend/system/Controls.hx index d247217d..0778469d 100644 --- a/source/funkin/backend/system/Controls.hx +++ b/source/funkin/backend/system/Controls.hx @@ -24,6 +24,11 @@ enum Control PAUSE; //CHEAT; SWITCHMOD; + + // Debugs + DEV_ACCESS; + DEV_CONSOLE; + DEV_RELOAD; } enum KeyboardScheme @@ -150,6 +155,24 @@ class Controls extends FlxActionSet @:pressed("switchmod") public var SWITCHMOD_HOLD(get, set): Bool; @:justReleased("switchmod") public var SWITCHMOD_R(get, set): Bool; + @:devModeOnly + @:gamepad([]) + @:justPressed("dev-access") public var DEV_ACCESS(get, set): Bool; + @:pressed("dev-access") public var DEV_ACCESS_HOLD(get, set): Bool; + @:justReleased("dev-access") public var DEV_ACCESS_R(get, set): Bool; + + @:devModeOnly + @:gamepad([]) + @:justPressed("dev-console") public var DEV_CONSOLE(get, set): Bool; + @:pressed("dev-console") public var DEV_CONSOLE_HOLD(get, set): Bool; + @:justReleased("dev-console") public var DEV_CONSOLE_R(get, set): Bool; + + @:devModeOnly + @:gamepad([]) + @:justPressed("dev-reload") public var DEV_RELOAD(get, set): Bool; + @:pressed("dev-reload") public var DEV_RELOAD_HOLD(get, set): Bool; + @:justReleased("dev-reload") public var DEV_RELOAD_R(get, set): Bool; + @:allow(funkin.backend.utils.ControlsUtil) var byName:Map = []; diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 56ae3e92..010f9238 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -1,19 +1,32 @@ package funkin.backend.system; -import lime.utils.AssetLibrary as LimeAssetLibrary; -import lime.utils.AssetType; import flixel.util.FlxColor; - -import lime.app.Application; -import funkin.backend.system.macros.GitCommitMacro; import funkin.backend.assets.IModsAssetLibrary; import funkin.backend.assets.ScriptedAssetLibrary; +import funkin.backend.system.macros.GitCommitMacro; +import lime.app.Application; +import lime.utils.AssetLibrary as LimeAssetLibrary; +import lime.utils.AssetType; /** * A class that reads the `flags.ini` file, allowing to read settable Flags (customs too). */ @:build(funkin.backend.system.macros.FlagMacro.build()) class Flags { + // -- Codename's Mod Config -- + public static var MOD_NAME:String = ""; + public static var MOD_DESCRIPTION:String = ""; + public static var MOD_API_VERSION:Int = 1; + public static var MOD_DOWNLOADED_LINK:String = ""; + public static var MOD_DEPENDENCIES:Array = []; + + public static var MOD_ICON64:String = ""; + public static var MOD_ICON32:String = ""; + public static var MOD_ICON16:String = ""; + public static var MOD_ICON:String = ""; + + public static var MOD_DISCORD_CLIENT_ID:String = ""; + public static var MOD_DISCORD_LOGO_KEY:String = ""; // -- Codename's Default Flags -- public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber; public static var COMMIT_HASH:String = GitCommitMacro.commitHash; @@ -28,10 +41,6 @@ class Flags { public static var REPO_OWNER:String = "CodenameCrew"; public static var REPO_URL:String = 'https://github.com/$REPO_OWNER/$REPO_NAME'; - // make this empty once you guys are done with the project. - // good luck /gen <3 @crowplexus - public static var RELEASE_CYCLE:String = "Beta"; - /** * Preferred sound extension for the game's audio files. * Currently is set to `mp3` for web targets, and `ogg` for other targets. @@ -41,7 +50,7 @@ class Flags { public static var IMAGE_EXT:String = "png"; // we also support jpg public static var DEFAULT_DISCORD_LOGO_KEY:String = "icon"; - public static var DEFAULT_DISCORD_CLIENT_ID:String = "1027994136193810442"; + public static var DEFAULT_DISCORD_CLIENT_ID:String = "1383853614589673472"; public static var DEFAULT_DISCORD_LOGO_TEXT:String = "Codename Engine"; @:also(funkin.game.Character.FALLBACK_CHARACTER) @@ -59,23 +68,25 @@ class Flags { public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override"; public static var DEFAULT_BPM:Float = 100.0; - public static var DEFAULT_BEATS_PER_MEASURE:Float = 4; + public static var DEFAULT_BEATS_PER_MEASURE:Int = 4; public static var DEFAULT_STEPS_PER_BEAT:Int = 4; + public static var DEFAULT_LOOP_TIME:Float = 0.0; public static var SUPPORTED_CHART_RUNTIME_FORMATS:Array = ["Legacy", "Psych Engine"]; public static var SUPPORTED_CHART_FORMATS:Array = ["BaseGame"]; - public static var BASEGAME_SONG_METADATA_VERSION:String = "2.2.2"; - public static var BASEGAME_SONG_CHART_DATA_VERSION:String = "2.0.0"; - public static var BASEGAME_DEFAULT_NOTE_STYLE:String = 'funkin'; - public static var BASEGAME_DEFAULT_ALBUM_ID:String = 'volume1'; - public static var BASEGAME_DEFAULT_PREVIEW_START:Float = 0; - public static var BASEGAME_DEFAULT_PREVIEW_END:Float = 15000; + public static var VSLICE_SONG_METADATA_VERSION:String = "2.2.2"; + public static var VSLICE_SONG_CHART_DATA_VERSION:String = "2.0.0"; + public static var VSLICE_DEFAULT_NOTE_STYLE:String = 'funkin'; + public static var VSLICE_DEFAULT_ALBUM_ID:String = 'volume1'; + public static var VSLICE_DEFAULT_PREVIEW_START:Int = 0; + public static var VSLICE_DEFAULT_PREVIEW_END:Int = 15000; /** - * Default background colors for songs without bg color + * Default background colors for songs or more without bg color */ public static var DEFAULT_COLOR:FlxColor = 0xFF9271FD; + public static var DEFAULT_WEEK_COLOR:FlxColor = 0xFFF9CF51; public static var DEFAULT_COOP_ALLOWED:Bool = false; public static var DEFAULT_OPPONENT_MODE_ALLOWED:Bool = false; @@ -97,7 +108,7 @@ class Flags { public static var DEFAULT_CAM_ZOOM_STRENGTH:Int = 1; public static var DEFAULT_CAM_ZOOM:Float = 1.05; // what zoom level it defaults to public static var DEFAULT_HUD_ZOOM:Float = 1.0; - public static var MAX_CAMERA_ZOOM:Float = 1.35; + public static var MAX_CAMERA_ZOOM_MULT:Float = 1.35; public static var DEFAULT_PAUSE_ITEMS:Array = ['Resume', 'Restart Song', 'Change Controls', 'Change Options', 'Exit to menu', "Exit to charter"]; public static var DEFAULT_CUTSCENE_PAUSE_ITEMS:Array = ['Resume Cutscene', 'Skip Cutscene', 'Restart Cutscene', 'Exit to menu']; @@ -157,7 +168,7 @@ class Flags { // -- End of Codename's Default Flags -- /** - * Flags that Codename couldn't recognize as it's own defaults (they can only be `string`!). + * Flags that Codename couldn't recognize as it's own defaults (they can only be `string`! due to them being unparsed). */ @:bypass public static var customFlags:Map = []; @@ -187,10 +198,9 @@ class Flags { } } - public static function loadFromDatas(files:Array) { + public static function loadFromDatas(datas:Array) { var flags:Map = []; - for(file in files) { - var data:String = Assets.getText(file); + for(data in datas) { if(data != null) loadFromData(flags, data); } diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx index da72adb9..ab435a7c 100644 --- a/source/funkin/backend/system/Main.hx +++ b/source/funkin/backend/system/Main.hx @@ -81,12 +81,12 @@ class Main extends Sprite public static var audioDisconnected:Bool = false; public static var changeID:Int = 0; - public static var pathBack = #if windows + public static var pathBack = #if (windows || linux) "../../../../" #elseif mac "../../../../../../../" #else - "" + "../../../../" #end; public static var startedFromSource:Bool = #if TEST_BUILD true #else false #end; @@ -180,6 +180,8 @@ class Main extends Sprite } public static function refreshAssets() @:privateAccess { + FunkinCache.instance.clearSecondLayer(); + var game = FlxG.game; var daSndTray = Type.createInstance(game._customSoundTray = funkin.menus.ui.FunkinSoundTray, []); var index:Int = game.numChildren - 1; @@ -238,9 +240,9 @@ class Main extends Sprite Sys.setCwd(haxe.io.Path.directory(Sys.programPath())); } #elseif android - Sys.setCwd(Path.addTrailingSlash(VERSION.SDK_INT > 30 ? Context.getObbDir() : Context.getExternalFilesDir())); + Sys.setCwd(haxe.io.Path.addTrailingSlash(VERSION.SDK_INT > 30 ? Context.getObbDir() : Context.getExternalFilesDir())); #elseif (ios || switch) - Sys.setCwd(Path.addTrailingSlash(openfl.filesystem.File.applicationStorageDirectory.nativePath)); + Sys.setCwd(haxe.io.Path.addTrailingSlash(openfl.filesystem.File.applicationStorageDirectory.nativePath)); #end } diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 4cf6b972..1b0aea70 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -6,16 +6,21 @@ import sys.FileSystem; import flixel.FlxState; import funkin.backend.assets.ModsFolder; import funkin.backend.chart.EventsData; -import funkin.menus.BetaWarningState; -import funkin.menus.TitleState; import funkin.backend.system.framerate.Framerate; +import funkin.menus.TitleState; +import haxe.io.Path; + +@dox(hide) +typedef AddonInfo = { + var name:String; + var path:String; +} /** * Simple state used for loading the game */ class MainState extends FlxState { public static var initiated:Bool = false; - public static var betaWarningShown:Bool = false; public override function create() { super.create(); if (!initiated) { @@ -36,25 +41,64 @@ class MainState extends FlxState { Paths.assetsTree.reset(); #if MOD_SUPPORT - var _lowPriorityAddons:Array = []; - var _highPriorityAddons:Array = []; - var _noPriorityAddons:Array = []; - if (FileSystem.exists(ModsFolder.addonsPath) && FileSystem.isDirectory(ModsFolder.addonsPath)) { - for(i=>addon in [for(dir in FileSystem.readDirectory(ModsFolder.addonsPath)) if (FileSystem.isDirectory(ModsFolder.addonsPath + dir)) dir]) { - if (addon.startsWith("[LOW]")) _lowPriorityAddons.insert(0, addon); - else if (addon.startsWith("[HIGH]")) _highPriorityAddons.insert(0, addon); - else _noPriorityAddons.insert(0, addon); - } - for (addon in _lowPriorityAddons) - Paths.assetsTree.addLibrary(ModsFolder.loadModLib(ModsFolder.addonsPath + addon, StringTools.ltrim(addon.substr("[LOW]".length)))); - } - if (ModsFolder.currentModFolder != null) - Paths.assetsTree.addLibrary(ModsFolder.loadModLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder)); + inline function isDirectory(path:String):Bool + return FileSystem.exists(path) && FileSystem.isDirectory(path); - if (FileSystem.exists(ModsFolder.addonsPath) && FileSystem.isDirectory(ModsFolder.addonsPath)){ - for (addon in _noPriorityAddons) Paths.assetsTree.addLibrary(ModsFolder.loadModLib(ModsFolder.addonsPath + addon, addon)); - for (addon in _highPriorityAddons) Paths.assetsTree.addLibrary(ModsFolder.loadModLib(ModsFolder.addonsPath + addon, StringTools.ltrim(addon.substr("[HIGH]".length)))); + inline function ltrim(str:String, prefix:String):String + return str.substr(prefix.length).ltrim(); + + inline function loadLib(path:String, name:String) + Paths.assetsTree.addLibrary(ModsFolder.loadModLib(path, name)); + + var _lowPriorityAddons:Array = []; + var _highPriorityAddons:Array = []; + var _noPriorityAddons:Array = []; + + var addonPaths = [ + ModsFolder.addonsPath, + ( + ModsFolder.currentModFolder != null ? + ModsFolder.modsPath + ModsFolder.currentModFolder + "/addons/" : + null + ) + ]; + + for(path in addonPaths) { + if (path == null) continue; + if (!isDirectory(path)) continue; + + for(addon in FileSystem.readDirectory(path)) { + if(!FileSystem.isDirectory(path + addon)) { + switch(Path.extension(addon).toLowerCase()) { + case 'zip': + addon = Path.withoutExtension(addon); + default: + continue; + } + } + + var data:AddonInfo = { + name: addon, + path: path + addon + }; + + if (addon.startsWith("[LOW]")) _lowPriorityAddons.insert(0, data); + else if (addon.startsWith("[HIGH]")) _highPriorityAddons.insert(0, data); + else _noPriorityAddons.insert(0, data); + } } + + for (addon in _lowPriorityAddons) + loadLib(addon.path, ltrim(addon.name, "[LOW]")); + + if (ModsFolder.currentModFolder != null) + loadLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder); + + for (addon in _noPriorityAddons) + loadLib(addon.path, addon.name); + + for (addon in _highPriorityAddons) + loadLib(addon.path, ltrim(addon.name, "[HIGH]")); #end Flags.load(); @@ -67,14 +111,10 @@ class MainState extends FlxState { ControlsUtil.loadCustomControls(); TitleState.initialized = false; - if(Framerate.isLoaded) + if (Framerate.isLoaded) Framerate.instance.reload(); - if (Flags.DISABLE_BETA_WARNING_SCREEN || betaWarningShown) - FlxG.switchState(new TitleState()); - else - FlxG.switchState(new BetaWarningState()); - betaWarningShown = true; + FlxG.switchState(new TitleState()); #if sys CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN); diff --git a/source/funkin/backend/system/framerate/CodenameBuildField.hx b/source/funkin/backend/system/framerate/CodenameBuildField.hx index aa7a9b7b..314479ea 100644 --- a/source/funkin/backend/system/framerate/CodenameBuildField.hx +++ b/source/funkin/backend/system/framerate/CodenameBuildField.hx @@ -13,5 +13,5 @@ class CodenameBuildField extends TextField { } public function reload() - text = 'Codename Engine ${Flags.RELEASE_CYCLE}\n${Flags.COMMIT_MESSAGE}'; + text = '${Flags.VERSION_MESSAGE}\n${Flags.COMMIT_MESSAGE}'; } diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx index 297fcde7..c485468c 100644 --- a/source/funkin/backend/system/framerate/FlixelInfo.hx +++ b/source/funkin/backend/system/framerate/FlixelInfo.hx @@ -12,9 +12,7 @@ class FlixelInfo extends FramerateCategory { @:privateAccess { - var c:Int = 0; - for(_ in FlxG.bitmap._cache.keys()) - c++; + var c:Int = Lambda.count(FlxG.bitmap._cache); if((FlxG.state is ModState)) { var state:ModState = cast FlxG.state; @@ -27,6 +25,7 @@ class FlixelInfo extends FramerateCategory { _text += '\nBitmaps Count: ${c}'; _text += '\nSounds Count: ${FlxG.sound.list.length}'; _text += '\nFlxG.game Childs Count: ${FlxG.game.numChildren}'; + if(FlxG.renderBlit) _text += '\nBlitting Render: true'; // _text += '\nCached objects count: ${cachedObjects}'; #if FLX_POINT_POOL //var points = flixel.math.FlxPoint.FlxBasePoint.pool; diff --git a/source/funkin/backend/system/framerate/Framerate.hx b/source/funkin/backend/system/framerate/Framerate.hx index 0528ffee..2acc7601 100644 --- a/source/funkin/backend/system/framerate/Framerate.hx +++ b/source/funkin/backend/system/framerate/Framerate.hx @@ -20,7 +20,7 @@ class Framerate extends Sprite { public static var codenameBuildField:CodenameBuildField; #end - public static var fontName:String = #if windows '${Sys.getEnv("windir")}\\Fonts\\consola.ttf' #else "_sans" #end; + public static var fontName:String = #if windows '${Sys.getEnv("windir")}\\Fonts\\consola.ttf' #else "_typewriter" #end; /** * 0: FPS INVISIBLE @@ -46,7 +46,7 @@ class Framerate extends Sprite { super(); if (instance != null) throw "Cannot create another instance"; instance = this; - textFormat = new TextFormat("Consolas", 12, -1); + textFormat = new TextFormat(fontName, 12, -1); isLoaded = true; diff --git a/source/funkin/backend/system/framerate/SystemInfo.hx b/source/funkin/backend/system/framerate/SystemInfo.hx index ec44f05f..154712bd 100644 --- a/source/funkin/backend/system/framerate/SystemInfo.hx +++ b/source/funkin/backend/system/framerate/SystemInfo.hx @@ -53,6 +53,25 @@ class SystemInfo extends FramerateCategory { if (osName != "") osInfo = '${osName} ${osVersion}'.trim(); } + #elseif windows + var windowsCurrentVersionPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; + var buildNumber = Std.parseInt(RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, "CurrentBuildNumber")); + var edition = RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, "ProductName"); + + var lcuKey = "WinREVersion"; // Last Cumulative Update Key On Older Windows Versions + if (buildNumber >= 22000) { // Windows 11 Initial Release Build Number + edition = edition.replace("Windows 10", "Windows 11"); + lcuKey = "LCUVer"; // Last Cumulative Update Key On Windows 11 + } + + var lcuVersion = RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, lcuKey); + + osInfo = edition; + + if (lcuVersion != null && lcuVersion != "") + osInfo += ' ${lcuVersion}'; + else if (lime.system.System.platformVersion != null && lime.system.System.platformVersion != "") + osInfo += ' ${lime.system.System.platformVersion}'; #else if (lime.system.System.platformLabel != null && lime.system.System.platformLabel != "" && lime.system.System.platformVersion != null && lime.system.System.platformVersion != "") osInfo = '${lime.system.System.platformLabel.replace(lime.system.System.platformVersion, "").trim()} ${lime.system.System.platformVersion}'; @@ -62,10 +81,7 @@ class SystemInfo extends FramerateCategory { try { #if windows - var process = new HiddenProcess("wmic", ["cpu", "get", "name"]); - if (process.exitCode() != 0) throw 'Could not fetch CPU information'; - - cpuName = process.stdout.readAll().toString().trim().split("\n")[1].trim(); + cpuName = RegistryUtil.get(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "ProcessorNameString"); #elseif mac var process = new HiddenProcess("sysctl -a | grep brand_string"); // Somehow this isn't able to use the args but it still works if (process.exitCode() != 0) throw 'Could not fetch CPU information'; @@ -86,7 +102,7 @@ class SystemInfo extends FramerateCategory { Logs.trace('Unable to grab CPU Name: $e', ERROR, RED); } - @:privateAccess { + @:privateAccess if(FlxG.renderTile) { // Blit doesn't enable the gpu. Idk if we should fix this if (flixel.FlxG.stage.context3D != null && flixel.FlxG.stage.context3D.gl != null) { gpuName = Std.string(flixel.FlxG.stage.context3D.gl.getParameter(flixel.FlxG.stage.context3D.gl.RENDERER)).split("/")[0].trim(); #if !flash diff --git a/source/funkin/backend/system/macros/BuildInfo.hx b/source/funkin/backend/system/macros/BuildInfo.hx index 37c0f197..873770b1 100644 --- a/source/funkin/backend/system/macros/BuildInfo.hx +++ b/source/funkin/backend/system/macros/BuildInfo.hx @@ -7,6 +7,8 @@ class BuildInfo { public static function printBuildInfo() { if(haxe.macro.Context.defined("display")) return; var haxeVersion = haxe.macro.Context.definedValue("haxe").trim(); + var sleepChangeVersion = false; + Sys.println('[ BUILD INFO ]'); Sys.println('Haxe Version: ${haxeVersion}'); try { @@ -33,8 +35,10 @@ class BuildInfo { } } - if(lastBuiltWith != null && lastBuiltWith.length > 0 && lastBuiltWith != haxeVersion) + if(lastBuiltWith != null && lastBuiltWith.length > 0 && lastBuiltWith != haxeVersion) { Sys.println('Last Built With Haxe: ${lastBuiltWith} [!!!! MAKE SURE IF YOU SWITCHED VERSIONS YOU DELETE EXPORT FOLDERS !!!!]'); + sleepChangeVersion = true; + } } catch(e) {} var targetPlatform = #if html5 "Web (HTML5)" #elseif ios "iOS" @@ -51,6 +55,14 @@ class BuildInfo { Sys.println('Target Platform: ' + targetPlatform); Sys.println('Build Date: ${Date.now().toString()}'); Sys.println(''); + + if(sleepChangeVersion) { + Sys.println('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); + Sys.println('!!!! MAKE SURE IF YOU SWITCHED VERSIONS YOU DELETE EXPORT FOLDERS !!!!'); + Sys.println('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'); + Sys.println(''); + Sys.sleep(5); + } } } #end \ No newline at end of file diff --git a/source/funkin/backend/system/macros/ControlsMacro.macro.hx b/source/funkin/backend/system/macros/ControlsMacro.macro.hx index 2219a677..93c927cc 100644 --- a/source/funkin/backend/system/macros/ControlsMacro.macro.hx +++ b/source/funkin/backend/system/macros/ControlsMacro.macro.hx @@ -14,6 +14,7 @@ class ControlsMacro { static var _allControls: Array = null; static var _allInternalControls: Array = null; + static var _allDevModeOnlyControls: Array = null; static var _currentControls: Map> = null; static var _currentGamepadControls: Map = null; static var _keySet: Map = null; @@ -29,6 +30,7 @@ class ControlsMacro _allControls = []; _allInternalControls = []; + _allDevModeOnlyControls = []; _currentControls = []; _currentGamepadControls = []; _keySet = []; @@ -154,6 +156,7 @@ class ControlsMacro _allControls = null; _allInternalControls = null; + _allDevModeOnlyControls = null; _currentControls = null; _currentGamepadControls = null; _keySet = null; @@ -289,6 +292,9 @@ class ControlsMacro case ":justReleased": keyset = extractString(meta.params[0]); expr = macro func($i{internalName}, JUST_RELEASED); + case ":devModeOnly": + if (!_allDevModeOnlyControls.contains(shortName)) + _allDevModeOnlyControls.push(shortName); default: shouldRemove = false; } @@ -326,17 +332,16 @@ class ControlsMacro // Generated Code: // inline function get_UI_UP(): Bool - // return _uiUp.check(); + // return _uiUp.check(); or return Options.devMode && _uiUp.check(); depending if its dev mode var getField: Field = { name: "get_" + name, access: [APrivate, AInline], kind: FFun({ ret: macro : Bool, params: [], - expr: macro - { - return $i{internalName}.check(); - }, + expr: _allDevModeOnlyControls.contains(shortName) ? + (macro return Options.devMode && $i{internalName}.check()) : + (macro return $i{internalName}.check()), args: [] }), pos: field.pos, diff --git a/source/funkin/backend/system/net/Socket.hx b/source/funkin/backend/system/net/Socket.hx index 4aefdabe..19ebf9a1 100644 --- a/source/funkin/backend/system/net/Socket.hx +++ b/source/funkin/backend/system/net/Socket.hx @@ -18,7 +18,7 @@ class Socket implements IFlxDestroyable { public function read():String { try { - return this.socket.input.readUntil(('\n').charCodeAt(0)).replace("\\n", "\n"); + return this.socket.input.readUntil('\n'.code).replace("\\n", "\n"); } catch(e) { } diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx index df818d03..49c5a991 100644 --- a/source/funkin/backend/utils/AudioAnalyzer.hx +++ b/source/funkin/backend/utils/AudioAnalyzer.hx @@ -21,21 +21,21 @@ class AudioAnalyzer { } public function analyze(startPos:Float, endPos:Float):Float { - var bitsPerSample = buffer.bitsPerSample; - var multi = __timeMulti / 4000 / bitsPerSample; - var bytesStartPos:Int = Math.floor(startPos * multi) * bitsPerSample; - var bytesEndPos:Int = Math.floor(endPos * multi) * bitsPerSample; + var bytesStartPos:Int = Math.floor(startPos * __timeMulti / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample; + var bytesEndPos:Int = Math.floor(endPos * __timeMulti / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample; - bytesStartPos -= bytesStartPos % bitsPerSample; - bytesEndPos -= bytesEndPos % bitsPerSample; - - var dataBuffer = buffer.data.buffer; + bytesStartPos -= bytesStartPos % buffer.bitsPerSample; + bytesEndPos -= bytesEndPos % buffer.bitsPerSample; var maxByte:Int = 0; - for(i in 0...Math.floor((bytesEndPos - bytesStartPos) / bitsPerSample)) { - var bytePos = bytesStartPos + (i * bitsPerSample); - // What if bitsPerSample is 8? or 32? or 64? -Ne_Eo - var byte:Int = dataBuffer.get(bytePos) | (dataBuffer.get(bytePos + 1) << 8); + for(i in 0...Math.floor((bytesEndPos - bytesStartPos) / buffer.bitsPerSample)) { + #if js + var byte:Int = buffer.data[bytesStartPos + (i * buffer.bitsPerSample)] + | (buffer.data[bytesStartPos + (i * buffer.bitsPerSample) + 1] << 8); + #else + var byte:Int = buffer.data.buffer.get(bytesStartPos + (i * buffer.bitsPerSample)) + | (buffer.data.buffer.get(bytesStartPos + (i * buffer.bitsPerSample) + 1) << 8); + #end if (byte > 256 * 128) byte -= 256 * 256; if (maxByte < byte) maxByte = byte; } diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 5482df6c..9617d520 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -145,7 +145,7 @@ final class CoolUtil if(result.isNothing) Logs.trace('The file where it has been tried to get the attributes from, might be corrupted or inexistent (code: ${result.getValue()})', WARNING); return result; #else - return 0; + return new FileAttributeWrapper(0); #end } @@ -183,7 +183,7 @@ final class CoolUtil addMissingFolders(Path.directory(path)); var result = NativeAPI.addFileAttributes(path, attrib, useAbsolute); - if(result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING); + if (result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING); return result; #else return 0; @@ -453,9 +453,13 @@ final class CoolUtil if (Assets.exists(infoPath)) { var musicInfo = IniUtil.parseAsset(infoPath, [ "BPM" => null, - "TimeSignature" => Flags.DEFAULT_BEATS_PER_MEASURE + "/" + Flags.DEFAULT_STEPS_PER_BEAT + "TimeSignature" => Flags.DEFAULT_BEATS_PER_MEASURE + "/" + Flags.DEFAULT_STEPS_PER_BEAT, + "LoopTime" => '${Flags.DEFAULT_LOOP_TIME}' ]); + if (FlxG.sound.music != null) + FlxG.sound.music.loopTime = Std.parseFloat(musicInfo["LoopTime"]) * 1000; + var timeSignParsed:Array> = musicInfo["TimeSignature"] == null ? [] : [for(s in musicInfo["TimeSignature"].split("/")) Std.parseFloat(s)]; var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE; var stepsPerBeat:Int = Flags.DEFAULT_STEPS_PER_BEAT; @@ -712,11 +716,11 @@ final class CoolUtil * @param spr Sprite to load the graphic for * @param path Path to the graphic */ - public static function loadAnimatedGraphic(spr:FlxSprite, path:String) { + public static function loadAnimatedGraphic(spr:FlxSprite, path:String, fps:Float = 24.0) { spr.frames = Paths.getFrames(path, true); if (spr.frames != null && spr.frames.frames != null) { - spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], 24, true); + spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], fps, true); spr.animation.play("idle"); } @@ -915,8 +919,8 @@ final class CoolUtil * @param mainEase Main ease * @param suffix Suffix (Ignored if `mainEase` is `linear`) */ - @:noUsing public static inline function flxeaseFromString(mainEase:String, suffix:String) - return Reflect.field(FlxEase, mainEase + (mainEase == "linear" ? "" : suffix)); + @:noUsing public static inline function flxeaseFromString(mainEase:String, ?suffix:String) + return Reflect.field(FlxEase, mainEase + (mainEase == "linear" || suffix == null ? "" : suffix)); /* * Returns the filename of a path, without the extension. @@ -1323,6 +1327,124 @@ final class CoolUtil public static inline function isMapEmpty(map: Map): Bool { return !map.keys().hasNext(); } + + public inline static function parsePropertyString(fieldPath:String):Array> { + return FlxTween.parseFieldString(fieldPath); + } + + public static function stringifyFieldsPath(fields:Array>):String { + var str = new StringBuf(); + var first = true; + for (field in fields) { + if (Type.typeof(field) == TInt) { + str.add('[${field}]'); + } else { + if (!first) + str.add('.'); + str.add(field); + } + first = false; + } + return str.toString(); + } + + public static function parseProperty(target:Dynamic, fields:OneOfTwo>>):Dynamic { + var fields:Array> = { + if((fields is String)) CoolUtil.parsePropertyString(fields); + else fields; + } + + var field = CoolUtil.last(fields); + for (i in 0...fields.length - 1) { + var component = fields[i]; + if (Type.typeof(component) == TInt) { + if ((target is Array)) { + var index:Int = cast component; + var arr:Array = cast target; + target = arr[index]; + } + } else { // TClass(String) + target = Reflect.getProperty(target, component); + } + if (!Reflect.isObject(target) && !(target is Array)) + throw 'The object does not have the property "$component" in "${stringifyFieldsPath(fields)}"'; + } + return new PropertyInfo(target, field); + } + + public static function cloneProperty(toTarget:Dynamic, fields:OneOfTwo>>, fromTarget:Dynamic):Dynamic { + var fields:Array> = { + if((fields is String)) CoolUtil.parsePropertyString(fields); + else fields; + } + + var toProperty = CoolUtil.parseProperty(toTarget, fields); + var fromProperty = CoolUtil.parseProperty(fromTarget, fields); + + return toProperty.setValue(fromProperty.getValue()); + } +} + +class PropertyInfo { + public var object:Dynamic; + public var field:OneOfTwo; + public var typeOfField:Type.ValueType; + #if hscript_improved + public var isCustom:Bool = false; + public var custom:hscript.IHScriptCustomBehaviour; + #end + + public function new(object:Dynamic, field:OneOfTwo) { + this.object = object; + this.field = field; + #if hscript_improved + if (object is hscript.IHScriptCustomBehaviour) + { + isCustom = true; + custom = cast object; + } + #end + + typeOfField = Type.typeof(field); + } + + public function getValue():Dynamic + { + if (typeOfField == TInt) + { + var index:Int = cast field; + var arr:Array = cast object; + return arr[index]; + } + else + { + #if hscript_improved + if (isCustom) + return custom.hget(field); + else + #end + return Reflect.getProperty(object, field); + } + } + + public function setValue(value:Dynamic):Void + { + if (typeOfField == TInt) + { + var index:Int = cast field; + var arr:Array = cast object; + arr[index] = value; + } + else + { + #if hscript_improved + if (isCustom) + custom.hset(field, value); + else + #end + Reflect.setProperty(object, field, value); + } + } } /** diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx index 6614b828..2de56184 100644 --- a/source/funkin/backend/utils/MemoryUtil.hx +++ b/source/funkin/backend/utils/MemoryUtil.hx @@ -127,7 +127,7 @@ final class MemoryUtil { public static function getMemType():String { #if windows var memoryMap:Map = [ - 0 => "Unknown", + 0 => null, 1 => "Other", 2 => "DRAM", 3 => "Synchronous DRAM", @@ -166,9 +166,9 @@ final class MemoryUtil { ]; var memoryOutput:Int = -1; - var process = new HiddenProcess("wmic", ["memorychip", "get", "SMBIOSMemoryType"]); + var process = new HiddenProcess("powershell", ["-Command", "Get-CimInstance Win32_PhysicalMemory | Select-Object -ExpandProperty SMBIOSMemoryType" ]); if (process.exitCode() == 0) memoryOutput = Std.int(Std.parseFloat(process.stdout.readAll().toString().trim().split("\n")[1])); - if (memoryOutput != -1) return memoryMap[memoryOutput]; + if (memoryOutput != -1) return memoryMap[memoryOutput] == null ? 'Unknown ($memoryOutput)' : memoryMap[memoryOutput]; #elseif mac var process = new HiddenProcess("system_profiler", ["SPMemoryDataType"]); var reg = ~/Type: (.+)/; diff --git a/source/funkin/backend/utils/RegistryUtil.hx b/source/funkin/backend/utils/RegistryUtil.hx new file mode 100644 index 00000000..722eb964 --- /dev/null +++ b/source/funkin/backend/utils/RegistryUtil.hx @@ -0,0 +1,120 @@ +package funkin.backend.utils; + +enum abstract RegistryHive(Int) { + var HKEY_CLASSES_ROOT = 0x80000000; + var HKEY_CURRENT_USER = 0x80000001; + var HKEY_LOCAL_MACHINE = 0x80000002; + var HKEY_USERS = 0x80000003; + var HKEY_CURRENT_CONFIG = 0x80000005; +} +#if windows +@:cppFileCode(' +#include +#include +#include +#include +') +#end +class RegistryUtil { + #if windows + @:functionCode(' + HKEY hKey; + LONG result; + DWORD dataSize = 0; + DWORD dataType = 0; + + std::wstring subkey = std::wstring(key.wchar_str()); + std::wstring valname = std::wstring(string.wchar_str()); + + result = RegOpenKeyExW((HKEY)reinterpret_cast(static_cast(hive)), subkey.c_str(), 0, KEY_READ, &hKey); + if (result != ERROR_SUCCESS) return null(); + + result = RegQueryValueExW(hKey, valname.c_str(), NULL, &dataType, NULL, &dataSize); + if (result != ERROR_SUCCESS || dataSize == 0) { + RegCloseKey(hKey); + return null(); + } + + std::vector buffer(dataSize / sizeof(wchar_t)); + result = RegQueryValueExW(hKey, valname.c_str(), NULL, NULL, (LPBYTE)buffer.data(), &dataSize); + RegCloseKey(hKey); + + if (result == ERROR_SUCCESS) { + return ::String(buffer.data()); + } + return null(); + ') + #end + public static function get(hive:RegistryHive, key:String, string:String):Null + { + return null; + } + + #if windows + @:functionCode(' + HKEY hKey; + LONG result; + + std::wstring subkey = std::wstring(key.wchar_str()); + std::wstring valname = std::wstring(string.wchar_str()); + std::wstring data = std::wstring(value.wchar_str()); + + result = RegCreateKeyExW((HKEY)reinterpret_cast(static_cast(hive)), subkey.c_str(), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); + if (result != ERROR_SUCCESS) return false; + + result = RegSetValueExW(hKey, valname.c_str(), 0, REG_SZ, (const BYTE*)data.c_str(), (DWORD)((data.length() + 1) * sizeof(wchar_t))); + RegCloseKey(hKey); + + return result == ERROR_SUCCESS; + ') + #end + public static function set(hive:RegistryHive, key:String, string:String, value:String):Bool + { + return false; + } + + #if windows + @:functionCode(' + HKEY hKey; + LONG result; + + std::wstring subkey = std::wstring(key.wchar_str()); + std::wstring valname = std::wstring(string.wchar_str()); + + result = RegOpenKeyExW((HKEY)reinterpret_cast(static_cast(hive)), subkey.c_str(), 0, KEY_READ, &hKey); + if (result != ERROR_SUCCESS) return false; + + DWORD dataType = 0; + result = RegQueryValueExW(hKey, valname.c_str(), NULL, &dataType, NULL, NULL); + + RegCloseKey(hKey); + + return result == ERROR_SUCCESS; + ') + #end + public static function exists(hive:RegistryHive, key:String, string:String):Bool + { + return false; + } + + #if windows + @:functionCode(' + HKEY hKey; + LONG result; + + std::wstring subkey = std::wstring(key.wchar_str()); + std::wstring valname = std::wstring(string.wchar_str()); + + result = RegOpenKeyExW((HKEY)reinterpret_cast(static_cast(hive)), subkey.c_str(), 0, KEY_SET_VALUE, &hKey); + if (result != ERROR_SUCCESS) return false; + + result = RegDeleteValueW(hKey, valname.c_str()); + RegCloseKey(hKey); + + return result == ERROR_SUCCESS; + ') + #end + public static function delete(hive:RegistryHive, key:String, string:String):Bool { + return false; + } +} \ No newline at end of file diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index 8fbcc793..48392a18 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -114,12 +114,13 @@ final class XMLUtil { * @param parentFolder The parent folder * @param defaultAnimType The default animation type */ - public static function loadSpriteFromXML(spr:FunkinSprite, node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT):FunkinSprite { + public static function loadSpriteFromXML(spr:FunkinSprite, node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, loadGraphic:Bool = true):FunkinSprite { if (parentFolder == null) parentFolder = ""; spr.name = node.getAtt("name"); spr.antialiasing = true; - spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite").getDefault(spr.name)}', null, true)); + if (loadGraphic) + spr.loadSprite(Paths.image('$parentFolder${node.getAtt("sprite").getDefault(spr.name)}', null, true)); spr.spriteAnimType = defaultAnimType; if (node.has.type) { @@ -239,10 +240,10 @@ final class XMLUtil { * @param cl The class to create (advanced) * @param args The arguments to pass to the class (advanced) */ - public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class, ?args:Array):FunkinSprite { + public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class, ?args:Array, loadGraphic:Bool = true):FunkinSprite { if(cl == null) cl = FunkinSprite; if(args == null) args = []; - return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType); + return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType, loadGraphic); } /** diff --git a/source/funkin/backend/utils/ZipUtil.hx b/source/funkin/backend/utils/ZipUtil.hx index 0a8bca20..0e87fd55 100644 --- a/source/funkin/backend/utils/ZipUtil.hx +++ b/source/funkin/backend/utils/ZipUtil.hx @@ -1,5 +1,8 @@ package funkin.backend.utils; +import haxe.zip.Entry; +import haxe.zip.Reader; + #if sys #if (!macro && sys) import openfl.display.BitmapData; @@ -9,8 +12,6 @@ import haxe.Exception; import haxe.Json; import haxe.crypto.Crc32; import haxe.zip.Compress; -import haxe.zip.Entry; -import haxe.zip.Reader; import haxe.zip.Tools; import haxe.zip.Uncompress; import haxe.zip.Writer; @@ -135,12 +136,12 @@ final class ZipUtil { if (prog == null) prog = new ZipProgress(); try { - var curPath:Array = ['$path']; + var curPath:Array = [path]; var destPath:Array = []; if (prefix != "") { prefix = prefix.replace("\\", "/"); - while(prefix.charAt(0) == "/") prefix = prefix.substr(1); - while(prefix.charAt(prefix.length-1) == "/") prefix = prefix.substr(0, prefix.length-1); + while(prefix.charCodeAt(0) == "/".code) prefix = prefix.substr(1); + while(prefix.charCodeAt(prefix.length-1) == "/".code) prefix = prefix.substr(0, prefix.length-1); destPath.push(prefix); } @@ -160,7 +161,7 @@ final class ZipUtil { } else { // is file, put it in the list var zipPath = '$zipPath/$e'; - while(zipPath.charAt(0) == "/") zipPath = zipPath.substr(1); + while(zipPath.charCodeAt(0) == "/".code) zipPath = zipPath.substr(1); files.push(new StrNameLabel('$path/$e', zipPath)); } } @@ -227,20 +228,6 @@ class ZipProgress { public function new() {} } -class ZipReader extends Reader { - public var files:List; - - public override function read() { - if (files != null) return files; - try { - var files = super.read(); - return this.files = files; - } catch(e) { - } - return new List(); - } -} - class ZipWriter extends Writer { public function flush() { o.flush(); @@ -265,4 +252,18 @@ class StrNameLabel { this.label = label; } } -#end \ No newline at end of file +#end + +class ZipReader extends Reader { + public var files:List; + + public override function read() { + if (files != null) return files; + try { + var files = super.read(); + return this.files = files; + } catch(e) { + } + return new List(); + } +} \ No newline at end of file diff --git a/source/funkin/backend/week/Week.hx b/source/funkin/backend/week/Week.hx new file mode 100644 index 00000000..d9f36922 --- /dev/null +++ b/source/funkin/backend/week/Week.hx @@ -0,0 +1,92 @@ +package funkin.backend.week; + +import flixel.math.FlxPoint; +import flixel.util.FlxColor; +import funkin.backend.week.WeekData.WeekCharacter; +import funkin.backend.week.WeekData; +import haxe.xml.Access; + +class Week { + public static function loadWeek(weekName:String, loadCharactersData:Bool = true):WeekData { + var week:Access = null; + try week = new Access(Xml.parse(Assets.getText(Paths.xml('weeks/weeks/$weekName'))).firstElement()) + catch(e) Logs.trace('Cannot parse week "$weekName.xml": ${Std.string(e)}', ERROR); + + if (week == null) return null; + if (!week.has.name) { + Logs.trace('Week "${weekName}" has no name attribute', WARNING); + return null; + } + + var weekObj:WeekData = { + xml: week, + name: week.att.name, + id: weekName, + sprite: week.getAtt('sprite').getDefault(weekName), + chars: [null, null, null], + songs: [], + difficulties: ['easy', 'normal', 'hard'], + bgColor: week.has.bgColor ? FlxColor.fromString(week.getAtt("bgColor")) : Flags.DEFAULT_WEEK_COLOR + }; + + var weekName = weekObj.name; + for (k => song in week.nodes.song) { + if (song == null) continue; + try { + var name = song.innerData.trim(); + if (name == "") { + Logs.trace('Song at index ${k} in week $weekName has no name. Skipping...', WARNING); + continue; + } + weekObj.songs.push({ + name: name, + hide: song.getAtt('hide').getDefault('false') == "true", + displayName: song.getAtt('displayName') + }); + } catch(e) { + Logs.trace('Song at index ${k} in week $weekName cannot contain any other XML nodes in its name.', WARNING); + continue; + } + } + + if (weekObj.songs.length <= 0) { + Logs.trace('Week $weekName has no songs.', WARNING); + return null; + } + + var diffNodes = week.nodes.difficulty; + if (diffNodes.length > 0) { + var diffs:Array = [for (e in diffNodes) if (e.has.name) e.att.name]; + if (diffs.length > 0) weekObj.difficulties = diffs; + } + + if (week.has.chars) for (k => e in week.att.chars.split(",")) { + var trim = e.trim(); + weekObj.chars[k] = (trim == "" || e == "none" || e == "null") ? null : (loadCharactersData ? loadWeekCharacter(trim) : {name: trim}); + } + + return weekObj; + } + + public static function loadWeekCharacter(charName:String):WeekCharacter { + var char:Access = null; + try char = new Access(Xml.parse(Assets.getText(Paths.xml('weeks/characters/$charName'))).firstElement()) + catch(e) Logs.trace('Cannot parse character "$charName.xml": ${Std.string(e)}', ERROR); + if (char == null) return null; + + if (!char.has.name) char.x.set("name", charName); + if (!char.has.sprite) char.x.set("sprite", 'menus/storymenu/characters/${char.att.name}'); + if (!char.has.updateHitbox) char.x.set("updateHitbox", "true"); + + return { + xml: char, + name: char.att.name, + spritePath: char.att.sprite, + scale: Std.parseFloat(char.getAtt('scale')).getDefault(1), + offset: FlxPoint.get( + Std.parseFloat(char.getAtt('x')).getDefault(0), + Std.parseFloat(char.getAtt('y')).getDefault(0) + ) + }; + } +} \ No newline at end of file diff --git a/source/funkin/backend/week/WeekData.hx b/source/funkin/backend/week/WeekData.hx new file mode 100644 index 00000000..7be40175 --- /dev/null +++ b/source/funkin/backend/week/WeekData.hx @@ -0,0 +1,31 @@ +package funkin.backend.week; + +import flixel.math.FlxPoint; +import flixel.util.FlxColor; +import haxe.xml.Access; + +typedef WeekData = { + var ?xml:Access; + var name:String; // name SHOULD NOT be used for loading week highscores, its just the name on the right side of the week, remember that next time!! - Nex + var id:String; // id IS instead for saving and loading!! - Nex + var sprite:String; + var chars:Array; + var songs:Array; + var difficulties:Array; + var bgColor:FlxColor; +} + +typedef WeekCharacter = { + var ?xml:Access; + var name:String; + var ?spritePath:String; + var ?scale:Float; + var ?offset:FlxPoint; + // var frames:FlxFramesCollection; +} + +typedef WeekSong = { + var name:String; + var hide:Bool; + var ?displayName:String; +} \ No newline at end of file diff --git a/source/funkin/editors/EditorTreeMenu.hx b/source/funkin/editors/EditorTreeMenu.hx index 6a22e54e..7bf5a0fb 100644 --- a/source/funkin/editors/EditorTreeMenu.hx +++ b/source/funkin/editors/EditorTreeMenu.hx @@ -7,6 +7,7 @@ import funkin.options.*; class EditorTreeMenu extends TreeMenu { public var bg:FlxBackdrop; public var bgType:String = "default"; + public var bgMovement:FlxPoint = new FlxPoint(); public override function create() { super.create(); @@ -18,16 +19,16 @@ class EditorTreeMenu extends TreeMenu { bg = new FlxBackdrop(); bg.loadGraphic(Paths.image('editors/bgs/${bgType}')); bg.antialiasing = true; + setBackgroundRotation(-5); add(bg); } + public inline function setBackgroundRotation(rotation:Float) { + bg.rotation = rotation; + bg.velocity.set(85, 0).degrees = bg.rotation; + } + public override function exit() { FlxG.switchState(new MainMenuState()); } - - public override function update(elapsed:Float) { - super.update(elapsed); - bg.x += elapsed * 125; - bg.y += elapsed * 125; - } } \ No newline at end of file diff --git a/source/funkin/editors/SaveWarning.hx b/source/funkin/editors/SaveWarning.hx index b475e3e2..b56e5775 100644 --- a/source/funkin/editors/SaveWarning.hx +++ b/source/funkin/editors/SaveWarning.hx @@ -37,7 +37,7 @@ class SaveWarning { if (closingWindow) WindowUtils.resetClosing(); } else { WindowUtils.preventClosing = false; WindowUtils.resetClosing(); - Sys.exit(0); + openfl.system.System.exit(0); } } }, @@ -51,7 +51,7 @@ class SaveWarning { if (closingWindow) WindowUtils.resetClosing(); } else { WindowUtils.preventClosing = false; WindowUtils.resetClosing(); - Sys.exit(0); + openfl.system.System.exit(0); } } } diff --git a/source/funkin/editors/charter/ChartCreationScreen.hx b/source/funkin/editors/charter/ChartCreationScreen.hx index a93a7b36..f6461f4f 100644 --- a/source/funkin/editors/charter/ChartCreationScreen.hx +++ b/source/funkin/editors/charter/ChartCreationScreen.hx @@ -318,7 +318,8 @@ class CompactCharacterButton extends UIButton { autoAlpha = false; charIcon = new HealthIcon(funkin.game.Character.getIconFromCharName(char)); - charIcon.scale.set(0.2, 0.2); + var size = Std.int(150 * 0.2); + charIcon.setUnstretchedGraphicSize(size, size, true); charIcon.updateHitbox(); charIcon.setPosition(10, bHeight/2 - charIcon.height / 2); charIcon.scrollFactor.set(1,1); @@ -330,11 +331,10 @@ class CompactCharacterButton extends UIButton { textBox.antialiasing = true; textBox.onChange = function(char:String) { char = funkin.game.Character.getIconFromCharName(char); - var image = Paths.image("icons/" + char); - if(!Assets.exists(image)) - image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON); - charIcon.loadGraphic(image, true, 150, 150); + charIcon.setIcon(char); + charIcon.setUnstretchedGraphicSize(size, size, true); charIcon.updateHitbox(); + charIcon.setPosition(10, bHeight/2 - charIcon.height / 2); } deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () { diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 7c111dbe..da4ebddc 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1423,8 +1423,11 @@ class Charter extends UIState { for(id=>str in strumLines.members) if (str != null) str.y = strumlineInfoBG.y; + strumlineAddButton.x = 0; strumlineAddButton.y = strumlineInfoBG.y; strumlineLockButton.y = strumlineInfoBG.y; + + strumlineLockButton.text.visible = strumlineLockButton.button.selectable = strumlineLockButton.button.visible = strumLines.members.length > 0; } public override function onResize(width:Int, height:Int) { @@ -1558,7 +1561,7 @@ class Charter extends UIState { #if REGION function _file_exit(_) { if (undos.unsaved) SaveWarning.triggerWarning(); - else {undos = null; FlxG.switchState(new CharterSelection()); PlayState.resetSongInfos(); Charter.instance.__clearStatics();} + else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();} } function _file_save_all(_) saveEverything(); @@ -2145,7 +2148,7 @@ class Charter extends UIState { } @:noCompletion public function __clearStatics() { - selection = null; undos = null; clipboard = null; playtestInfo = null; + selection = null; undos = null; clipboard = null; playtestInfo = null; waveformHandler.destroy(); Charter.waveformHandler = null; autoSaveTimer = 0; } diff --git a/source/funkin/editors/charter/CharterAutoSaveUI.hx b/source/funkin/editors/charter/CharterAutoSaveUI.hx index 9ed9d692..37391f1a 100644 --- a/source/funkin/editors/charter/CharterAutoSaveUI.hx +++ b/source/funkin/editors/charter/CharterAutoSaveUI.hx @@ -105,7 +105,7 @@ class CharterAutoSaveUI extends UISliceSprite { bar.follow(this, 10, bHeight-20); bar.alpha = alpha; } - progressBar.clipRect = progressBar.clipRect.set(0, 0, progressBar.width*progress,progressBar.height); + progressBar.rawClipRect = progressBar.rawClipRect.set(0, 0, progressBar.frameWidth*progress,progressBar.frameHeight); } public function appearAnimation() { diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx index 07f5004e..67864d42 100644 --- a/source/funkin/editors/charter/CharterEventScreen.hx +++ b/source/funkin/editors/charter/CharterEventScreen.hx @@ -202,11 +202,11 @@ class CharterEventScreen extends UISubstateWindow { public function saveCurTab() { if (curEvent < 0) return; - events[curEvent].params = [ - for(p in paramsFields) { + var dataParams = EventsData.getEventParams(events[curEvent].name); + var params:Array = [ + for (i => p in paramsFields) { if (p is UIDropDown) { - var dataParams = EventsData.getEventParams(events[curEvent].name); - if (dataParams[paramsFields.indexOf(p)].type == TStrumLine) cast(p, UIDropDown).index; + if (dataParams[i].type == TStrumLine) cast(p, UIDropDown).index; else cast(p, UIDropDown).label.text; } else if (p is UINumericStepper) { @@ -227,6 +227,14 @@ class CharterEventScreen extends UISubstateWindow { null; } ]; + + while(dataParams.length > 0 && { + var index = params.length - 1; + var dataParam = dataParams[index]; + dataParam.saveIfDefault == false && params[index] == dataParam.defValue; + }) params.pop(); + + events[curEvent].params = params; } } diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx index 6ba0fc62..7cf4cede 100644 --- a/source/funkin/editors/charter/CharterMetaDataScreen.hx +++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx @@ -3,6 +3,7 @@ package funkin.editors.charter; import flixel.math.FlxPoint; import funkin.backend.chart.ChartData.ChartMetaData; import funkin.editors.extra.PropertyButton; +import funkin.game.HealthIcon; using StringTools; @@ -19,7 +20,7 @@ class CharterMetaDataScreen extends UISubstateWindow { public var displayNameTextBox:UITextBox; public var iconTextBox:UITextBox; - public var iconSprite:FlxSprite; + public var iconSprite:HealthIcon; public var opponentModeCheckbox:UICheckbox; public var coopAllowedCheckbox:UICheckbox; public var colorWheel:UIColorwheel; @@ -84,7 +85,7 @@ class CharterMetaDataScreen extends UISubstateWindow { coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, "Co-op Mode", metadata.coopAllowed); add(coopAllowedCheckbox); - colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.parsedColor); + colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color); add(colorWheel); addLabelOn(colorWheel, "Color"); @@ -124,22 +125,14 @@ class CharterMetaDataScreen extends UISubstateWindow { } function updateIcon(icon:String) { - if (iconSprite == null) add(iconSprite = new FlxSprite()); + if (iconSprite == null) add(iconSprite = new HealthIcon()); - if (iconSprite.animation.exists(icon)) return; - @:privateAccess iconSprite.animation.clearAnimations(); - - var path:String = Paths.image('icons/$icon'); - if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON); - - iconSprite.loadGraphic(path, true, 150, 150); - iconSprite.animation.add(icon, [0], 0, false); - iconSprite.antialiasing = true; - iconSprite.animation.play(icon); - - iconSprite.scale.set(0.5, 0.5); + iconSprite.setIcon(icon); + var size = Std.int(150 * 0.5); + iconSprite.setUnstretchedGraphicSize(size, size, true); iconSprite.updateHitbox(); - iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2)); + iconSprite.setPosition(iconTextBox.x + iconTextBox.bWidth + 8, iconTextBox.y + (iconTextBox.bHeight / 2) - (iconSprite.height / 2)); + iconSprite.scrollFactor.set(1, 1); } public function saveMeta() { @@ -157,8 +150,7 @@ class CharterMetaDataScreen extends UISubstateWindow { stepsPerBeat: Std.int(stepsPerBeatStepper.value), displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, - color: colorWheel.curColorString, - parsedColor: colorWheel.curColor, + color: colorWheel.curColor, opponentModeAllowed: opponentModeCheckbox.checked, coopAllowed: coopAllowedCheckbox.checked, difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()], diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx index e16b0a9c..4d518735 100644 --- a/source/funkin/editors/charter/CharterSelection.hx +++ b/source/funkin/editors/charter/CharterSelection.hx @@ -1,5 +1,6 @@ package funkin.editors.charter; +import funkin.backend.chart.Chart; import funkin.backend.chart.ChartData.ChartMetaData; import funkin.backend.chart.ChartData; import funkin.backend.system.framerate.Framerate; @@ -34,16 +35,20 @@ class CharterSelection extends EditorTreeMenu { FlxG.switchState(new Charter(s.name, d)); }) ]; - list.push(new NewOption("New Difficulty", "Press ACCEPT to create a new difficulty.", function() { + #if sys + list.push(new NewOption("New Difficulty", "New Difficulty", function() { FlxG.state.openSubState(new ChartCreationScreen(saveChart)); })); + #end optionsTree.add(new OptionsScreen(s.name, "Select a difficulty to continue.", list)); - }, s.parsedColor.getDefault(0xFFFFFFFF)) + }, s.color.getDefault(0xFFFFFFFF)) ]; - list.insert(0, new NewOption("New Song", "Press ACCEPT to create a new song.", function() { + #if sys + list.insert(0, new NewOption("New Song", "New Song", function() { FlxG.state.openSubState(new SongCreationScreen(saveSong)); })); + #end main = new OptionsScreen("Chart Editor", "Select a song to modify the charts from.", list); @@ -88,11 +93,12 @@ class CharterSelection extends EditorTreeMenu { } } - public function saveSong(creation:SongCreationData) { + #if sys + public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) { var songAlreadyExists:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase()); if (songAlreadyExists) { - openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create already exists, if you would like to override it delete the song first!", [ + openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create Already exists, if you would like to override it delete the song first!", [ {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} ])); return; @@ -109,11 +115,17 @@ class CharterSelection extends EditorTreeMenu { sys.FileSystem.createDirectory('$songFolder/charts'); // Save Files - CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, Flags.JSON_PRETTY_PRINT)); + CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(creation.meta)); if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes); if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes); + + if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player.${Flags.SOUND_EXT}', creation.playerVocals); + if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals); #end + if (callback != null) + callback(songFolder, creation); + var option = new EditorIconOption(creation.meta.name, "Press ACCEPT to choose a difficulty to edit.", creation.meta.icon, function() { curSong = creation.meta; var list:Array = [ @@ -122,11 +134,11 @@ class CharterSelection extends EditorTreeMenu { FlxG.switchState(new Charter(creation.meta.name, d)); }) ]; - list.push(new NewOption("New Difficulty", "Press ACCEPT to create a new difficulty.", function() { + list.push(new NewOption("New Difficulty", "New Difficulty", function() { FlxG.state.openSubState(new ChartCreationScreen(saveChart)); })); optionsTree.insert(1, new OptionsScreen(creation.meta.name, "Select a difficulty to continue.", list)); - }, creation.meta.parsedColor.getDefault(0xFFFFFFFF)); + }, creation.meta.color.getDefault(0xFFFFFFFF)); // Add to List freeplayList.songs.insert(0, creation.meta); @@ -160,7 +172,8 @@ class CharterSelection extends EditorTreeMenu { var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json')); if (meta.difficulties != null && !meta.difficulties.contains(name)) { meta.difficulties.push(name); - CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(meta)); + CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(meta)); } } + #end } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterStrumline.hx b/source/funkin/editors/charter/CharterStrumline.hx index 22caa289..2d4a4d78 100644 --- a/source/funkin/editors/charter/CharterStrumline.hx +++ b/source/funkin/editors/charter/CharterStrumline.hx @@ -88,8 +88,7 @@ class CharterStrumline extends UISprite { button = new CharterStrumlineOptions(this); members.push(button); - vocals = strumLine.vocalsSuffix.length > 0 ? FlxG.sound.load(Paths.voices(PlayState.SONG.meta.name, PlayState.difficulty, strumLine.vocalsSuffix)) : new FlxSound(); - vocals.group = FlxG.sound.defaultMusicGroup; + updateInfo(); selectedWaveform = -1; } diff --git a/source/funkin/editors/charter/CharterStrumlineButton.hx b/source/funkin/editors/charter/CharterStrumlineButton.hx index 39207e5f..a1955248 100644 --- a/source/funkin/editors/charter/CharterStrumlineButton.hx +++ b/source/funkin/editors/charter/CharterStrumlineButton.hx @@ -59,7 +59,7 @@ class CharterStrumlineButton extends UISprite { super.update(elapsed); button.updateSpriteRect(); - if(UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect)) { + if (UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect) && button.visible) { buttonScale.set(0.95, 0.95); if (FlxG.mouse.justPressed && onClick != null) { onClick(); diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx index 8d4517d9..cd19059e 100644 --- a/source/funkin/editors/charter/CharterStrumlineScreen.hx +++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx @@ -242,7 +242,8 @@ class CharacterButton extends UIButton { super(x, y, null, null, 250, 54); charIcon = new HealthIcon(Character.getIconFromCharName(char)); - charIcon.scale.set(0.3, 0.3); + var size = Std.int(150 * 0.3); + charIcon.setUnstretchedGraphicSize(size, size, true); charIcon.updateHitbox(); charIcon.setPosition(x + 10, bHeight/2 - charIcon.height / 2); charIcon.scrollFactor.set(1,1); @@ -254,11 +255,10 @@ class CharacterButton extends UIButton { textBox.antialiasing = true; textBox.onChange = function(char:String) { char = Character.getIconFromCharName(char); - var image = Paths.image("icons/" + char); - if(!Assets.exists(image)) - image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON); - charIcon.loadGraphic(image, true, 150, 150); + charIcon.setIcon(char); + charIcon.setUnstretchedGraphicSize(size, size, true); charIcon.updateHitbox(); + charIcon.setPosition(x + 10, bHeight/2 - charIcon.height / 2); } deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () { diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 3548171d..167147d0 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -3,17 +3,26 @@ package funkin.editors.charter; import flixel.group.FlxGroup; import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormatMarkerPair; -import funkin.backend.chart.ChartData.ChartMetaData; +import funkin.backend.chart.ChartData; +import funkin.backend.chart.FNFLegacyParser.SwagSong; +import funkin.backend.chart.PsychParser; +import funkin.backend.chart.VSliceParser; +import funkin.backend.utils.ZipUtil.ZipReader; +import funkin.game.HealthIcon; +import haxe.Json; import haxe.io.Bytes; +import haxe.io.BytesInput; typedef SongCreationData = { var meta:ChartMetaData; var instBytes:Bytes; var voicesBytes:Bytes; + @:optional var playerVocals:Bytes; + @:optional var oppVocals:Bytes; } class SongCreationScreen extends UISubstateWindow { - private var onSave:Null -> Void = null; + private var onSave:Null -> Null SongCreationData -> Void> -> Void = null; public var songNameTextBox:UITextBox; public var bpmStepper:UINumericStepper; @@ -21,15 +30,26 @@ class SongCreationScreen extends UISubstateWindow { public var stepsPerBeatStepper :UINumericStepper; public var instExplorer:UIFileExplorer; public var voicesExplorer:UIFileExplorer; + public var importFrom:UIButton; public var displayNameTextBox:UITextBox; public var iconTextBox:UITextBox; - public var iconSprite:FlxSprite; + public var iconSprite:HealthIcon; public var opponentModeCheckbox:UICheckbox; public var coopAllowedCheckbox:UICheckbox; public var colorWheel:UIColorwheel; public var difficultiesTextBox:UITextBox; + public var engineDropdown:UIDropDown; + public var createSong:UIButton; + + public var importInstExplorer:UIFileExplorer; + public var importVoicesExplorer:UIFileExplorer; + + public var importIdTextBox:UITextBox; + public var importChartFile:UIFileExplorer; + public var importMetaFile:UIFileExplorer; + public var backButton:UIButton; public var saveButton:UIButton; public var closeButton:UIButton; @@ -37,11 +57,21 @@ class SongCreationScreen extends UISubstateWindow { public var songDataGroup:FlxGroup = new FlxGroup(); public var menuDataGroup:FlxGroup = new FlxGroup(); + public var selectFormatGroup:FlxGroup = new FlxGroup(); + public var importAudioGroup:FlxGroup = new FlxGroup(); + public var importDataGroup:FlxGroup = new FlxGroup(); + public var pages:Array = []; public var pageSizes:Array = []; + + public var importPages:Array = []; + public var importPageSizes:Array = []; + + public var isImporting = false; + public var curPage:Int = 0; - public function new(?onSave:SongCreationData->Void) { + public function new(?onSave:SongCreationData -> Null SongCreationData -> Void> -> Void) { super(); if (onSave != null) this.onSave = onSave; } @@ -80,8 +110,6 @@ class SongCreationScreen extends UISubstateWindow { stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); songDataGroup.add(stepsPerBeatStepper); - var voicesUIText:UIText = null; - instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (path, res) { if (path == null || res == null) return; var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res); @@ -102,6 +130,14 @@ class SongCreationScreen extends UISubstateWindow { songDataGroup.add(voicesExplorer); addLabelOn(voicesExplorer, "Vocal Audio File"); + importFrom = new UIButton(windowSpr.x + 20, windowSpr.y + windowSpr.bHeight - 16 - 32, "Import From...", function() { + winTitle = "Importing Song"; + isImporting = true; + updatePagesTexts(); + refreshPages(); + }, 150); + songDataGroup.add(importFrom); + var menuTitle:UIText; menuDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Menus Data (Freeplay/Story)", 28)); @@ -134,7 +170,66 @@ class SongCreationScreen extends UISubstateWindow { for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox]) {checkbox.y += 6; checkbox.x += 4;} + var menuTitle:UIText; + selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Import From:", 28)); + + selectFormatGroup.add(engineDropdown); + addLabelOn(engineDropdown, "Chart Format"); + + createSong = new UIButton(windowSpr.x + 20, windowSpr.y + windowSpr.bHeight - 16 - 32, "< Back", function() { + winTitle = "Creating New Song"; + isImporting = false; + updatePagesTexts(); + refreshPages(); + }, 120); + selectFormatGroup.add(createSong); + + var menuTitle:UIText; + importAudioGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Add Audios", 28)); + + importInstExplorer = new UIFileExplorer(menuTitle.x, menuTitle.y + menuTitle.height + 36, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; + var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importInstExplorer.x + 8, importInstExplorer.y + 8, res); + importInstExplorer.members.push(audioPlayer); + importInstExplorer.uiElement = audioPlayer; + }); + importAudioGroup.add(importInstExplorer); + addLabelOn(importInstExplorer, "Inst Audio File").applyMarkup( + "Inst Audio File $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + importVoicesExplorer = new UIFileExplorer(importInstExplorer.x + 320 + 26, importInstExplorer.y, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; + var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importVoicesExplorer.x + 8, importVoicesExplorer.y + 8, res); + importVoicesExplorer.members.push(audioPlayer); + importVoicesExplorer.uiElement = audioPlayer; + }); + importAudioGroup.add(importVoicesExplorer); + addLabelOn(importVoicesExplorer, "Vocal Audio File"); + + var menuTitle:UIText; + importDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Add Data", 28)); + + importDataGroup.add(importIdTextBox = new UITextBox(menuTitle.x, menuTitle.y + menuTitle.height + 36)); + addLabelOn(importIdTextBox, "Song file name").applyMarkup( + "Song file name $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + importChartFile = new UIFileExplorer(importIdTextBox.x, importIdTextBox.y + importIdTextBox.height + 56, null, null, "fnfc", function (_, _) importIdTextBox.label.text = new haxe.io.Path(importChartFile.filePath).file); + importDataGroup.add(importChartFile); + addLabelOn(importChartFile, "Data/Chart File").applyMarkup( + "Data/Chart File $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + importMetaFile = new UIFileExplorer(importChartFile.x + 320 + 26, importChartFile.y, null, null, "json"); + importDataGroup.add(importMetaFile); + addLabelOn(importMetaFile, "Meta File").applyMarkup( + "Meta File $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + engineDropdown = new UIDropDown(menuTitle.x, menuTitle.y + menuTitle.height + 36, 480, 32, ["Psych/Legacy FNF", "V-Slice", "V-Slice Project (.fnfc)"]); saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + var pages = isImporting ? importPages : pages; if (curPage == pages.length-1) { saveSongInfo(); close(); @@ -167,17 +262,41 @@ class SongCreationScreen extends UISubstateWindow { pages.push(cast add(menuDataGroup)); pageSizes.push(FlxPoint.get(748 - 32 + 40, 400)); + importPages.push(cast add(selectFormatGroup)); + importPageSizes.push(FlxPoint.get(500, 250)); + + importPages.push(cast add(importAudioGroup)); + importPageSizes.push(FlxPoint.get(705, 250)); + + importPages.push(cast add(importDataGroup)); + importPageSizes.push(FlxPoint.get(705, 300)); + refreshPages(); updatePagesTexts(); } public override function update(elapsed:Float) { - if (curPage == 0) { - if (instExplorer.file != null) + if (isImporting) + { + var name = engineDropdown.options[engineDropdown.index]; + var project = name == "V-Slice Project (.fnfc)"; + + if (curPage == 1) { + importInstExplorer.selectable = importVoicesExplorer.selectable = !project; + saveButton.selectable = project ? true : (importInstExplorer.file != null); + } else if (curPage == 2) { + importIdTextBox.selectable = !project; + importChartFile.fileType = project ? "fnfc" : "json"; + importMetaFile.selectable = name == "V-Slice"; + saveButton.selectable = importChartFile.file != null && (!importMetaFile.selectable || importMetaFile.file != null) && (!importIdTextBox.selectable || importIdTextBox.label.text.trim().length > 0); + } else saveButton.selectable = true; - else saveButton.selectable = false; - } else - saveButton.selectable = true; + } else { + if (curPage == 0) + saveButton.selectable = (instExplorer.file != null); + else + saveButton.selectable = true; + } saveButton.alpha = saveButton.field.alpha = saveButton.selectable ? 1 : 0.4; super.update(elapsed); @@ -185,10 +304,15 @@ class SongCreationScreen extends UISubstateWindow { function refreshPages() { for (i=>page in pages) - page.visible = page.exists = i == curPage; + page.visible = page.exists = i == curPage && !isImporting; + + for (i=>page in importPages) + page.visible = page.exists = i == curPage && isImporting; } function updatePagesTexts() { + var pageSizes = isImporting ? importPageSizes : pageSizes; + var pages = isImporting ? importPages : pages; windowSpr.bWidth = Std.int(pageSizes[curPage].x); windowSpr.bHeight = Std.int(pageSizes[curPage].y); @@ -196,7 +320,7 @@ class SongCreationScreen extends UISubstateWindow { titleSpr.y = windowSpr.y + ((30 - titleSpr.height) / 2); saveButton.field.text = curPage == pages.length-1 ? "Save & Close" : 'Next >'; - titleSpr.text = 'Creating New Song (${curPage+1}/${pages.length})'; + titleSpr.text = '$winTitle (${curPage+1}/${pages.length})'; backButton.field.text = '< Back'; backButton.visible = backButton.exists = curPage > 0; @@ -204,51 +328,121 @@ class SongCreationScreen extends UISubstateWindow { backButton.x = (saveButton.x = windowSpr.x + windowSpr.bWidth - 20 - 125) - 20 - saveButton.bWidth; closeButton.x = (curPage > 0 ? backButton : saveButton).x - 20 - saveButton.bWidth; - for (button in [saveButton, backButton, closeButton]) + for (button in [saveButton, backButton, closeButton, importFrom, createSong]) button.y = windowSpr.y + windowSpr.bHeight - 16 - 32; } function updateIcon(icon:String) { - if (iconSprite == null) menuDataGroup.add(iconSprite = new FlxSprite()); + if (iconSprite == null) menuDataGroup.add(iconSprite = new HealthIcon()); - if (iconSprite.animation.exists(icon)) return; - @:privateAccess iconSprite.animation.clearAnimations(); - - var path:String = Paths.image('icons/$icon'); - if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON); - - iconSprite.loadGraphic(path, true, 150, 150); - iconSprite.animation.add(icon, [0], 0, false); - iconSprite.antialiasing = true; - iconSprite.animation.play(icon); - - iconSprite.scale.set(0.5, 0.5); + iconSprite.setIcon(icon); + var size = Std.int(150 * 0.5); + iconSprite.setUnstretchedGraphicSize(size, size, true); iconSprite.updateHitbox(); - iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2)); + iconSprite.setPosition(iconTextBox.x + iconTextBox.bWidth + 8, iconTextBox.y + (iconTextBox.bHeight / 2) - (iconSprite.height / 2)); + iconSprite.scrollFactor.set(1, 1); } function saveSongInfo() { - UIUtil.confirmUISelections(this); + if (isImporting) + { + try switch(engineDropdown.options[engineDropdown.index]) + { + case "V-Slice Project (.fnfc)": + var files:Map = []; + for (field in new ZipReader(new BytesInput(importChartFile.file)).read()) { + var fileName = field.fileName; + var fileContent = field.data; + files.set(fileName, fileContent); + } + saveFromVSlice(files); + case "V-Slice": + var songId = importIdTextBox.label.text; + var files:Map = []; + files.set('${songId}-metadata.json', importMetaFile.file); + files.set('${songId}-chart.json', importChartFile.file); + files.set('Inst.${Flags.SOUND_EXT}', importInstExplorer.file); + files.set('Voices.${Flags.SOUND_EXT}', importVoicesExplorer.file); + saveFromVSlice(files, songId); + default /*"Psych/Legacy FNF"*/: + var songId = importIdTextBox.label.text; + var oldChart:SwagSong = Json.parse(cast importChartFile.file); + var base:ChartData = { + strumLines: [], + noteTypes: [], + events: [], + meta: {name: null}, + scrollSpeed: Flags.DEFAULT_SCROLL_SPEED, + stage: Flags.DEFAULT_STAGE, + codenameChart: true + }; + PsychParser.parse(oldChart, base); + if (onSave != null) onSave({ + meta: { + name: songId, + difficulties: [songId], + bpm: oldChart.bpm, + beatsPerMeasure: oldChart.beatsPerMeasure, + stepsPerBeat: oldChart.stepsPerBeat, + displayName: oldChart.song + }, + instBytes: importInstExplorer.file, + voicesBytes: importVoicesExplorer.file, + }, (songFolder:String, creation:SongCreationData) -> { + #if sys + CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT)); + #end + }); + } catch (e) { + openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", Std.string(e), [ + {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} + ])); + } + } else { + for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper]) + @:privateAccess stepper.__onChange(stepper.label.text); - var meta:ChartMetaData = { - name: songNameTextBox.label.text, - bpm: bpmStepper.value, - beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), - stepsPerBeat: Std.int(stepsPerBeatStepper.value), - displayName: displayNameTextBox.label.text, - icon: iconTextBox.label.text, - color: colorWheel.curColorString, - parsedColor: colorWheel.curColor, - opponentModeAllowed: opponentModeCheckbox.checked, - coopAllowed: coopAllowedCheckbox.checked, - difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()], - }; + var meta:ChartMetaData = { + name: songNameTextBox.label.text, + bpm: bpmStepper.value, + beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), + stepsPerBeat: Std.int(stepsPerBeatStepper.value), + displayName: displayNameTextBox.label.text, + icon: iconTextBox.label.text, + color: colorWheel.curColor, + opponentModeAllowed: opponentModeCheckbox.checked, + coopAllowed: coopAllowedCheckbox.checked, + difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()], + }; + + if (onSave != null) onSave({ + meta: meta, + instBytes: instExplorer.file, + voicesBytes: voicesExplorer.file + }, null); + } + } + + function saveFromVSlice(files:Map, ?name:String) { + var songId = name == null && files.exists("manifest.json") ? Json.parse(files.get("manifest.json")).songId : name; + var vslicemeta:SwagMetadata = Json.parse(files.get('${songId}-metadata.json')); + var vslicechart:NewSwagSong = Json.parse(files.get('${songId}-chart.json')); + var playData = vslicemeta.playData; + + var meta:ChartMetaData = {name: songId}; + var diffCharts:Array = []; + VSliceParser.parse(vslicemeta, vslicechart, meta, diffCharts, songId); if (onSave != null) onSave({ meta: meta, - instBytes: instExplorer.file, - voicesBytes: voicesExplorer.file + instBytes: files.get('Inst.${Flags.SOUND_EXT}'), + voicesBytes: files.get('Voices.${Flags.SOUND_EXT}'), //it may exist + playerVocals: files.get('Voices-${playData.characters.playerVocals != null ? playData.characters.playerVocals[0] : playData.characters.player}.${Flags.SOUND_EXT}'), + oppVocals: files.get('Voices-${playData.characters.opponentVocals != null ? playData.characters.opponentVocals[0] : playData.characters.opponent}.${Flags.SOUND_EXT}'), + }, (songFolder:String, creation:SongCreationData) -> { + #if sys + for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/charts/${diff.diffName}.json', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT)); + #end }); } - } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIAudioPlayer.hx b/source/funkin/editors/ui/UIAudioPlayer.hx index ed567b59..4b87dd4d 100644 --- a/source/funkin/editors/ui/UIAudioPlayer.hx +++ b/source/funkin/editors/ui/UIAudioPlayer.hx @@ -54,6 +54,7 @@ class UIAudioPlayer extends UIButton { timeBarPlayer = new FlxSprite(timeBar.x, timeBar.y).loadGraphic(Paths.image('editors/ui/audio-time-empty')); timeBarPlayer.colorTransform.color = 0x440364; timeBarPlayer.scale.y = (timeBarPlayer.frameHeight + 2) / timeBarPlayer.frameHeight; + timeBarPlayer.pixelPerfectRender = true; members.push(timeBarPlayer); timeBarSpr = cast new UISprite(timeBar.x, timeBar.y).makeSolid(timeBar.barWidth, timeBar.barHeight, 0x00FFFFFF); @@ -87,13 +88,13 @@ class UIAudioPlayer extends UIButton { playingSprite.animation.play(sound.playing ? "playing" : "paused"); timeText.text = '${FlxStringUtil.formatTime(sound.time/1000, true)} / ${FlxStringUtil.formatTime(sound.length/1000)}'; - if(timeBarPlayer.clipRect == null) - timeBarPlayer.clipRect = new FlxRect(0, 0, timeBarPlayer.frameWidth, timeBarPlayer.frameHeight); + if(timeBarPlayer.rawClipRect == null) + timeBarPlayer.rawClipRect = new FlxRect(0, 0, timeBarPlayer.frameWidth, timeBarPlayer.frameHeight); - timeBarPlayer.clipRect.x = timeBarPlayer.frameWidth * (sound.time/sound.length); - timeBarPlayer.clipRect.width = 2; + timeBarPlayer.rawClipRect.x = timeBarPlayer.frameWidth * (sound.time/sound.length); + timeBarPlayer.rawClipRect.width = 2; - timeBarPlayer.clipRect = timeBarPlayer.clipRect; + timeBarPlayer.rawClipRect = timeBarPlayer.rawClipRect; nextPlayerColor = sound.playing ? 0x732D95 : 0x440364; timeBarPlayer.colorTransform.color = FlxColor.interpolate(timeBarPlayer.colorTransform.color, nextPlayerColor, 1/14); diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx index 6e7c8f81..4f75fd6e 100644 --- a/source/funkin/editors/ui/UIFileExplorer.hx +++ b/source/funkin/editors/ui/UIFileExplorer.hx @@ -15,16 +15,19 @@ class UIFileExplorer extends UISliceSprite { public var onFile:(String, Bytes)->Void; public var uiElement:FlxSprite; + + public var fileType:String = "txt"; public function new(x:Float, y:Float, ?w:Int, ?h:Int, fileType:String = "txt", ?onFile:(String, Bytes)->Void) { super(x, y, (w != null ? w : 320), (h != null ? h : 58), 'editors/ui/inputbox'); + this.fileType = fileType; if (onFile != null) this.onFile = onFile; uploadButton = new UIButton(x + 8, y+ 8, null, function () { var fileDialog = new FileDialog(); fileDialog.onSelect.add(loadFile); - fileDialog.browse(OPEN, fileType); + fileDialog.browse(OPEN, this.fileType); }, bWidth - 16, bHeight - 16); members.push(uploadButton); @@ -66,6 +69,7 @@ class UIFileExplorer extends UISliceSprite { } public function removeFile() { + if (!selectable) return; if (uiElement != null) { members.remove(uiElement); uiElement.destroy(); diff --git a/source/funkin/game/GameOverSubstate.hx b/source/funkin/game/GameOverSubstate.hx index 2d7af30d..706b798c 100644 --- a/source/funkin/game/GameOverSubstate.hx +++ b/source/funkin/game/GameOverSubstate.hx @@ -101,30 +101,8 @@ class GameOverSubstate extends MusicBeatSubstate if (__cancelDefault) return; - if (controls.ACCEPT) - { - endBullshit(); - } - - if (controls.BACK) - { - if (PlayState.chartingMode && Charter.undos.unsaved) - game.saveWarn(false); - else { - PlayState.resetSongInfos(); - if (Charter.instance != null) Charter.instance.__clearStatics(); - - if (FlxG.sound.music != null) - FlxG.sound.music.stop(); - FlxG.sound.music = null; - - if (PlayState.isStoryMode) - FlxG.switchState(new StoryMenuState()); - else - FlxG.switchState(new FreeplayState()); - } - - } + if (controls.ACCEPT) endBullshit(); + if (controls.BACK) exit(); if (!isEnding && ((!lossSFX.playing) || (character.getAnimName() == "firstDeath" && character.isAnimFinished())) && (FlxG.sound.music == null || !FlxG.sound.music.playing)) { @@ -193,6 +171,25 @@ class GameOverSubstate extends MusicBeatSubstate }); } + function exit() + { + var event = new CancellableEvent(); + gameoverScript.call('onReturnToMenu', [event]); + + if (event.cancelled) + return; + + if (PlayState.chartingMode && Charter.undos.unsaved) game.saveWarn(false); + else { + if (Charter.instance != null) Charter.instance.__clearStatics(); + + if (FlxG.sound.music != null) FlxG.sound.music.stop(); + FlxG.sound.music = null; + + FlxG.switchState(PlayState.isStoryMode ? new StoryMenuState() : new FreeplayState()); + } + } + override function destroy() { gameoverScript.call("destroy"); diff --git a/source/funkin/game/HealthIcon.hx b/source/funkin/game/HealthIcon.hx index 5d2c8225..935eefb1 100644 --- a/source/funkin/game/HealthIcon.hx +++ b/source/funkin/game/HealthIcon.hx @@ -1,12 +1,32 @@ package funkin.game; +import flixel.graphics.FlxGraphic; +import flixel.math.FlxPoint; +import flixel.util.typeLimit.OneOfTwo; +import funkin.backend.scripting.events.healthicon.HealthIconChangeEvent; -class HealthIcon extends FlxSprite +class HealthIcon extends FunkinSprite { /** - * Used for FreeplayState! If you use it elsewhere, prob gonna annoying + * Attaches the icon to a sprite, following it's position */ public var sprTracker:FlxSprite; + /** + * Where to place the icon in relation to the sprite + * + * LEFT: Left of the sprite + * + * CENTER: Center of the sprite + * + * RIGHT: Right of the sprite + */ + public var sprTrackerAlignment:TrackerAlignment = RIGHT; + /** + * Offset of the icon in relation to the sprite + * + * By default it is set to (10, -30) and is intended to be used with Alphabet + */ + public var sprTrackerOffset:FlxPoint = new FlxPoint(10, -30); /** * The currently showing icon @@ -20,31 +40,60 @@ class HealthIcon extends FlxSprite /** * Health steps in this format: - * Min Percentage => Frame Index + * + * Min Percentage => Frame Index / Animation Name */ - public var healthSteps:Map = null; + public var healthSteps:Map> = null; /** - * current animation state + * Current animation state */ - public var curAnimState:Int = -1; + public var curAnimState:OneOfTwo = -1; + + /** + * The Default Scale For The Icon + * + * This is what scale the icon should return to when its bump animation is finished + */ + public var defaultScale:Float = 1; + + /** + * Whenever or not the icon is animated or not + */ + public var animated:Bool = false; + + /** + * XML Animated Icon data + * + * `null` if the icon is not animated or its invalid + */ + public var xmlData:Xml; + + /** + * Extra offsets to add when updating the hitbox + */ + public var extraOffsets:FlxPoint = FlxPoint.get(); /** * Helper for HScript who can't make maps - * @param steps Something like this: `[[0, 1], [20, 0]]` + * + * THIS IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE + * + * Please set the healthSteps directly in the script instead, hscript does support maps + * + * @param steps Something like this: `[[0, 1], [20, 0]]` or `[[0, "losing"], [20, "neutral"]]` for animated icons */ - public function setHealthSteps(steps:Array>) { // helper for hscript that can't do maps + @:dox(hide) @:noCompletion @:deprecated("use healthSteps instead") + public function setHealthSteps(steps:Array>>):Void { // helper for hscript that can't do maps if (steps == null) return; healthSteps = []; - for(s in steps) + for (s in steps) if (s.length > 1) healthSteps[s[0]] = s[1]; - var am = 0; - for(k=>e in healthSteps) am++; - if (am <= 0) healthSteps = [ - 0 => 1, // losing icon - 20 => 0, // normal icon + if (Lambda.count(healthSteps) <= 0) healthSteps = [ + 0 => (this.animated ? "losing" : 1), // losing icon + 20 => (this.animated ? "neutral" : 0), // normal icon ]; } @@ -58,50 +107,357 @@ class HealthIcon extends FlxSprite scrollFactor.set(); } - public function setIcon(char:String, width:Int = 150, height:Int = 150) { - if(curCharacter != char || this.width != width || this.height != height) { - curCharacter = char; - var path = Paths.image('icons/$char'); - if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON); - - loadGraphic(path, true, width, height); - - animation.add(char, [for(i in 0...frames.frames.length) i], 0, false, isPlayer); - antialiasing = true; - animation.play(char); - - healthSteps = [ - 0 => 1, // losing icon - 20 => 0, // normal icon - ]; - - if (frames.frames.length >= 3) - healthSteps[80] = 2; // winning icon - } + /** + * Called every beat, and causes the icon to become bigger + **/ + public dynamic function bump():Void { + var iconScale = Flags.BOP_ICON_SCALE; + scale.set(defaultScale * iconScale, defaultScale * iconScale); + updateHitbox(); } - override function update(elapsed:Float) - { + /** + * Called every frame and causes the icon to become smaller + **/ + public dynamic function updateBump():Void { + var iconLerp = Flags.ICON_LERP; + scale.set(CoolUtil.fpsLerp(scale.x, defaultScale, iconLerp), CoolUtil.fpsLerp(scale.y, defaultScale, iconLerp)); + updateHitbox(); + } + + /** + * Sets the icon to the specified character + * @param char Character to set the icon to + * @param allowAnimated Whenever the icon can be animated + **/ + public function setIcon(char:String, allowAnimated:Bool = true):Void { + if (curCharacter == char) return; + + var oldIconPath = 'icons/$char'; + var newIconPath = 'icons/$char/icon'; + + if (!Assets.exists(Paths.image(oldIconPath)) && !Assets.exists(Paths.image(newIconPath))) { + char = 'face'; + oldIconPath = 'icons/$char'; + newIconPath = 'icons/$char/icon'; + } + curCharacter = char; + + var iconPath = Assets.exists(Paths.image(oldIconPath)) ? oldIconPath : newIconPath; + var iconXmlPath = Paths.getPath('images/icons/$char/data.xml'); + var iconFoundData = Assets.exists(iconXmlPath); + + try { + xmlData = iconFoundData ? Xml.parse(Assets.getText(iconXmlPath)) : null; + } catch(e) { + Logs.trace('Error while parsing icon data for $char: ${e.message}', ERROR); + xmlData = null; + } + var xmlValid = xmlData != null && (xmlData = xmlData.firstElement()) != null; + + this.animated = allowAnimated && iconFoundData && xmlValid && Paths.framesExists(Paths.image(newIconPath), true, true, true); + + var iconAmt:Int = 0; + var iconSize:Int = 0; + var iconIsPlayer = xmlValid ? xmlData.get("facing").getDefault("right").toLowerCase() == "left" : false; + + animateAtlas = null; // reset + if (this.animated) + loadSprite(Paths.image(newIconPath)); + else { + var iconAsset:FlxGraphic = FlxG.bitmap.add(Paths.image(iconPath)); + var assetW:Float = iconAsset.width; + var assetH:Float = iconAsset.height; + + iconAmt = Math.round(assetW / assetH); // Just in case the icon is in a weird aspect ratio + iconSize = Math.floor(assetW / iconAmt); + if (iconSize * iconAmt > assetW) { + iconSize = Math.floor(assetW / iconAmt); + iconAmt = Math.floor(assetW / iconSize); + } + + loadGraphic(iconAsset, true, Std.int(Math.min(iconSize, assetW)), Std.int(Math.min(iconSize, assetH))); + + setGraphicSize(150); + updateHitbox(); + + animation.add(char, [for(i in 0...iconAmt) i], 0, false, isPlayer != iconIsPlayer); + animation.play(char); + } + + if(!animation.onFinishEnd.has(animFinishCallback)) + animation.onFinishEnd.add(animFinishCallback); + if(animateAtlas != null && !animateAtlas.anim.onFinishEnd.has(animFinishCallback)) + animateAtlas.anim.onFinishEnd.add(animFinishCallback); + + var parsedSteps:Map = []; + + antialiasing = true; + if (xmlValid) { + if (xmlData.exists("antialiasing")) + antialiasing = xmlData.get("antialiasing").toLowerCase() == "true"; + if (xmlData.exists("offsetX")) + extraOffsets.x = Std.parseFloat(xmlData.get("offsetX")).getDefault(0); + if (xmlData.exists("offsetY")) + extraOffsets.y = Std.parseFloat(xmlData.get("offsetY")).getDefault(0); + + for (node in xmlData.elements()) + switch(node.nodeName) { + case "transition": + if (this.animated == false) { + Logs.trace('Icon ${char} data is not allowed when not animated', WARNING); + continue; + } + if (!node.exists("anim")) { + Logs.trace('Icon ${char} data is missing anim', WARNING); + continue; + } + if (!node.exists("to")) { + Logs.trace('Icon ${char} data is missing to', WARNING); + continue; + } + if (!node.exists("from")) { + Logs.trace('Icon ${char} data is missing from', WARNING); + continue; + } + + var animName = 'from-${node.get("from")}-to-${node.get("to")}'; + + var offsetX:Float = 0; + var offsetY:Float = 0; + if (node.exists("offsetX")) + offsetX = Std.parseFloat(node.get("offsetX")).getDefault(0); + else if (node.exists("offsetx")) + offsetX = Std.parseFloat(node.get("offsetx")).getDefault(0); + + if (node.exists("offsetY")) + offsetY = Std.parseFloat(node.get("offsetY")).getDefault(0); + else if (node.exists("offsety")) + offsetY = Std.parseFloat(node.get("offsety")).getDefault(0); + + addOffset(animName, offsetX, offsetY); + + addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), false); // don't allow looping for transitions + if (animateAtlas == null && animation.exists(animName)) + animation.getByName(animName).flipX = isPlayer != iconIsPlayer; + case "anim": + if (this.animated == false) { + Logs.trace('Icon ${char} data is not allowed when not animated', WARNING); + continue; + } + if (!node.exists("name")) { + Logs.trace('Icon ${char} data is missing name', WARNING); + continue; + } + if (!node.exists("anim")) { + Logs.trace('Icon ${char} data is missing anim', WARNING); + continue; + } + + var animName = node.get("name"); + + var offsetX:Float = 0; + var offsetY:Float = 0; + if (node.exists("offsetX")) + offsetX = Std.parseFloat(node.get("offsetX")).getDefault(0); + else if (node.exists("offsetx")) + offsetX = Std.parseFloat(node.get("offsetx")).getDefault(0); + + if (node.exists("offsetY")) + offsetY = Std.parseFloat(node.get("offsetY")).getDefault(0); + else if (node.exists("offsety")) + offsetY = Std.parseFloat(node.get("offsety")).getDefault(0); + + addOffset(animName, offsetX, offsetY); + + var looped:Bool = false; + if (node.exists("looped")) + looped = node.get("looped").toLowerCase() == "true"; + else if (node.exists("loop")) + looped = node.get("loop").toLowerCase() == "true"; + + addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), looped); + if (animateAtlas == null && animation.exists(animName)) + animation.getByName(animName).flipX = isPlayer != iconIsPlayer; + case "step": + if (!node.exists("percent")) { + Logs.trace('Icon ${char} data is missing percent', WARNING); + continue; + } + if (!node.exists("name")) { + Logs.trace('Icon ${char} data is missing name', WARNING); + continue; + } + + parsedSteps.set(Std.parseInt(node.get("percent")).getDefault(0), node.get("name")); + } + } + + if (Lambda.count(parsedSteps) > 0) { + healthSteps = parsedSteps; + } else { + var hasLosing = this.animated ? hasAnim("losing") : iconAmt >= 2; + var hasWinning = this.animated ? hasAnim("winning") : iconAmt >= 3; + + if (hasLosing) { + healthSteps = [ + 0 => (this.animated ? "losing" : 1), // losing icon + 20 => (this.animated ? "neutral" : 0), // normal icon + ]; + } else { + healthSteps = [ + 0 => (this.animated ? "neutral" : 0), // normal icon + ]; + } + if (hasWinning) + healthSteps.set(80, this.animated ? "winning" : 2); // winning icon + } + var data = getIconAnim(health); + if (data.isValid) { + if (this.animated) + playAnim(data.animState); + else + animation.curAnim.curFrame = data.animState; + curAnimState = data.animState; + } + + if (animateAtlas != null) { + @:bypassAccessor + frameWidth = 150; + @:bypassAccessor + frameHeight = 150; + extraOffsets.x -= frameWidth / 2; + extraOffsets.y -= frameHeight / 2; + updateHitbox(); + } else { + setGraphicSize(150); + updateHitbox(); + } + + defaultScale = (xmlValid && xmlData.exists("scale")) ? Std.parseFloat(xmlData.get("scale")).getDefault(scale.x) : scale.x; + scale.set(defaultScale, defaultScale); + updateHitbox(); + } + + var normalizedNames = ["neutral", "losing", "winning"]; + private function normalizeAnim(anim:OneOfTwo):OneOfTwo { + if(this.animated) { + if (anim is Int) { + var _:Int = cast anim; + if(_ >= 0 && _ < normalizedNames.length) + anim = normalizedNames[anim]; + } + } else { + if (anim is String) { + var _ = normalizedNames.indexOf(cast anim); + if(_ >= 0) + anim = _; + } + } + return anim; + } + + /** + * Gets the animation data for a specific health percentage + * @param health Health percentage + * @return Animation data (-1 if invalid) + */ + public dynamic function getIconAnim(health:Float):IconAnimData { + var i:OneOfTwo = -1; + var oldKey:Int = -1; + var isValid = false; + for (k=>icon in healthSteps) if (k > oldKey && k <= health * 100) { + oldKey = k; + i = icon; + isValid = true; + } + + i = normalizeAnim(i); + + return new IconAnimData(i, isValid); + } + + override function update(elapsed:Float):Void { super.update(elapsed); - if (sprTracker != null) - setPosition(sprTracker.x + sprTracker.width + 10, sprTracker.y - 30); + if (sprTracker != null) { + setPosition(sprTracker.x + (switch sprTrackerAlignment { + case LEFT: -width; + case CENTER: (sprTracker.width - width) / 2; + case RIGHT: sprTracker.width; + }) + sprTrackerOffset.x, sprTracker.y + sprTrackerOffset.y); + } - if (animation.curAnim != null) { - var i:Int = -1; - var oldKey:Int = -1; - for (k=>icon in healthSteps) if (k > oldKey && k <= health * 100) { - oldKey = k; - i = icon; - } - if (i >= 0 && curAnimState != i) { - var event = EventManager.get(funkin.backend.scripting.events.healthicon.HealthIconChangeEvent).recycle(i, this); + if (animation.curAnim != null || (this.animated && animateAtlas != null)) { + var data = getIconAnim(health); + var localAnimState = data.animState; + + if (data.isValid && curAnimState != localAnimState) { + var event = EventManager.get(HealthIconChangeEvent).recycle(localAnimState, this); funkin.backend.scripting.GlobalScript.event("onHealthIconAnimChange", event); - if (!event.cancelled) - animation.curAnim.curFrame = event.amount; + if (!event.cancelled) { + if (this.animated) { + var transAnim = 'from-$curAnimState-to-${event.anim}'; + playAnim(hasAnim(transAnim) ? transAnim : event.anim); + } else { + if(animation.curAnim != null) + animation.curAnim.curFrame = event.anim; + } + } - curAnimState = event.amount; + curAnimState = localAnimState; } } } + + function animFinishCallback(anim:String):Void { + if (this.animated) + if (anim.startsWith("from-")) + playAnim(anim.substr(anim.lastIndexOf('-') + 1)); + } + + override function updateHitbox():Void { + super.updateHitbox(); + offset += extraOffsets; + } } + +@:dox(hide) +class IconAnimData { + public var animState:OneOfTwo; + public var isValid:Bool; + + public function new(animState:OneOfTwo, isValid:Bool) { + this.animState = animState; + this.isValid = isValid; + } + + public function toString():String { + return '$animState (Valid: $isValid)'; + } +} + +/** + * Used for `funkin.game.HealthIcon.sprTrackerAlignment`. + * This determines the position of the icon in relation to the sprite tracker. +**/ +enum abstract TrackerAlignment(Int) { + /** + * Left of the sprite tracker + * + * Mathematically: `tracker.x - icon.width` + */ + var LEFT = 0; + /** + * Center of the sprite tracker + * + * Mathematically: `tracker.x + (tracker.width - icon.width) / 2` + */ + var CENTER = 1; + /** + * Right of the sprite tracker + * + * Mathematically: `tracker.x + tracker.width` + */ + var RIGHT = 2; +} \ No newline at end of file diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index 19618bb7..10519461 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -75,6 +75,11 @@ class Note extends FlxSprite public var scrollSpeed:Null = null; public var noteAngle:Null = null; + public var copyStrumAngle:Bool = true; + public var updateNotesPosX:Bool = true; + public var updateNotesPosY:Bool = true; + public var updateFlipY:Bool = true; + public var noteType(get, never):String; @:dox(hide) public var __strumCameras:Array = null; @@ -146,7 +151,9 @@ class Note extends FlxSprite case 0: animation.addByPrefix('scroll', 'purple0'); animation.addByPrefix('hold', 'purple hold piece'); - animation.addByPrefix('holdend', 'pruple end hold'); + animation.addByPrefix("holdend", "pruple end hold"); + if (animation.exists("holdend") != true) // null or false + animation.addByPrefix('holdend', 'purple hold end'); case 1: animation.addByPrefix('scroll', 'blue0'); animation.addByPrefix('hold', 'blue hold piece'); @@ -191,7 +198,7 @@ class Note extends FlxSprite } public var lastScrollSpeed:Null = null; - public var gapFix:Single = 0; + public var gapFix:SingleOrFloat = 0; public var useAntialiasingFix(get, set):Bool; inline function set_useAntialiasingFix(v:Bool) { if(v != useAntialiasingFix) { @@ -211,7 +218,7 @@ class Note extends FlxSprite override function drawComplex(camera:FlxCamera) { var downscrollCam = (camera is HudCamera ? ({var _:HudCamera=cast camera;_;}).downscroll : false); - flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0)); + if (updateFlipY) flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0)); if (downscrollCam) { frameOffset.y += __notePosFrameOffset.y * 2; super.drawComplex(camera); @@ -267,30 +274,22 @@ class Note extends FlxSprite public function updateSustain(strum:Strum) { var scrollSpeed = strum.getScrollSpeed(this); - var len = 0.45 * CoolUtil.quantize(scrollSpeed, 100); - - if (nextSustain != null && lastScrollSpeed != scrollSpeed) { - // is long sustain + if (lastScrollSpeed != scrollSpeed) { lastScrollSpeed = scrollSpeed; - - scale.y = (sustainLength * len) / frameHeight; - updateHitbox(); - scale.y += gapFix / frameHeight; + if (nextSustain != null) { + scale.y = (sustainLength * 0.45 * scrollSpeed) / frameHeight; + updateHitbox(); + scale.y += gapFix / frameHeight; + } } - if (!wasGoodHit) return; - var t = CoolUtil.bound((Conductor.songPosition - strumTime) / (height) * len, 0, 1); - var swagRect = this.clipRect == null ? new FlxRect() : this.clipRect; - swagRect.x = 0; - swagRect.y = t * frameHeight; - swagRect.width = frameWidth; - swagRect.height = frameHeight; - - setClipRect(swagRect); + updateSustainClip(); } - public inline function setClipRect(rect:FlxRect) { - this.clipRect = rect; + public function updateSustainClip() if (wasGoodHit) { + var t = FlxMath.bound((Conductor.songPosition - strumTime) / height * 0.45 * lastScrollSpeed, 0, 1); + var rect = clipRect == null ? FlxRect.get() : clipRect; + clipRect = rect.set(0, frameHeight * t, frameWidth, frameHeight * (1 - t)); } @:noCompletion @@ -306,5 +305,7 @@ class Note extends FlxSprite public override function destroy() { super.destroy(); + + clipRect = FlxDestroyUtil.put(clipRect); } } diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index f4e6f04c..19497319 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -7,6 +7,7 @@ import flixel.math.FlxPoint; import flixel.sound.FlxSound; import flixel.text.FlxText; import flixel.tweens.FlxTween; +import flixel.tweens.misc.VarTween; import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxSort; @@ -18,9 +19,9 @@ import funkin.backend.chart.EventsData; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; import funkin.backend.scripting.ScriptPack; +import funkin.backend.scripting.events.*; import funkin.backend.scripting.events.gameplay.*; import funkin.backend.scripting.events.note.*; -import funkin.backend.scripting.events.*; import funkin.backend.system.Conductor; import funkin.backend.system.RotatingSpriteGroup; import funkin.editors.SaveWarning; @@ -29,7 +30,7 @@ import funkin.editors.charter.CharterSelection; import funkin.game.SplashHandler; import funkin.game.cutscenes.*; import funkin.menus.*; -import funkin.menus.StoryMenuState.WeekData; +import funkin.backend.week.WeekData; import funkin.savedata.FunkinSave; import haxe.io.Path; @@ -69,15 +70,15 @@ class PlayState extends MusicBeatState */ public static var fromMods:Bool = false; /** - * Whenever Charting Mode has been enabled for this song. + * Whenever Charting Mode is enabled for this song. */ public static var chartingMode:Bool = false; /** - * Whenever the song has been started with opponent mode on. + * Whenever the song is started with opponent mode on. */ public static var opponentMode:Bool = Flags.DEFAULT_OPPONENT_MODE; /** - * Whenever the song has been started with co-op mode on. + * Whenever the song is started with co-op mode on. */ public static var coopMode:Bool = Flags.DEFAULT_COOP_MODE; @@ -138,8 +139,8 @@ class PlayState extends MusicBeatState */ public var downscroll(get, set):Bool; - @:dox(hide) private function set_downscroll(v:Bool) {return camHUD.downscroll = v;} - @:dox(hide) private function get_downscroll():Bool {return camHUD.downscroll;} + private inline function set_downscroll(v:Bool) return camHUD.downscroll = v; + private inline function get_downscroll():Bool return camHUD.downscroll; /** * Instrumental sound (Inst.ogg). @@ -225,23 +226,6 @@ class PlayState extends MusicBeatState */ public var doIconBop:Bool = true; - /** - * Whenever cam zooming is enabled, enables on a note hit if not cancelled. - */ - public var camZooming:Bool = false; - /** - * Interval of cam zooming (beats). - * For example: if set to 4, the camera will zoom every 4 beats. - */ - public var camZoomingInterval:Int = Flags.DEFAULT_CAM_ZOOM_INTERVAL; - /** - * How strong the cam zooms should be (defaults to 1) - */ - public var camZoomingStrength:Float = Flags.DEFAULT_CAM_ZOOM_STRENGTH; - /** - * Maximum amount of zoom for the camera. - */ - public var maxCamZoom:Float = Flags.MAX_CAMERA_ZOOM; /** * Current song name (lowercase) */ @@ -263,12 +247,12 @@ class PlayState extends MusicBeatState /** * Current health. Goes from 0 to maxHealth (defaults to 2) */ - public var health:Float = 1; + public var health(default, set):Float = 1; /** * Maximum health the player can have. Defaults to 2. */ - @:isVar public var maxHealth(get, set):Float = Flags.DEFAULT_MAX_HEALTH; + public var maxHealth(default, set):Float = Flags.DEFAULT_MAX_HEALTH; /** * Current combo. */ @@ -304,6 +288,11 @@ class PlayState extends MusicBeatState * Opponent's icon */ public var iconP2:HealthIcon; + /** + * Every active icon that will be updated during gameplay (defaults to `iconP1` and `iconP1` between `create` and `postCreate` in scripts) + */ + public var iconArray:Array = []; + /** * Camera for the HUD (notes, misses). */ @@ -383,6 +372,30 @@ class PlayState extends MusicBeatState */ public var camHUDZoomLerp:Float = Flags.DEFAULT_HUD_ZOOM_LERP; + /** + * Whenever cam zooming is enabled, enables on a note hit if not cancelled. + */ + public var camZooming:Bool = false; + /** + * Interval of cam zooming (beats). + * For example: if set to 4, the camera will zoom every 4 beats. + */ + public var camZoomingInterval:Int = Flags.DEFAULT_CAM_ZOOM_INTERVAL; + /** + * How strong the cam zooms should be (defaults to 1) + */ + public var camZoomingStrength:Float = Flags.DEFAULT_CAM_ZOOM_STRENGTH; + /** + * Default multiplier for `maxCamZoom`. + */ + public var maxCamZoomMult:Float = Flags.MAX_CAMERA_ZOOM_MULT; + /** + * Maximum amount of zoom for the camera (based on `maxCamZoomMult` and the camera's zoom IF not set). + */ + public var maxCamZoom(get, default):Float = Math.NaN; + + private inline function get_maxCamZoom() return Math.isNaN(maxCamZoom) ? maxCamZoomMult * defaultCamZoom : maxCamZoom; + /** * Zoom for the pixel assets. */ @@ -532,10 +545,10 @@ class PlayState extends MusicBeatState */ public function updateRating() { var rating = null; - var acc = get_accuracy(); + var acc = accuracy; // caching since it has a getter with an operation - Nex - for(e in comboRatings) - if (e.percent <= acc && (rating == null || rating.percent < e.percent)) + if (comboRatings != null && comboRatings.length > 0) for (e in comboRatings) + if ((e.percent <= acc && e.maxMisses >= misses) && (rating == null || (rating.percent < e.percent && e.maxMisses >= misses))) rating = e; var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating)); @@ -543,13 +556,13 @@ class PlayState extends MusicBeatState curRating = event.rating; } - private inline function get_maxHealth() - return this.maxHealth; - private function set_maxHealth(v:Float) { - if (healthBar != null && healthBar.max == this.maxHealth) { - healthBar.setRange(healthBar.min, v); - } - return this.maxHealth = v; + private inline function set_health(v:Float) + return health = FlxMath.bound(v, 0, maxHealth); + private inline function set_maxHealth(v:Float) { + if (healthBar != null && healthBar.max == maxHealth) healthBar.setRange(healthBar.min, v); + maxHealth = v; + health = health; // running the setter - Nex + return maxHealth; } private inline function get_curStage() @@ -581,6 +594,7 @@ class PlayState extends MusicBeatState @:dox(hide) override public function create() { Note.__customNoteTypeExists = []; + // SCRIPTING & DATA INITIALIZATION #if REGION instance = this; @@ -609,8 +623,8 @@ class PlayState extends MusicBeatState detailsText = isStoryMode ? ("Story Mode: " + storyWeek.name) : "Freeplay"; // Checks if cutscene files exists - var cutscenePath = Paths.script('songs/${SONG.meta.name.toLowerCase()}/cutscene'); - var endCutscenePath = Paths.script('songs/${SONG.meta.name.toLowerCase()}/cutscene-end'); + var cutscenePath = Paths.script('songs/${SONG.meta.name}/cutscene'); + var endCutscenePath = Paths.script('songs/${SONG.meta.name}/cutscene-end'); if (Assets.exists(cutscenePath)) cutscene = cutscenePath; if (Assets.exists(endCutscenePath)) endCutscene = endCutscenePath; @@ -637,10 +651,11 @@ class PlayState extends MusicBeatState // case "": // ADD YOUR HARDCODED SCRIPTS HERE! default: - var scriptsFolders:Array = ['songs/${SONG.meta.name.toLowerCase()}/scripts', 'data/charts/', 'songs/']; + var normal = 'songs/${SONG.meta.name}/scripts'; + var scriptsFolders:Array = [normal, normal + '/$difficulty/', 'data/charts/', 'songs/']; - for(folder in scriptsFolders) { - for(file in Paths.getFolderContent(folder, true, fromMods ? MODS : BOTH)) { + for (folder in scriptsFolders) { + for (file in Paths.getFolderContent(folder, true, fromMods ? MODS : BOTH)) { if (folder == 'data/charts/') Logs.warn('data/charts/ is deprecated and will be removed in the future. Please move script $file to songs/', DARKYELLOW, "PlayState"); @@ -701,7 +716,7 @@ class PlayState extends MusicBeatState case 2: "girlfriend"; }) : strumLine.position; if (strumLine.characters != null) for(k=>charName in strumLine.characters) { - var char = new Character(0, 0, charName, stage.isCharFlipped(charPosName, strumLine.type == 1)); + var char = new Character(0, 0, charName, stage.isCharFlipped(stage.characterPoses[charName] != null ? charName : charPosName, strumLine.type == 1)); stage.applyCharStuff(char, charPosName, k); chars.push(char); } @@ -782,9 +797,9 @@ class PlayState extends MusicBeatState if (Flags.DEFAULT_HEALTH != null) health = Flags.DEFAULT_HEALTH; else health = maxHealth / 2; - iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : Flags.DEFAULT_HEALTH_ICON, true); - iconP2 = new HealthIcon(dad != null ? dad.getIcon() : Flags.DEFAULT_HEALTH_ICON, false); - for(icon in [iconP1, iconP2]) { + iconArray.push(iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : Flags.DEFAULT_HEALTH_ICON, true)); + iconArray.push(iconP2 = new HealthIcon(dad != null ? dad.getIcon() : Flags.DEFAULT_HEALTH_ICON, false)); + for (icon in iconArray) { icon.y = healthBar.y - (icon.height / 2); add(icon); } @@ -801,7 +816,8 @@ class PlayState extends MusicBeatState scoreTxt.alignment = RIGHT; missesTxt.alignment = CENTER; accuracyTxt.alignment = LEFT; - updateRatingStuff(); + if (updateRatingStuff != null) + updateRatingStuff(); for(e in [healthBar, healthBarBG, iconP1, iconP2, scoreTxt, missesTxt, accuracyTxt]) e.cameras = [camHUD]; @@ -838,7 +854,8 @@ class PlayState extends MusicBeatState // Make icons appear in the correct spot during cutscenes healthBar.update(0); - updateIconPositions(); + if (updateIconPositions != null) + updateIconPositions(); __updateNote_event = EventManager.get(NoteUpdateEvent); @@ -847,6 +864,7 @@ class PlayState extends MusicBeatState /** * Function used to update Discord Presence. + * * This function is dynamic, which means you can do `updateDiscordPresence = function() {}` in scripts. */ public dynamic function updateDiscordPresence() @@ -867,7 +885,7 @@ class PlayState extends MusicBeatState return; } - var songName = SONG.meta.name.toLowerCase(); + var songName = SONG.meta.name; if (cutsceneScriptPath == null) cutsceneScriptPath = Paths.script('songs/$songName/${prefix}cutscene'); @@ -1027,10 +1045,11 @@ class PlayState extends MusicBeatState Note.__customNoteTypeExists = []; } - public static function resetSongInfos() { - deathCounter = 0; - seenCutscene = false; - } + @:dox(hide) @:deprecated("scrollSpeedTween is deprecated, use eventsTween['scrollSpeed'] instead") + public var scrollSpeedTween(get, set):FlxTween; + private inline function get_scrollSpeedTween() return eventsTween.get("scrollSpeed"); + private inline function set_scrollSpeedTween(val:FlxTween) {eventsTween.set("scrollSpeed", val); return val;} + // End of backwards compat @:dox(hide) private function generateSong(?songData:ChartData):Void { @@ -1223,7 +1242,7 @@ class PlayState extends MusicBeatState if (SaveWarning.selectionClass != null) FlxG.switchState(Type.createInstance(SaveWarning.selectionClass, [])); } else { WindowUtils.preventClosing = false; WindowUtils.resetClosing(); - Sys.exit(0); + openfl.system.System.exit(0); } } }, @@ -1236,7 +1255,7 @@ class PlayState extends MusicBeatState if (SaveWarning.selectionClass != null) FlxG.switchState(Type.createInstance(SaveWarning.selectionClass, [])); } else { WindowUtils.preventClosing = false; WindowUtils.resetClosing(); - Sys.exit(0); + openfl.system.System.exit(0); } } }, @@ -1250,21 +1269,20 @@ class PlayState extends MusicBeatState ])); } - function updateIconPositions() { + dynamic function updateIconPositions() { var iconOffset = Flags.ICON_OFFSET; + var healthBarPercent = healthBar.percent; - var center:Float = healthBar.x + healthBar.width * FlxMath.remapToRange(healthBar.percent, 0, 100, 1, 0); + var center:Float = healthBar.x + healthBar.width * FlxMath.remapToRange(healthBarPercent, 0, 100, 1, 0); iconP1.x = center - iconOffset; iconP2.x = center - (iconP2.width - iconOffset); - health = CoolUtil.bound(health, 0, maxHealth); - - iconP1.health = healthBar.percent / 100; - iconP2.health = 1 - (healthBar.percent / 100); + iconP1.health = healthBarPercent / 100; + iconP2.health = 1 - (healthBarPercent / 100); } - function updateRatingStuff() { + dynamic function updateRatingStuff() { scoreTxt.text = 'Score:$songScore'; missesTxt.text = '${comboBreaks ? "Combo Breaks" : "Misses"}:$misses'; @@ -1294,45 +1312,37 @@ class PlayState extends MusicBeatState return; } - updateRatingStuff(); + if (updateRatingStuff != null) + updateRatingStuff(); - if (controls.PAUSE && startedCountdown && canPause) - pauseGame(); + if (canAccessDebugMenus && chartingMode && controls.DEV_ACCESS) + FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false)); - if (canAccessDebugMenus) { - if (chartingMode && FlxG.keys.justPressed.SEVEN) { - FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false)); - } - #if EXPERMENTAL_SCRIPT_RELOADING - if (FlxG.keys.justPressed.F5) { - Logs.warn('Reloading scripts...', "PlayState"); - scripts.reload(); - Logs.warn('Song scripts successfully reloaded.', GREEN, "PlayState"); - } - #end - } + if (doIconBop) + for (icon in iconArray) + if (icon.updateBump != null) + icon.updateBump(); - if (doIconBop) { - var iconLerp = Flags.ICON_LERP; - iconP1.scale.set(lerp(iconP1.scale.x, 1, iconLerp), lerp(iconP1.scale.y, 1, iconLerp)); - iconP2.scale.set(lerp(iconP2.scale.x, 1, iconLerp), lerp(iconP2.scale.y, 1, iconLerp)); + if (updateIconPositions != null) + updateIconPositions(); - iconP1.updateHitbox(); - iconP2.updateHitbox(); - } - updateIconPositions(); - - if (startingSong) - { - if (startedCountdown) - { + if (startingSong) { + if (startedCountdown) { Conductor.songPosition += Conductor.songOffset + elapsed * 1000; if (Conductor.songPosition >= 0) startSong(); } } else if (FlxG.sound.music != null) { var instTime = FlxG.sound.music.time; - var isOffsync = vocals.time != instTime || [for(strumLine in strumLines.members) strumLine.vocals.time != instTime].contains(true); + var isOffsync:Bool = vocals.time != instTime; + if(!isOffsync) { + for(strumLine in strumLines.members) { + if(strumLine.vocals.time != instTime) { + isOffsync = true; + break; + } + } + } __vocalOffsetViolation = Math.max(0, __vocalOffsetViolation + (isOffsync ? elapsed : -elapsed / 2)); if (__vocalOffsetViolation > Flags.VOCAL_OFFSET_VIOLATION_THRESHOLD) { resyncVocals(); @@ -1343,31 +1353,13 @@ class PlayState extends MusicBeatState while(events.length > 0 && events.last().time <= Conductor.songPosition) executeEvent(events.pop()); - if (generatedMusic && strumLines.members[curCameraTarget] != null) - { - var pos = FlxPoint.get(); - var r = 0; - for(c in strumLines.members[curCameraTarget].characters) { - if (c == null || !c.visible) continue; - var cpos = c.getCameraPosition(); - pos.x += cpos.x; - pos.y += cpos.y; - r++; - //cpos.put(); - } - if (r > 0) { - pos.x /= r; - pos.y /= r; + if (controls.PAUSE && startedCountdown && canPause) + pauseGame(); - var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r)); - if (!event.cancelled) - camFollow.setPosition(pos.x, pos.y); - } - pos.put(); - } + if (generatedMusic) + moveCamera(); - if (camZooming) - { + if (camZooming) { FlxG.camera.zoom = lerp(FlxG.camera.zoom, defaultCamZoom, camGameZoomLerp); camHUD.zoom = lerp(camHUD.zoom, defaultHudZoom, camHUDZoomLerp); } @@ -1401,7 +1393,52 @@ class PlayState extends MusicBeatState scripts.event("postDraw", e); } - public var scrollSpeedTween:FlxTween = null; + public function moveCamera() if (strumLines.members[curCameraTarget] != null) { + var data:CamPosData = getStrumlineCamPos(curCameraTarget); + if (data.amount > 0) { + var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(data.pos, strumLines.members[curCameraTarget], data.amount)); + if (!event.cancelled) + camFollow.setPosition(event.position.x, event.position.y); + } + data.put(); + } + + /** + * Returns the camera position of the specified strumline. + * @param strumLine The strumline to get the camera position of. + * @param pos The position to put the camera position in. If `null`, a new FlxPoint will be created. + * @param ignoreInvisible Whenever invisible characters should be ignored. + **/ + public inline function getStrumlineCamPos(strumLine:Int, ?pos:FlxPoint = null, ?ignoreInvisible:Bool = true):CamPosData { + return getCharactersCamPos(strumLines.members[strumLine].characters, pos, ignoreInvisible); + } + + /** + * Returns the camera position of the specified characters. + * @param chars The characters to get the camera position of. + * @param pos The position to put the camera position in. If `null`, a new FlxPoint will be created. + * @param ignoreInvisible Whenever invisible characters should be ignored. + **/ + public dynamic function getCharactersCamPos(chars:Array, ?pos:FlxPoint = null, ?ignoreInvisible:Bool = true):CamPosData { + if (pos == null) pos = FlxPoint.get(); + var amount = 0; + for(c in chars) { + if (c == null || (ignoreInvisible && !c.visible)) continue; + var cpos = c.getCameraPosition(); + pos.x += cpos.x; + pos.y += cpos.y; + amount++; + //cpos.put(); // not actually in the pool, so no need + } + if (amount > 0) { + pos.x /= amount; + pos.y /= amount; + } + return new CamPosData(pos, amount); + } + + + public var eventsTween:Map = []; public function executeEvent(event:ChartEvent) @:privateAccess { if (event == null || event.params == null) return; @@ -1413,12 +1450,92 @@ class PlayState extends MusicBeatState switch(event.name) { case "HScript Call": - scripts.call(event.params[0], event.params[1].split(',')); + var scriptPacks:Array = [scripts, stateScripts]; + for (strLine in strumLines.members) for (char in strLine.characters) scriptPacks.push(char.scripts); + var args:Array = event.params[1].split(','); + + for (pack in scriptPacks) { + pack.call(event.params[0], args); + //public functions + if (pack.publicVariables.exists(event.params[0])) { + var func = pack.publicVariables.get(event.params[0]); + if (func != null && Reflect.isFunction(func)) + Reflect.callMethod(null, func, args); + } + } + + //static functions + if (Script.staticVariables.exists(event.params[0])) { + var func = Script.staticVariables.get(event.params[0]); + if (func != null && Reflect.isFunction(func)) + Reflect.callMethod(null, func, args); + } case "Camera Movement": + var tween = eventsTween.get("cameraMovement"); + if (tween != null) { + if (tween.onComplete != null) tween.onComplete(tween); + tween.cancel(); + } + curCameraTarget = event.params[0]; + moveCamera(); + + if (strumLines.members[curCameraTarget] != null) { + if (event.params[1] == false) FlxG.camera.snapToTarget(); + else if (event.params[3] != null && event.params[3] != "CLASSIC") { // making more nullchecks in this event because of the default save value being false - Nex + var oldFollow = FlxG.camera.followEnabled; + FlxG.camera.followEnabled = false; + eventsTween.set("cameraMovement", FlxTween.tween(FlxG.camera.scroll, {x: camFollow.x - FlxG.camera.width * 0.5, y: camFollow.y - FlxG.camera.height * 0.5}, + (Conductor.stepCrochet / 1000) * (event.params[2] == null ? 4 : event.params[2]), { + ease: CoolUtil.flxeaseFromString(event.params[3], event.params[4]), + onComplete: (_) -> FlxG.camera.followEnabled = oldFollow + }) + ); + } + } + case "Camera Position": + var tween = eventsTween.get("cameraMovement"); + if (tween != null) { + if (tween.onComplete != null) tween.onComplete(tween); + tween.cancel(); + } + + curCameraTarget = -1; + var isOffset = event.params[6] == true; + camFollow.setPosition(isOffset ? (camFollow.x + event.params[0]) : event.params[0], isOffset ? (camFollow.y + event.params[1]) : event.params[1]); + + if (event.params[2] == false) FlxG.camera.snapToTarget(); + else if (event.params[4] != null && event.params[4] != "CLASSIC") { + var oldFollow = FlxG.camera.followEnabled; + FlxG.camera.followEnabled = false; + eventsTween.set("cameraMovement", FlxTween.tween(FlxG.camera.scroll, {x: camFollow.x - FlxG.camera.width * 0.5, y: camFollow.y - FlxG.camera.height * 0.5}, + (Conductor.stepCrochet / 1000) * (event.params[3] == null ? 4 : event.params[3]), { + ease: CoolUtil.flxeaseFromString(event.params[4], event.params[5]), + onComplete: (_) -> FlxG.camera.followEnabled = oldFollow + }) + ); + } case "Add Camera Zoom": var camera:FlxCamera = event.params[1] == "camHUD" ? camHUD : camGame; camera.zoom += event.params[0]; + case "Camera Zoom": + var cam = event.params[2] == "camHUD" ? camHUD : camGame; + var name = (event.params[2] == "camHUD" ? "camHUD" : "camGame") + ".zoom"; // avoiding having different values from these 2 - Nex + var tween = eventsTween.get(name); + if (tween != null) tween.cancel(); + + var finalZoom:Float = event.params[1] * (event.params[6] == 'direct' ? FlxCamera.defaultZoom : stage.defaultZoom); + if (event.params[7] == true) finalZoom *= cam.zoom; + + if (event.params[0] == false) { + cam.zoom = finalZoom; + if (cam == camHUD) defaultHudZoom = finalZoom; + else defaultCamZoom = finalZoom; + } else + eventsTween.set(name, FlxTween.tween(cam, {zoom: finalZoom}, (Conductor.stepCrochet / 1000) * event.params[3], {ease: CoolUtil.flxeaseFromString(event.params[4], event.params[5]), onUpdate: function(_) { + if (cam == camHUD) defaultHudZoom = cam.zoom; + else defaultCamZoom = cam.zoom; + }})); case "Camera Modulo Change": camZoomingInterval = event.params[0]; camZoomingStrength = event.params[1]; @@ -1431,12 +1548,16 @@ class PlayState extends MusicBeatState camera.flash(event.params[1], (Conductor.stepCrochet / 1000) * event.params[2], null, true); case "BPM Change": // automatically handled by conductor case "Scroll Speed Change": - if (scrollSpeedTween != null) scrollSpeedTween.cancel(); + var tween = eventsTween.get("scrollSpeedTween"); + if (tween != null) tween.cancel(); + + var finalScroll:Float = event.params[1]; + if (event.params[5] == true) finalScroll *= scrollSpeed; if (event.params[0] == false) - scrollSpeed = event.params[1]; + scrollSpeed = finalScroll; else - scrollSpeedTween = FlxTween.tween(this, {scrollSpeed: event.params[1]}, (Conductor.stepCrochet / 1000) * event.params[2], {ease: CoolUtil.flxeaseFromString(event.params[3], event.params[4])}); + eventsTween.set("scrollSpeedTween", FlxTween.tween(this, {scrollSpeed: finalScroll}, (Conductor.stepCrochet / 1000) * event.params[2], {ease: CoolUtil.flxeaseFromString(event.params[3], event.params[4])})); case "Alt Animation Toggle": var strLine = strumLines.members[event.params[2]]; if (strLine != null) { @@ -1451,7 +1572,7 @@ class PlayState extends MusicBeatState case "Play Animation": if (strumLines.members[event.params[0]] != null && strumLines.members[event.params[0]].characters != null) for (char in strumLines.members[event.params[0]].characters) - if (char != null) char.playAnim(event.params[1], event.params[2], null); + if (char != null) char.playAnim(event.params[1], event.params[2], event.params[3] == "NONE" ? null : event.params[3]); case "Unknown": // nothing } } @@ -1506,6 +1627,7 @@ class PlayState extends MusicBeatState */ public function endSong():Void { + endingSong = true; gameAndCharsCall("onSongEnd"); canPause = false; inst.volume = 0; @@ -1546,8 +1668,6 @@ class PlayState extends MusicBeatState * Immediately switches to the next song, or goes back to the Story/Freeplay menu. */ public function nextSong() { - resetSongInfos(); - if (isStoryMode) { campaignScore += songScore; @@ -1581,7 +1701,7 @@ class PlayState extends MusicBeatState FlxG.sound.music.stop(); - __loadSong(storyPlaylist[0].toLowerCase(), difficulty); + __loadSong(storyPlaylist[0], difficulty); FlxG.switchState(new PlayState()); } @@ -1711,9 +1831,9 @@ class PlayState extends MusicBeatState var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick"); + event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick", 0.5, true, 0.7, true, true, iconP1); else - event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false); + event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false, 0.5, true, 0.7, true, true, iconP2); event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event); strumLine.onHit.dispatch(event); @@ -1876,14 +1996,9 @@ class PlayState extends MusicBeatState } if (doIconBop) - { - var iconScale = Flags.BOP_ICON_SCALE; - iconP1.scale.set(iconScale, iconScale); - iconP2.scale.set(iconScale, iconScale); - - iconP1.updateHitbox(); - iconP2.updateHitbox(); - } + for (icon in iconArray) + if (icon.bump != null) + icon.bump(); scripts.call("beatHit", [curBeat]); } @@ -1974,7 +2089,7 @@ class PlayState extends MusicBeatState public static function loadWeek(weekData:WeekData, ?difficulty:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; storyWeek = weekData; - storyPlaylist = [for(e in weekData.songs) e.name]; + storyPlaylist = [for (e in weekData.songs) e.name]; isStoryMode = true; campaignScore = 0; campaignMisses = 0; @@ -2008,6 +2123,8 @@ class PlayState extends MusicBeatState */ public static function __loadSong(_name:String, _difficulty:String) { difficulty = _difficulty; + seenCutscene = false; + deathCounter = 0; SONG = Chart.parse(_name, _difficulty); fromMods = SONG.fromMods; @@ -2018,8 +2135,10 @@ final class ComboRating { public var percent:Float; public var rating:String; public var color:FlxColor; + public var maxMisses:Float; // Float since it could be Math.POSITIVE_INFINITY - Nex - public function new(percent:Float, rating:String, color:FlxColor) { + public function new(?percent:Float, ?rating:String, ?color:FlxColor, ?misses:Float) { + maxMisses = misses == null || Math.isNaN(misses) ? Math.POSITIVE_INFINITY : misses; this.percent = percent; this.rating = rating; this.color = color; @@ -2034,3 +2153,28 @@ typedef PlayStateTransitionData = { var camFollowY:Float; var camZoom:Float; } + +class CamPosData { + /** + * The camera position. + **/ + public var pos:FlxPoint; + /** + * The amount of characters that was involved in the calculation. + **/ + public var amount:Int; + + public function new(pos:FlxPoint, amount:Int) { + this.pos = pos; + this.amount = amount; + } + + /** + * Puts the position back into the pool, making it reusable. + **/ + public function put() { + if(pos == null) return; + pos.put(); + pos = null; + } +} \ No newline at end of file diff --git a/source/funkin/game/Splash.hx b/source/funkin/game/Splash.hx new file mode 100644 index 00000000..6352e31f --- /dev/null +++ b/source/funkin/game/Splash.hx @@ -0,0 +1,39 @@ +package funkin.game; + +class Splash extends FunkinSprite +{ + /** + * The current splash strum + * WARNING: It can be null + */ + public var strum:Null; + + /** + * Shortcut to `strum.ID` + * WARNING: It can be null + */ + public var strumID:Null; + + public static function copyFrom(source:Splash):Splash + { + var spr = new Splash(); + @:privateAccess { + spr.setPosition(source.x, source.y); + spr.frames = source.frames; + if (source.animateAtlas != null && source.atlasPath != null) + spr.loadSprite(source.atlasPath); + spr.animation.copyFrom(source.animation); + spr.visible = source.visible; + spr.alpha = source.alpha; + spr.antialiasing = source.antialiasing; + spr.scale.set(source.scale.x, source.scale.y); + spr.scrollFactor.set(source.scrollFactor.x, source.scrollFactor.y); + spr.skew.set(source.skew.x, source.skew.y); + spr.transformMatrix = source.transformMatrix; + spr.matrixExposed = source.matrixExposed; + spr.zoomFactor = source.zoomFactor; + spr.animOffsets = source.animOffsets.copy(); + } + return spr; + } +} diff --git a/source/funkin/game/SplashGroup.hx b/source/funkin/game/SplashGroup.hx index 121951bc..cc2b75a1 100644 --- a/source/funkin/game/SplashGroup.hx +++ b/source/funkin/game/SplashGroup.hx @@ -2,7 +2,7 @@ package funkin.game; import haxe.xml.Access; -class SplashGroup extends FlxTypedGroup { +class SplashGroup extends FlxTypedGroup { /** * Whenever the splash group has successfully loaded or not. */ @@ -51,7 +51,7 @@ class SplashGroup extends FlxTypedGroup { var _antialiasing:Bool = true; function createSplash(imagePath:String) { - var splash = new FunkinSprite(); + var splash = new Splash(); splash.antialiasing = true; splash.active = splash.visible = false; splash.loadSprite(Paths.image(imagePath)); @@ -64,7 +64,7 @@ class SplashGroup extends FlxTypedGroup { return splash; } - function setupAnims(xml:Access, splash:FunkinSprite) { + function setupAnims(xml:Access, splash:Splash) { for(strum in xml.nodes.strum) { var id:Null = Std.parseInt(strum.att.id); if (id != null) { @@ -93,12 +93,14 @@ class SplashGroup extends FlxTypedGroup { }; } - function pregenerateSplashes(splash:FunkinSprite) { + function pregenerateSplashes(splash:Splash) { // make 7 additional splashes for(i in 0...7) { - var spr = FunkinSprite.copyFrom(splash); + var spr = Splash.copyFrom(splash); spr.animation.finishCallback = function(name:String) { spr.active = spr.visible = false; + spr.strum = null; + spr.strumID = null; }; add(spr); } @@ -112,11 +114,14 @@ class SplashGroup extends FlxTypedGroup { return animNames[FlxG.random.int(0, animNames.length - 1)]; } - var __splash:FunkinSprite; + var __splash:Splash; public function showOnStrum(strum:Strum) { if (!valid) return null; __splash = recycle(); + __splash.strum = strum; + __splash.strumID = strum.ID; + @:privateAccess __splash.scale.x = __splash.scale.y = _scale * strum.strumLine.strumScale; __splash.alpha = _alpha; diff --git a/source/funkin/game/SplashHandler.hx b/source/funkin/game/SplashHandler.hx index ad14ad8c..d17981a7 100644 --- a/source/funkin/game/SplashHandler.hx +++ b/source/funkin/game/SplashHandler.hx @@ -2,7 +2,7 @@ package funkin.game; import funkin.backend.scripting.events.splash.*; -class SplashHandler extends FlxTypedGroup { +class SplashHandler extends FlxTypedGroup { /** * Map containing all of the splashes group. */ diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index ab653653..40475f15 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -130,18 +130,19 @@ class Stage extends FlxBasic implements IBeatReceiver { case "box" | "solid": if (!node.has.name || !node.has.width || !node.has.height) continue; - var spr = new FunkinSprite( - (node.has.x) ? Std.parseFloat(node.att.x).getDefault(0) : 0, - (node.has.y) ? Std.parseFloat(node.att.y).getDefault(0) : 0 - ); - spr.name = node.getAtt("name"); + var isSolid = node.name == "solid"; - (node.name == "solid" ? spr.makeSolid : spr.makeGraphic)( + var spr = new FunkinSprite(); + (isSolid ? spr.makeSolid : spr.makeGraphic)( Std.parseInt(node.att.width), Std.parseInt(node.att.height), (node.has.color) ? CoolUtil.getColorFromDynamic(node.att.color) : -1 ); + if (isSolid) node.x.remove("updateHitbox"); + for (a in ["width", "height", "color"]) node.x.remove(a); + XMLUtil.loadSpriteFromXML(spr, node, "", NONE, false); + stageSprites.set(spr.name, spr); addSprite(spr); case "boyfriend" | "bf" | "player": @@ -353,6 +354,11 @@ class Stage extends FlxBasic implements IBeatReceiver { public function measureHit(curMeasure:Int) {} + public override function destroy() { + startCam.put(); + super.destroy(); + } + /** * Gets a list of stages that are available to be used. * @param mods Whenever only the mods folder should be checked @@ -469,4 +475,4 @@ typedef OldCharInfo = { var alpha:Float; var zoomFactor:Float; var angle:Float; -} \ No newline at end of file +} diff --git a/source/funkin/game/Strum.hx b/source/funkin/game/Strum.hx index 2ecccf53..d5031945 100644 --- a/source/funkin/game/Strum.hx +++ b/source/funkin/game/Strum.hx @@ -42,6 +42,18 @@ class Strum extends FlxSprite { public var lastDrawCameras(default, null):Array = []; + // Copy fields + public var copyStrumCamera:Bool = true; + public var copyStrumScrollX:Bool = true; + public var copyStrumScrollY:Bool = true; + public var copyStrumAngle:Bool = true; + public var updateNotesPosX:Bool = true; + public var updateNotesPosY:Bool = true; + public var extraCopyFields(default, set):Array = []; + + private inline function set_extraCopyFields(val:Array) + return extraCopyFields = val == null ? [] : val; + /** * Whenever the strum is pressed. **/ @@ -130,47 +142,61 @@ class Strum extends FlxSprite { public function updateNotePosition(daNote:Note) { if (!daNote.exists) return; - daNote.__strumCameras = lastDrawCameras; daNote.__strum = this; - daNote.scrollFactor.set(scrollFactor.x, scrollFactor.y); - daNote.__noteAngle = getNotesAngle(daNote); - daNote.angle = daNote.isSustainNote ? daNote.__noteAngle : angle; + if (copyStrumCamera) daNote.__strumCameras = lastDrawCameras; + if (copyStrumScrollX) daNote.scrollFactor.x = scrollFactor.x; + if (copyStrumScrollY) daNote.scrollFactor.y = scrollFactor.y; + if (copyStrumAngle && daNote.copyStrumAngle) { + daNote.__noteAngle = getNotesAngle(daNote); + daNote.angle = daNote.isSustainNote ? daNote.__noteAngle : angle; + } updateNotePos(daNote); + for (field in extraCopyFields) + CoolUtil.cloneProperty(daNote, field, this); // TODO: make this cached to reduce the reflection calls - Neo } private inline function updateNotePos(daNote:Note) { - if (daNote.strumRelativePos) { - daNote.setPosition((this.width - daNote.width) / 2, (daNote.strumTime - Conductor.songPosition) * (0.45 * CoolUtil.quantize(getScrollSpeed(daNote), 100))); - if (daNote.isSustainNote) daNote.y += N_WIDTHDIV2 * (height / Note.swagWidth); - } else { - var offset = FlxPoint.get(0, (Conductor.songPosition - daNote.strumTime) * (0.45 * CoolUtil.quantize(getScrollSpeed(daNote), 100))); - var realOffset = FlxPoint.get(0, 0); + var shouldX = updateNotesPosX && daNote.updateNotesPosX; + var shouldY = updateNotesPosY && daNote.updateNotesPosY; - if (daNote.isSustainNote) offset.y -= N_WIDTHDIV2 * (height / Note.swagWidth); - - if (Std.int(daNote.__noteAngle % 360) != 0) { - var noteAngleCos = FlxMath.fastCos(daNote.__noteAngle / PIX180); - var noteAngleSin = FlxMath.fastSin(daNote.__noteAngle / PIX180); - - var aOffset:FlxPoint = FlxPoint.get( - (daNote.origin.x / daNote.scale.x) - daNote.offset.x, - (daNote.origin.y / daNote.scale.y) - daNote.offset.y - ); - realOffset.x = -aOffset.x + (noteAngleCos * (offset.x + aOffset.x)) + (noteAngleSin * (offset.y + aOffset.y)); - realOffset.y = -aOffset.y + (noteAngleSin * (offset.x + aOffset.x)) + (noteAngleCos * (offset.y + aOffset.y)); - - aOffset.put(); + if (shouldX || shouldY) { + if (daNote.strumRelativePos) { + if (shouldX) daNote.x = (this.width - daNote.width) / 2; + if (shouldY) { + daNote.y = (daNote.strumTime - Conductor.songPosition) * (0.45 * CoolUtil.quantize(getScrollSpeed(daNote), 100)); + if (daNote.isSustainNote) daNote.y += N_WIDTHDIV2; + } } else { - realOffset.x = offset.x; - realOffset.y = offset.y; + var offset = FlxPoint.get(0, (Conductor.songPosition - daNote.strumTime) * (0.45 * CoolUtil.quantize(getScrollSpeed(daNote), 100))); + var realOffset = FlxPoint.get(0, 0); + + if (daNote.isSustainNote) offset.y -= N_WIDTHDIV2; + + if (Std.int(daNote.__noteAngle % 360) != 0) { + var noteAngleCos = FlxMath.fastCos(daNote.__noteAngle / PIX180); + var noteAngleSin = FlxMath.fastSin(daNote.__noteAngle / PIX180); + + var aOffset:FlxPoint = FlxPoint.get( + (daNote.origin.x / daNote.scale.x) - daNote.offset.x, + (daNote.origin.y / daNote.scale.y) - daNote.offset.y + ); + realOffset.x = -aOffset.x + (noteAngleCos * (offset.x + aOffset.x)) + (noteAngleSin * (offset.y + aOffset.y)); + realOffset.y = -aOffset.y + (noteAngleSin * (offset.x + aOffset.x)) + (noteAngleCos * (offset.y + aOffset.y)); + + aOffset.put(); + } else { + realOffset.x = offset.x; + realOffset.y = offset.y; + } + realOffset.y *= -1; + + if (shouldX) daNote.x = x + realOffset.x; + if (shouldY) daNote.y = y + realOffset.y; + + offset.put(); + realOffset.put(); } - realOffset.y *= -1; - - daNote.setPosition(x + realOffset.x, y + realOffset.y); - - offset.put(); - realOffset.put(); } } diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 484a6075..27b998d3 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -221,7 +221,7 @@ class StrumLine extends FlxTypedGroup { if (cpu && __updateNote_event.__autoCPUHit && !daNote.avoid && !daNote.wasGoodHit && daNote.strumTime < __updateNote_songPos) PlayState.instance.goodNoteHit(this, daNote); - if (daNote.wasGoodHit && daNote.isSustainNote && daNote.strumTime + (daNote.sustainLength) < __updateNote_songPos) { + if (daNote.wasGoodHit && daNote.isSustainNote && daNote.strumTime + daNote.sustainLength < __updateNote_songPos) { deleteNote(daNote); return; } @@ -247,8 +247,9 @@ class StrumLine extends FlxTypedGroup { var __notePerStrum:Array = []; function __inputProcessPressed(note:Note) { - if (__pressed[note.strumID] && note.isSustainNote && note.canBeHit && !note.wasGoodHit) { + if (__pressed[note.strumID] && note.isSustainNote && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { PlayState.instance.goodNoteHit(this, note); + note.updateSustainClip(); } } function __inputProcessJustPressed(note:Note) { @@ -347,8 +348,10 @@ class StrumLine extends FlxTypedGroup { * Creates a strum and returns the created strum (needs to be added manually). * @param i Index of the strum * @param animPrefix (Optional) Animation prefix (`left` = `arrowLEFT`, `left press`, `left confirm`). + * @param spritePath (Optional) The sprite's graphic path if you don't want the default one. + * @param playIntroAnimation (Optional) Whenever the intro animation should be played, by default might be `true` under certain conditions. */ - public function createStrum(i:Int, ?animPrefix:String) { + public function createStrum(i:Int, ?animPrefix:String, ?spritePath:String, ?playIntroAnimation:Bool) { if (animPrefix == null) animPrefix = strumAnimPrefix[i % strumAnimPrefix.length]; var babyArrow:Strum = new Strum(startingPos.x + (Note.swagWidth * strumScale * (data.strumSpacing != null ? data.strumSpacing : 1) * i), startingPos.y + (Note.swagWidth*0.5) - (Note.swagWidth * strumScale * 0.5)); @@ -359,7 +362,8 @@ class StrumLine extends FlxTypedGroup { babyArrow.scrollSpeed = data.scrollSpeed; var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix); - event.__doAnimation = !MusicBeatState.skipTransIn && (PlayState.instance != null ? PlayState.instance.introLength > 0 : true); + event.__doAnimation = playIntroAnimation == null ? (!MusicBeatState.skipTransIn && (PlayState.instance != null ? PlayState.instance.introLength > 0 : true)) : playIntroAnimation; + if (spritePath != null) event.sprite = spritePath; if (PlayState.instance != null) event = PlayState.instance.gameAndCharsEvent("onStrumCreation", event); if (!event.cancelled) { diff --git a/source/funkin/game/cutscenes/Cutscene.hx b/source/funkin/game/cutscenes/Cutscene.hx index 9845e916..51e6ed1f 100644 --- a/source/funkin/game/cutscenes/Cutscene.hx +++ b/source/funkin/game/cutscenes/Cutscene.hx @@ -8,11 +8,11 @@ import funkin.menus.PauseSubState; */ class Cutscene extends MusicBeatSubstate { var __callback:Void->Void; - var __pausable:Bool; var game:PlayState = PlayState.instance; public var paused:Bool = false; public var pauseItems:Array; + public var pausable:Bool; public var skippable(default, set):Bool; public function set_skippable(val:Bool):Bool { @@ -26,9 +26,9 @@ class Cutscene extends MusicBeatSubstate { canOpenCustomTransition = true; __callback = callback; - __pausable = allowPause; pauseItems = Flags.DEFAULT_CUTSCENE_PAUSE_ITEMS; + pausable = allowPause; skippable = canSkip; } @@ -38,7 +38,7 @@ class Cutscene extends MusicBeatSubstate { } public function pauseCheck():Bool - return controls.PAUSE && __pausable; + return controls.PAUSE && pausable; var _before:Array = [false, true]; public function pauseCutscene() { diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index 22c27da3..2b94de48 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -92,7 +92,7 @@ class DialogueCutscene extends ScriptedCutscene { callback: node.getAtt('callback'), changeDefAnim: node.getAtt('changeDefAnim'), speed: Std.parseFloat(node.getAtt("speed")).getDefault(0.05), - musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).getDefault(0.8)) : null, + musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.musicVolume).getDefault(0.8)) : null, changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.att.changeMusic), volume, true) : null, playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.att.playSound)) : null, nextSound: node.has.nextSound ? FlxG.sound.load(Paths.sound(node.att.nextSound)) : null, @@ -200,7 +200,7 @@ class DialogueCutscene extends ScriptedCutscene { if (curMusic != null && !curMusic.persist) curMusic.destroy(); super.destroy(); - cutscene = null; + if (cutscene == this) cutscene = null; FlxG.cameras.remove(dialogueCamera); } } diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx index 9e5a50bd..f3e3e274 100644 --- a/source/funkin/game/cutscenes/VideoCutscene.hx +++ b/source/funkin/game/cutscenes/VideoCutscene.hx @@ -186,7 +186,7 @@ class VideoCutscene extends Cutscene { text.destroy(); }}); - __pausable = __loaded = true; + pausable = __loaded = true; video.play(); } diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 88a2efdb..31ebc7c8 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -23,6 +23,7 @@ import flixel.tweens.FlxEase; import flixel.util.FlxDestroyUtil; import funkin.backend.system.Flags; +import funkin.Types; import funkin.menus.ui.Alphabet; import flixel.group.FlxGroup.FlxTypedGroup; diff --git a/source/funkin/menus/BetaWarningState.hx b/source/funkin/menus/BetaWarningState.hx deleted file mode 100644 index 30e3c4fd..00000000 --- a/source/funkin/menus/BetaWarningState.hx +++ /dev/null @@ -1,57 +0,0 @@ -package funkin.menus; - -import flixel.text.FlxText; -import flixel.util.FlxColor; -import funkin.backend.FunkinText; - -class BetaWarningState extends MusicBeatState { - var titleAlphabet:Alphabet; - var disclaimer:FunkinText; - - var transitioning:Bool = false; - - public override function create() { - super.create(); - - titleAlphabet = new Alphabet(0, 0, "WARNING", true); - titleAlphabet.screenCenter(X); - add(titleAlphabet); - - disclaimer = new FunkinText(16, titleAlphabet.y + titleAlphabet.height + 10, FlxG.width - 32, "", 32); - disclaimer.alignment = CENTER; - disclaimer.applyMarkup('This engine is still in a *${Flags.RELEASE_CYCLE}* state. That means *majority of the features* are either *buggy* or *non finished*. If you find any bugs, please report them to the Codename Engine GitHub.\n\nPress ENTER to continue', - [ - new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFFF4444), "*") - ] - ); - add(disclaimer); - - var off = Std.int((FlxG.height - (disclaimer.y + disclaimer.height)) / 2); - disclaimer.y += off; - titleAlphabet.y += off; - - DiscordUtil.call("onMenuLoaded", ["Beta Warning"]); - } - - public override function update(elapsed:Float) { - super.update(elapsed); - - if (controls.ACCEPT && transitioning) { - FlxG.camera.stopFX(); FlxG.camera.visible = false; - goToTitle(); - } - - if (controls.ACCEPT && !transitioning) { - transitioning = true; - CoolUtil.playMenuSFX(CONFIRM); - FlxG.camera.flash(FlxColor.WHITE, 1, function() { - FlxG.camera.fade(FlxColor.BLACK, 2.5, false, goToTitle); - }); - } - } - - private function goToTitle() { - MusicBeatState.skipTransIn = MusicBeatState.skipTransOut = true; - FlxG.switchState(new TitleState()); - } -} \ No newline at end of file diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index 9bb7c9a9..bed77535 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -139,6 +139,8 @@ class FreeplayState extends MusicBeatState var icon:HealthIcon = new HealthIcon(songs[i].icon); icon.sprTracker = songText; + icon.setUnstretchedGraphicSize(150, 150, true); + icon.updateHitbox(); // using a FlxGroup is too much fuss! iconArray.push(icon); @@ -234,7 +236,7 @@ class FreeplayState extends MusicBeatState scoreText.x = coopText.x = scoreBG.x + 4; diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2)); - interpColor.fpsLerpTo(songs[curSelected].parsedColor, 0.0625); + interpColor.fpsLerpTo(songs[curSelected].color, 0.0625); bg.color = interpColor.color; #if PRELOAD_ALL @@ -320,10 +322,9 @@ class FreeplayState extends MusicBeatState } public function convertChart() { - var curSong = songs[curSelected]; - trace('Converting ${curSong.name} (${curSong.difficulties[curDifficulty]}) to Codename format...'); - var chart = Chart.parse(curSong.name, curSong.difficulties[curDifficulty]); - Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSong.difficulties[curDifficulty].toLowerCase()); + trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...'); + var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]); + Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty]); } /** @@ -346,9 +347,9 @@ class FreeplayState extends MusicBeatState updateScore(); if (curSong.difficulties.length > 1) - diffText.text = '< ${curSong.difficulties[curDifficulty]} >'; + diffText.text = '< ${curSong.difficulties[curDifficulty].toUpperCase()} >'; else - diffText.text = validDifficulties ? curSong.difficulties[curDifficulty] : "-"; + diffText.text = validDifficulties ? curSong.difficulties[curDifficulty].toUpperCase() : "-"; } function updateScore() { @@ -453,16 +454,10 @@ class FreeplaySonglist { public function getSongsFromSource(source:funkin.backend.assets.AssetsLibraryList.AssetSource, useTxt:Bool = true) { var path:String = Paths.txt('freeplaySonglist'); - var songsFound:Array = []; - if (useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source)) { - songsFound = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); - } else { - songsFound = Paths.getFolderDirectories('songs', false, source); - } + var songsFound:Array = useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source) ? CoolUtil.coolTextFile(path) : Paths.getFolderDirectories('songs', false, source); if (songsFound.length > 0) { - for(s in songsFound) - songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS)); + for (s in songsFound) songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS)); return false; } return true; @@ -485,4 +480,4 @@ class FreeplaySonglist { return songList; } -} \ No newline at end of file +} diff --git a/source/funkin/menus/GitarooPause.hx b/source/funkin/menus/GitarooPause.hx index cff8aec9..f72e7d2b 100644 --- a/source/funkin/menus/GitarooPause.hx +++ b/source/funkin/menus/GitarooPause.hx @@ -64,9 +64,7 @@ class GitarooPause extends MusicBeatState } else { - PlayState.resetSongInfos(); if (Charter.instance != null) Charter.instance.__clearStatics(); - FlxG.switchState(new MainMenuState()); } } diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 06f1cbe1..220bf73b 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -92,7 +92,7 @@ class MainMenuState extends MusicBeatState if (!selectedSomethin) { if (canAccessDebugMenus) { - if (FlxG.keys.justPressed.SEVEN) { + if (controls.DEV_ACCESS) { persistentUpdate = false; persistentDraw = true; openSubState(new funkin.editors.EditorPicker()); diff --git a/source/funkin/menus/ModSwitchMenu.hx b/source/funkin/menus/ModSwitchMenu.hx index 38175085..56ef0ddc 100644 --- a/source/funkin/menus/ModSwitchMenu.hx +++ b/source/funkin/menus/ModSwitchMenu.hx @@ -12,9 +12,15 @@ class ModSwitchMenu extends MusicBeatSubstate { var alphabets:FlxTypedGroup; var curSelected:Int = 0; + var subCam:FlxCamera; + public override function create() { super.create(); + camera = subCam = new FlxCamera(); + subCam.bgColor = 0; + FlxG.cameras.add(subCam, false); + var bg = new FlxSprite(0, 0).makeSolid(FlxG.width, FlxG.height, 0xFF000000); bg.updateHitbox(); bg.scrollFactor.set(); @@ -66,5 +72,12 @@ class ModSwitchMenu extends MusicBeatSubstate { } alphabets.members[curSelected].alpha = 1; } + + override function destroy() { + super.destroy(); + + if (FlxG.cameras.list.contains(subCam)) + FlxG.cameras.remove(subCam); + } } #end diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index 8badd60a..e8ad62af 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -165,7 +165,6 @@ class PauseSubState extends MusicBeatSubstate if (PlayState.chartingMode && Charter.undos.unsaved) game.saveWarn(false); else { - PlayState.resetSongInfos(); if (Charter.instance != null) Charter.instance.__clearStatics(); // prevents certain notes to disappear early when exiting - Nex diff --git a/source/funkin/menus/StoryMenuState.hx b/source/funkin/menus/StoryMenuState.hx index 79ef23de..ede7a714 100644 --- a/source/funkin/menus/StoryMenuState.hx +++ b/source/funkin/menus/StoryMenuState.hx @@ -5,17 +5,20 @@ import flixel.text.FlxText; import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; +import flixel.util.typeLimit.OneOfTwo; import funkin.backend.FunkinText; +import funkin.backend.scripting.events.CancellableEvent; import funkin.backend.scripting.events.menu.MenuChangeEvent; import funkin.backend.scripting.events.menu.storymenu.*; -import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.week.*; import funkin.savedata.FunkinSave; import haxe.io.Path; import haxe.xml.Access; class StoryMenuState extends MusicBeatState { - public var characters:Map = []; + public var characters:Map = []; public var weeks:Array = []; + public var weekList:StoryWeeklist; // yes it supports parameters - Nex public var scoreMessage:String = "WEEK SCORE:{0}"; @@ -33,7 +36,6 @@ class StoryMenuState extends MusicBeatState { public var blackBar:FlxSprite; public var weekBG:FlxSprite; - public var defColor:String = "#F9CF51"; public var interpColor:FlxInterpolateColor; public var lerpScore:Float = 0; @@ -64,7 +66,7 @@ class StoryMenuState extends MusicBeatState { weekTitle.alpha = 0.7; weekBG = new FlxSprite(0, 56).makeSolid(FlxG.width, 400, 0xFFFFFFFF); - weekBG.color = weeks.length > 0 ? weeks[0].bgColor : FlxColor.fromString(defColor); + weekBG.color = weeks.length > 0 ? weeks[0].bgColor : Flags.DEFAULT_WEEK_COLOR; weekBG.updateHitbox(); weekSprites = new FlxTypedGroup(); @@ -92,19 +94,18 @@ class StoryMenuState extends MusicBeatState { tracklist.color = 0xFFE55777; add(weekSprites); - for(e in [blackBar, scoreText, weekTitle, weekBG, tracklist]) { - e.antialiasing = true; + for (e in [blackBar, scoreText, weekTitle, weekBG, tracklist]) { e.scrollFactor.set(); add(e); } add(characterSprites = new FlxTypedGroup()); - for(i=>week in weeks) { + for (i=>week in weeks) { var spr:MenuItem = new MenuItem(0, (i * 120) + 480, 'menus/storymenu/weeks/${week.sprite}'); weekSprites.add(spr); - for(e in week.difficulties) { + for (e in week.difficulties) { var le = e.toLowerCase(); if (difficultySprites[le] == null) { var diffSprite = new FlxSprite(leftArrow.x + leftArrow.width, leftArrow.y); @@ -163,6 +164,12 @@ class StoryMenuState extends MusicBeatState { weekBG.color = interpColor.color; } + public override function beatHit(curBeat:Int) { + super.beatHit(curBeat); + if (characterSprites != null) // reason why I wanted to use a MusicBeatGroup, but eh, whatever - Nex + characterSprites.forEachAlive(function(spr) spr.beatHit(curBeat)); + } + public function goBack() { var event = event("onGoBack", new CancellableEvent()); if (!event.cancelled) @@ -181,13 +188,16 @@ class StoryMenuState extends MusicBeatState { e.targetY = k - curWeek; e.alpha = k == curWeek ? 1.0 : 0.6; } - tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.name].join('\n')}'; + tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.displayName.getDefault(e.name).toUpperCase()].join('\n')}'; weekTitle.text = weeks[curWeek].name.getDefault(""); - if(characterSprites != null) for(i in 0...3) { - var curChar:FunkinSprite; var newChar:Access = characters[weeks[curWeek].chars[i]]; - if(newChar == null) modifyCharacterAt(i); - else if((curChar = characterSprites.members[i]) == null || newChar.getAtt("name") != curChar.name) modifyCharacterAt(i, newChar); + if (characterSprites != null) for (i in 0...3) { + var char = weeks[curWeek].chars[i]; + var curChar:FunkinSprite = null; + var newChar = null; + + if (char == null || (newChar = characters[char.name]) == null) modifyCharacterAt(i); + else if ((curChar = cast characterSprites.members[i]) == null || newChar.name != curChar.name) modifyCharacterAt(i, newChar); // forcing the sprites to be FunkinSprite basically - Nex } changeDifficulty(0, true); @@ -195,27 +205,6 @@ class StoryMenuState extends MusicBeatState { MemoryUtil.clearMinor(); } - public function modifyCharacterAt(i:Int, ?node:Access):FunkinSprite { - var old = characterSprites.members[i]; - if(old != null) { - characterSprites.remove(old); - old.destroy(); - } - - if(node == null) return null; - var curChar:FunkinSprite = XMLUtil.createSpriteFromXML(node, "", BEAT); - curChar.offset.x += curChar.x; curChar.offset.y += curChar.y; - curChar.setPosition((FlxG.width * 0.25) * (1 + i) - 150, 70); - if(characterSprites != null) characterSprites.insert(i, curChar); // Making so many null checks abt this group just in case if mods destroy it - Nex - curChar.playAnim("idle", true, DANCE); - return curChar; - } - - public override function beatHit(curBeat:Int) { - super.beatHit(curBeat); - if(characterSprites != null) characterSprites.forEachAlive(function(spr) spr.beatHit(curBeat)); - } - var __oldDiffName = null; public function changeDifficulty(change:Int, force:Bool = false) { if (change == 0 && !force) return; @@ -244,122 +233,44 @@ class StoryMenuState extends MusicBeatState { } public function loadXMLs() { - // CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); - var weeks:Array = []; - - switch(Flags.WEEKS_LIST_MOD_MODE) { - case 'prepend': - getWeeksFromSource(weeks, MODS); - getWeeksFromSource(weeks, SOURCE); - case 'append': - getWeeksFromSource(weeks, SOURCE); - getWeeksFromSource(weeks, MODS); - default /*case 'override'*/: - if (getWeeksFromSource(weeks, MODS)) - getWeeksFromSource(weeks, SOURCE); - } - - for(k=>weekName in weeks) { - var week:Access = null; - try { - week = new Access(Xml.parse(Assets.getText(Paths.xml('weeks/weeks/$weekName'))).firstElement()); - } catch(e) { - Logs.trace('Cannot parse week "$weekName.xml": ${Std.string(e)}`', ERROR); - } - - if (week == null) continue; - - if (!week.has.name) { - Logs.trace('Story Menu: Week at index ${k} has no name. Skipping...', WARNING); - continue; - } - var weekObj:WeekData = { - name: week.att.name, - id: weekName, - sprite: week.getAtt('sprite').getDefault(weekName), - chars: [null, null, null], - songs: [], - difficulties: ['easy', 'normal', 'hard'], - bgColor: FlxColor.fromString(week.getAtt("bgColor").getDefault(defColor)) - }; - - var diffNodes = week.nodes.difficulty; - if (diffNodes.length > 0) { - var diffs:Array = []; - for(e in diffNodes) { - if (e.has.name) diffs.push(e.att.name); - } - if (diffs.length > 0) - weekObj.difficulties = diffs; - } - - if (week.has.chars) { - for(k=>e in week.att.chars.split(",")) { - if (e.trim() == "" || e == "none" || e == "null") - weekObj.chars[k] = null; - else { - addCharacter(weekObj.chars[k] = e.trim()); - } - } - } - for(k2=>song in week.nodes.song) { - if (song == null) continue; - try { - var name = song.innerData.trim(); - if (name == "") { - Logs.trace('Story Menu: Song at index ${k2} in week ${weekObj.name} has no name. Skipping...', WARNING); - continue; - } - weekObj.songs.push({ - name: name, - hide: song.getAtt('hide').getDefault('false') == "true" - }); - } catch(e) { - Logs.trace('Story Menu: Song at index ${k2} in week ${weekObj.name} cannot contain any other XML nodes in its name.', WARNING); - continue; - } - } - if (weekObj.songs.length <= 0) { - Logs.trace('Story Menu: Week ${weekObj.name} has no songs. Skipping...', WARNING); - continue; - } - this.weeks.push(weekObj); - } + weekList = StoryWeeklist.get(true, false); // will only load week files AND NOT characters too (we will load them later only if needed)!! - Nex + weeks = weekList.weeks; + for (week in weeks) for (char in week.chars) if (char != null) + addCharacter(char.name); } - public function addCharacter(charName:String) { - var char:Access = null; - try { - char = new Access(Xml.parse(Assets.getText(Paths.xml('weeks/characters/$charName'))).firstElement()); - } catch(e) { - Logs.trace('Story Menu: Cannot parse character "$charName.xml": ${Std.string(e)}`', ERROR); - } + public function addCharacter(char:OneOfTwo) { + // better to use unsafe casts for going fast - Nex + var charObj:WeekData.WeekCharacter = null; + var charName:String; - if(char != null && characters[charName] == null) { - if(!char.x.exists("name")) char.x.set("name", charName); - if(!char.x.exists("sprite")) char.x.set("sprite", 'menus/storymenu/characters/${charName}'); - if(!char.x.exists("beatInterval")) char.x.set("beatInterval", "1"); - if(!char.x.exists("updateHitbox")) char.x.set("updateHitbox", "true"); - characters[charName] = char; - } + charName = char is String ? cast char : (charObj = cast char).name; + if (characters[charName] != null) return; // will load only if it can be saved inside the map - Nex + characters[charName] = charObj == null ? Week.loadWeekCharacter(charName) : charObj; } - public function getWeeksFromSource(weeks:Array, source:funkin.backend.assets.AssetsLibraryList.AssetSource) { - var path:String = Paths.txt('freeplaySonglist'); - var weeksFound:Array = []; - if (Paths.assetsTree.existsSpecific(path, "TEXT", source)) { - var trim = ""; - weeksFound = CoolUtil.coolTextFile(Paths.txt('weeks/weeks')); - } else { - weeksFound = [for(c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)]; - } + public function modifyCharacterAt(i:Int, ?data:WeekData.WeekCharacter):FunkinSprite { + var curChar:FunkinSprite = null; - if (weeksFound.length > 0) { - for(s in weeksFound) - weeks.push(s); - return false; + if (characterSprites != null) { + var old = characterSprites.members[i]; + if (old != null) { + characterSprites.remove(old); + old.destroy(); + } + + if (data != null) { + curChar = XMLUtil.createSpriteFromXML(data.xml, "", BEAT); + curChar.offset.x += curChar.x; curChar.offset.y += curChar.y; + curChar.setPosition((FlxG.width * 0.25) * (1 + i) - 150, 70); + curChar.playAnim("idle", true, DANCE); + characterSprites.insert(i, curChar); + } else { + //characterSprites.members[i] = null; // cant add nulls to flxgroups, so - Nex + characterSprites.insert(i, new FunkinSprite()).visible = false; + } } - return true; + return curChar; } public function selectWeek() { @@ -369,7 +280,7 @@ class StoryMenuState extends MusicBeatState { canSelect = false; CoolUtil.playMenuSFX(CONFIRM); - if(characterSprites != null) + if (characterSprites != null) characterSprites.forEachAlive(function(spr) spr.playAnim("confirm", true, LOCK)); PlayState.loadWeek(event.week, event.difficulty); @@ -382,21 +293,6 @@ class StoryMenuState extends MusicBeatState { } } -typedef WeekData = { - var name:String; // name SHOULD NOT be used for loading week highscores, its just the name on the right side of the week, remember that next time!! - Nex - var id:String; // id IS instead for saving and loading!! - Nex - var sprite:String; - var chars:Array; - var songs:Array; - var difficulties:Array; - var bgColor:FlxColor; -} - -typedef WeekSong = { - var name:String; - var hide:Bool; -} - class MenuItem extends FlxSprite { public var targetY:Float = 0; @@ -438,3 +334,42 @@ class MenuItem extends FlxSprite color = (time % 0.1 > 0.05) ? FlxColor.WHITE : 0xFF33ffff; } } + +class StoryWeeklist { + public var weeks:Array = []; + + public function new() {} + + public function getWeeksFromSource(source:funkin.backend.assets.AssetsLibraryList.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) { + var path:String = Paths.txt('weeks/weeks'); + var weeksFound:Array = useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source) ? CoolUtil.coolTextFile(path) : + [for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)]; + + if (weeksFound.length > 0) { + for (w in weeksFound) { + var week = Week.loadWeek(w, loadCharactersData); + if (week != null) weeks.push(week); + } + return false; + } + return true; + } + + public static function get(useTxt:Bool = true, loadCharactersData:Bool = true) { + var weekList = new StoryWeeklist(); + + switch(Flags.WEEKS_LIST_MOD_MODE) { + case 'prepend': + weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData); + weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData); + case 'append': + weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData); + weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData); + default /*case 'override'*/: + if (weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData)) + weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData); + } + + return weekList; + } +} \ No newline at end of file diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx index e447ef01..fc50386f 100644 --- a/source/funkin/menus/credits/CreditsMain.hx +++ b/source/funkin/menus/credits/CreditsMain.hx @@ -1,11 +1,12 @@ package funkin.menus.credits; +import flixel.util.FlxColor; +import funkin.backend.assets.AssetsLibraryList.AssetSource; +import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor; import funkin.options.OptionsScreen; import funkin.options.TreeMenu; -import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor; import funkin.options.type.*; import haxe.xml.Access; -import flixel.util.FlxColor; class CreditsMain extends TreeMenu { var bg:FlxSprite; @@ -21,21 +22,17 @@ class CreditsMain extends TreeMenu { bg.antialiasing = true; add(bg); - var xmlPath = Paths.xml('config/credits'); - for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) { - if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) { - var access:Access = null; - try { - access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source))); - } catch(e) { - Logs.trace('[CreditsMain] Error while parsing credits.xml: ${Std.string(e)}', ERROR); - } + for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) { + var xmlPath = Paths.xml('config/credits/LIB_$i'); - if (access != null) - for(c in parseCreditsFromXML(access, source)) - items.push(c); + if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT")) { + var access:Access = null; + try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT"))) + catch(e) Logs.trace('[CreditsMain] Error while parsing credits.xml: ${Std.string(e)}', ERROR); + if (access != null) for (c in parseCreditsFromXML(access)) items.push(c); } } + items.push(new TextOption("Codename Engine >", "Select this to see all the contributors of the engine!", function() { optionsTree.add(Type.createInstance(CreditsCodename, [])); })); @@ -52,7 +49,7 @@ class CreditsMain extends TreeMenu { /** * XML STUFF */ - public function parseCreditsFromXML(xml:Access, source:funkin.backend.assets.AssetsLibraryList.AssetSource):Array { + public function parseCreditsFromXML(xml:Access, source:AssetSource = BOTH):Array { var credsMenus:Array = []; for(node in xml.elements) { diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index 3ae5a127..7714647a 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -52,7 +52,9 @@ class Alphabet extends FlxSpriteGroup public function refreshAlphabetXML(path:String) { AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path; try { - var xml = new Access(Xml.parse(Assets.getText(path)).firstElement()); + var file = Assets.getText(path); + if(file == null) return; + var xml = new Access(Xml.parse(file).firstElement()); AlphaCharacter.boldAnims = []; AlphaCharacter.letterAnims = []; AlphaCharacter.boldAlphabetPath = AlphaCharacter.letterAlphabetPath = 'ui/alphabet'; diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 9ca1dab7..8a3493ad 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -33,6 +33,7 @@ class Options public static var gameplayShaders:Bool = true; public static var colorHealthBar:Bool = true; public static var lowMemoryMode:Bool = false; + public static var devMode:Bool = false; public static var betaUpdates:Bool = false; public static var splashesEnabled:Bool = true; public static var hitWindow:Float = 250; @@ -94,6 +95,8 @@ class Options public static var P1_NOTE_DOWN:Array = [S]; public static var P1_NOTE_UP:Array = [W]; public static var P1_NOTE_RIGHT:Array = [D]; + + // Menus public static var P1_LEFT:Array = [A]; public static var P1_DOWN:Array = [S]; public static var P1_UP:Array = [W]; @@ -101,16 +104,30 @@ class Options public static var P1_ACCEPT:Array = [ENTER]; public static var P1_BACK:Array = [BACKSPACE]; public static var P1_PAUSE:Array = [ENTER]; + + // Misc public static var P1_RESET:Array = [R]; public static var P1_SWITCHMOD:Array = [TAB]; + public static var P1_VOLUME_UP:Array = []; + public static var P1_VOLUME_DOWN:Array = []; + public static var P1_VOLUME_MUTE:Array = []; + + // Debugs + public static var P1_DEV_ACCESS:Array = [SEVEN]; + public static var P1_DEV_CONSOLE:Array = [F2]; + public static var P1_DEV_RELOAD:Array = [F5]; /** - * PLAYER 2 CONTROLS (ALT) - */ + * PLAYER 2 CONTROLS (ALT) + */ + + // Notes public static var P2_NOTE_LEFT:Array = [LEFT]; public static var P2_NOTE_DOWN:Array = [DOWN]; public static var P2_NOTE_UP:Array = [UP]; public static var P2_NOTE_RIGHT:Array = [RIGHT]; + + // Menus public static var P2_LEFT:Array = [LEFT]; public static var P2_DOWN:Array = [DOWN]; public static var P2_UP:Array = [UP]; @@ -118,16 +135,30 @@ class Options public static var P2_ACCEPT:Array = [SPACE]; public static var P2_BACK:Array = [ESCAPE]; public static var P2_PAUSE:Array = [ESCAPE]; + + // Misc public static var P2_RESET:Array = []; public static var P2_SWITCHMOD:Array = []; + public static var P2_VOLUME_UP:Array = [PLUS]; + public static var P2_VOLUME_DOWN:Array = [MINUS]; + public static var P2_VOLUME_MUTE:Array = [ZERO]; + + // Debugs + public static var P2_DEV_ACCESS:Array = []; + public static var P2_DEV_CONSOLE:Array = []; + public static var P2_DEV_RELOAD:Array = []; /** - * SOLO GETTERS - */ + * SOLO GETTERS + */ + + // Notes public static var SOLO_NOTE_LEFT(get, null):Array; public static var SOLO_NOTE_DOWN(get, null):Array; public static var SOLO_NOTE_UP(get, null):Array; public static var SOLO_NOTE_RIGHT(get, null):Array; + + // Menus public static var SOLO_LEFT(get, null):Array; public static var SOLO_DOWN(get, null):Array; public static var SOLO_UP(get, null):Array; @@ -135,8 +166,18 @@ class Options public static var SOLO_ACCEPT(get, null):Array; public static var SOLO_BACK(get, null):Array; public static var SOLO_PAUSE(get, null):Array; + + // Misc public static var SOLO_RESET(get, null):Array; public static var SOLO_SWITCHMOD(get, null):Array; + public static var SOLO_VOLUME_UP(get, null):Array; + public static var SOLO_VOLUME_DOWN(get, null):Array; + public static var SOLO_VOLUME_MUTE(get, null):Array; + + // Debugs + public static var SOLO_DEV_ACCESS(get, null):Array; + public static var SOLO_DEV_CONSOLE(get, null):Array; + public static var SOLO_DEV_RELOAD(get, null):Array; public static function load() { if (__save == null) __save = new FlxSave(); @@ -165,6 +206,10 @@ class Options PlayerSettings.solo.setKeyboardScheme(Solo); PlayerSettings.player1.setKeyboardScheme(Duo(true)); PlayerSettings.player2.setKeyboardScheme(Duo(false)); + + FlxG.sound.volumeUpKeys = SOLO_VOLUME_UP; + FlxG.sound.volumeDownKeys = SOLO_VOLUME_DOWN; + FlxG.sound.muteKeys = SOLO_VOLUME_MUTE; } public static function save() { diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx index 2b8c0168..4b851df9 100644 --- a/source/funkin/options/OptionsMenu.hx +++ b/source/funkin/options/OptionsMenu.hx @@ -54,36 +54,24 @@ class OptionsMenu extends TreeMenu { if (o.substate != null) { persistentUpdate = false; persistentDraw = true; - if (o.substate is MusicBeatSubstate) { - openSubState(o.substate); - } else { - openSubState(Type.createInstance(o.substate, [])); - } + if (o.substate is MusicBeatSubstate) openSubState(o.substate); + else openSubState(Type.createInstance(o.substate, [])); } else { - if (o.state is OptionsScreen) { - optionsTree.add(o.state); - } else { - optionsTree.add(Type.createInstance(o.state, [])); - } + if (o.state is OptionsScreen) optionsTree.add(o.state); + else optionsTree.add(Type.createInstance(o.state, [])); } })]); - var xmlPath = Paths.xml("config/options"); - for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) { - if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) { - var access:Access = null; - try { - access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source))); - } catch(e) { - Logs.trace('Error while parsing options.xml: ${Std.string(e)}', ERROR); - } + for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) { + var xmlPath = Paths.xml('config/options/LIB_$i'); - if (access != null) - for(o in parseOptionsFromXML(access)) - main.add(o); + if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT")) { + var access:Access = null; + try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT"))) + catch(e) Logs.trace('Error while parsing options.xml: ${Std.string(e)}', ERROR); + if (access != null) for (o in parseOptionsFromXML(access)) main.add(o); } } - } public override function exit() { diff --git a/source/funkin/options/categories/MiscOptions.hx b/source/funkin/options/categories/MiscOptions.hx index 2aecb20f..52b8e624 100644 --- a/source/funkin/options/categories/MiscOptions.hx +++ b/source/funkin/options/categories/MiscOptions.hx @@ -1,9 +1,12 @@ package funkin.options.categories; - class MiscOptions extends OptionsScreen { public override function new() { - super("Miscellaneous", "Use this menu to reset save data or engine settings."); + super("Miscellaneous", "Change some other options like enabling Developer Mode, resetting data..."); + add(new Checkbox( + "Developer Mode", + "If checked, you will be able to access developer features like certain keybinds, editors, developer options, state reloads, console and more.", + "devMode")); #if UPDATE_CHECKING add(new Checkbox( "Enable Nightly Updates", diff --git a/source/funkin/options/keybinds/KeybindsOptions.hx b/source/funkin/options/keybinds/KeybindsOptions.hx index afd552a0..861296ba 100644 --- a/source/funkin/options/keybinds/KeybindsOptions.hx +++ b/source/funkin/options/keybinds/KeybindsOptions.hx @@ -67,6 +67,23 @@ class KeybindsOptions extends MusicBeatSubstate { }, ] }, + { + name: 'Volume', + settings: [ + { + name: 'Up', + control: 'VOLUME_UP' + }, + { + name: 'Down', + control: 'VOLUME_DOWN' + }, + { + name: 'Mute', + control: 'VOLUME_MUTE' + }, + ] + }, { name: 'Engine', settings: [ @@ -75,6 +92,26 @@ class KeybindsOptions extends MusicBeatSubstate { control: 'SWITCHMOD' }, ] + }, + { + name: 'Developer', + devModeOnly: true, + settings: [ + { + name: 'Developer Menus', + control: 'DEV_ACCESS' + }, + #if GLOBAL_SCRIPT // since theyre integrated into global script - Nex + { + name: 'Open Console', + control: 'DEV_CONSOLE' + }, + { + name: 'Reload State', + control: 'DEV_RELOAD' + }, + #end + ] } ]; @@ -132,7 +169,9 @@ class KeybindsOptions extends MusicBeatSubstate { for (i in customCategories) categories.push(i); var k:Int = 0; - for(category in categories) { + for (category in categories) { + if (category.devModeOnly && !Options.devMode) continue; + k++; var title = new Alphabet(0, k * 75, category.name, true); title.screenCenter(X); @@ -168,6 +207,10 @@ class KeybindsOptions extends MusicBeatSubstate { } add(alphabets); add(camFollow); + + FlxG.sound.volumeUpKeys = []; + FlxG.sound.volumeDownKeys = []; + FlxG.sound.muteKeys = []; } public override function destroy() { @@ -181,10 +224,8 @@ class KeybindsOptions extends MusicBeatSubstate { public override function update(elapsed:Float) { super.update(elapsed); - - if (isSubState) { - bg.alpha = lerp(bg.alpha, 0.1, 0.125); - } else { + if (isSubState) bg.alpha = lerp(bg.alpha, 0.1, 0.125); + else { if (curSelected < 4) { if (coloredBG.alpha == 0) coloredBG.color = noteColors[curSelected]; @@ -200,11 +241,11 @@ class KeybindsOptions extends MusicBeatSubstate { changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0)); if (controls.BACK) { - MusicBeatState.skipTransIn = true; - if (isSubState) - close(); - else + if (isSubState) close(); + else { + MusicBeatState.skipTransIn = true; FlxG.switchState(new OptionsMenu()); + } ControlsUtil.resetCustomControls(); Options.applyKeybinds(); ControlsUtil.loadCustomControls(); @@ -251,7 +292,7 @@ class KeybindsOptions extends MusicBeatSubstate { var minH = FlxG.height / 2; var maxH = alphabets.members[alphabets.length-1].y + alphabets.members[alphabets.length-1].height - (FlxG.height / 2); if (minH < maxH) - camFollow.setPosition(FlxG.width / 2, CoolUtil.bound(alphabet.y + (alphabet.height / 2) - (35), minH, maxH)); + camFollow.setPosition(FlxG.width / 2, CoolUtil.bound(alphabet.y + (alphabet.height / 2) - 35, minH, maxH)); else camFollow.setPosition(FlxG.width / 2, FlxG.height / 2); } @@ -311,4 +352,5 @@ typedef KeybindSettingData = { typedef ControlsCategory = { var name:String; var settings:Array; + var ?devModeOnly:Bool; } \ No newline at end of file diff --git a/source/funkin/options/type/GithubIconOption.hx b/source/funkin/options/type/GithubIconOption.hx index 15a063cd..ef2d187b 100644 --- a/source/funkin/options/type/GithubIconOption.hx +++ b/source/funkin/options/type/GithubIconOption.hx @@ -49,7 +49,21 @@ class GithubUserIcon extends FlxSprite super.update(elapsed); } + #if target.threaded final mutex = new sys.thread.Mutex(); + #end + + inline function acquireMutex() { + #if target.threaded + mutex.acquire(); + #end + } + inline function releaseMutex() { + #if target.threaded + mutex.release(); + #end + } + override function drawComplex(camera:FlxCamera):Void { // Making the image download only if the player actually sees it on the screen - Nex if(waitUntilLoad <= 0) { waitUntilLoad = null; @@ -82,19 +96,19 @@ class GithubUserIcon extends FlxSprite } if(bmap != null) try { - mutex.acquire(); // Avoiding critical section - Nex + acquireMutex(); // Avoiding critical section - Nex var leGraphic:FlxGraphic = FlxG.bitmap.add(bmap, false, key); leGraphic.persist = true; updateDaFunni(leGraphic); bmap = null; - mutex.release(); + releaseMutex(); } catch(e) { Logs.traceColored([Logs.logText('Failed to update the pfp for ${user.login}: ${e.message}', RED)], ERROR); } } else { - mutex.acquire(); + acquireMutex(); updateDaFunni(bmap); - mutex.release(); + releaseMutex(); } }); } diff --git a/source/funkin/options/type/IconOption.hx b/source/funkin/options/type/IconOption.hx index aa4ad3c2..b61c5c67 100644 --- a/source/funkin/options/type/IconOption.hx +++ b/source/funkin/options/type/IconOption.hx @@ -14,6 +14,10 @@ class IconOption extends TextOption { iconSpr = new HealthIcon(icon, false); iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2); + iconSpr.setUnstretchedGraphicSize(150, 150, true); + iconSpr.updateHitbox(); + iconSpr.sprTracker = __text; + iconSpr.sprTrackerAlignment = LEFT; add(iconSpr); } } \ No newline at end of file diff --git a/source/funkin/options/type/NewOption.hx b/source/funkin/options/type/NewOption.hx index 0008566e..28255b8c 100644 --- a/source/funkin/options/type/NewOption.hx +++ b/source/funkin/options/type/NewOption.hx @@ -1,7 +1,6 @@ package funkin.options.type; import flixel.util.FlxColor; -import funkin.game.HealthIcon; /** * Option type that has a new button, and is green. diff --git a/source/lime/ui/FileDialog.hx b/source/lime/ui/FileDialog.hx new file mode 100644 index 00000000..418d7119 --- /dev/null +++ b/source/lime/ui/FileDialog.hx @@ -0,0 +1,411 @@ +package lime.ui; + +import haxe.io.Bytes; +import haxe.io.Path; +import lime._internal.backend.native.NativeCFFI; +import lime.app.Event; +import lime.graphics.Image; +import lime.system.BackgroundWorker; +import lime.utils.ArrayBuffer; +import lime.utils.Resource; +#if hl +import hl.Bytes as HLBytes; +import hl.NativeArray; +#end +#if sys +import sys.io.File; +#end +#if (js && html5) +import js.html.Blob; +#end + +/** + Simple file dialog used for asking user where to save a file, or select files to open. + + Example usage: + ```haxe + var fileDialog = new FileDialog(); + + fileDialog.onCancel.add( () -> trace("Canceled.") ); + + fileDialog.onSave.add( path -> trace("File saved in " + path) ); + + fileDialog.onOpen.add( res -> trace("Size of the file = " + (res:haxe.io.Bytes).length) ); + + if ( fileDialog.open("jpg", null, "Load file") ) + trace("File dialog opened, waiting for selection..."); + else + trace("This dialog is unsupported."); + ``` + + Availability note: most file dialog operations are only available on desktop targets, though + `save()` is also available in HTML5. +**/ +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(lime._internal.backend.native.NativeCFFI) +@:access(lime.graphics.Image) +class FileDialog +{ + /** + Triggers when the user clicks "Cancel" during any operation, or when a function is unsupported + (such as `open()` on HTML5). + **/ + public var onCancel = new EventVoid>(); + + /** + Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can + be implicitly cast to `haxe.io.Bytes`. + **/ + public var onOpen = new EventVoid>(); + + /** + Triggers when `open()` is successful. The `lime.utils.Resource` contains the file's data, and can + be implicitly cast to `haxe.io.Bytes`, the String is the path to the file. + **/ + public var onOpenFile = new Event<(Resource, String)->Void>(); // Added by @NeeEoo + + /** + Triggers when `save()` is successful. The `String` is the path to the saved file. + **/ + public var onSave = new EventVoid>(); + + /** + Triggers when `browse()` is successful and `type` is anything other than + `FileDialogType.OPEN_MULTIPLE`. The `String` is the path to the selected file. + **/ + public var onSelect = new EventVoid>(); + + /** + Triggers when `browse()` is successful and `type` is `FileDialogType.OPEN_MULTIPLE`. The + `Array` contains all selected file paths. + **/ + public var onSelectMultiple = new Event->Void>(); + + public function new() {} + + /** + Opens a file selection dialog. If successful, either `onSelect` or `onSelectMultiple` will trigger + with the result(s). + + This function only works on desktop targets, and will return `false` otherwise. + @param type Type of the file dialog: `OPEN`, `SAVE`, `OPEN_DIRECTORY` or `OPEN_MULTIPLE`. + @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example, + `"*.jpg"` will match any file ending in `.jpg`. + @param defaultPath The directory in which to start browsing and/or the default filename to + suggest. Defaults to `Sys.getCwd()`, with no default filename. + @param title The title to give the dialog window. + @return Whether `browse()` is supported on this target. + **/ + public function browse(type:FileDialogType = null, filter:String = null, defaultPath:String = null, title:String = null):Bool + { + if (type == null) type = FileDialogType.OPEN; + + #if desktop + var worker = new BackgroundWorker(); + + worker.doWork.add(function(_) + { + switch (type) + { + case OPEN: + #if linux + if (title == null) title = "Open File"; + #end + + var path = null; + #if (!macro && lime_cffi) + #if hl + var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath); + if (bytes != null) + { + path = @:privateAccess String.fromUTF8(cast bytes); + } + #else + path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath); + #end + #end + + worker.sendComplete(path); + + case OPEN_MULTIPLE: + #if linux + if (title == null) title = "Open Files"; + #end + + var paths = null; + #if (!macro && lime_cffi) + #if hl + var bytes:NativeArray = cast NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath); + if (bytes != null) + { + paths = []; + for (i in 0...bytes.length) + { + paths[i] = @:privateAccess String.fromUTF8(bytes[i]); + } + } + #else + paths = NativeCFFI.lime_file_dialog_open_files(title, filter, defaultPath); + #end + #end + + worker.sendComplete(paths); + + case OPEN_DIRECTORY: + #if linux + if (title == null) title = "Open Directory"; + #end + + var path = null; + #if (!macro && lime_cffi) + #if hl + var bytes = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath); + if (bytes != null) + { + path = @:privateAccess String.fromUTF8(cast bytes); + } + #else + path = NativeCFFI.lime_file_dialog_open_directory(title, filter, defaultPath); + #end + #end + + worker.sendComplete(path); + + case SAVE: + #if linux + if (title == null) title = "Save File"; + #end + + var path = null; + #if (!macro && lime_cffi) + #if hl + var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath); + if (bytes != null) + { + path = @:privateAccess String.fromUTF8(cast bytes); + } + #else + path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath); + #end + #end + + worker.sendComplete(path); + } + }); + + worker.onComplete.add(function(result) + { + switch (type) + { + case OPEN, OPEN_DIRECTORY, SAVE: + var path:String = cast result; + + if (path != null) + { + // Makes sure the filename ends with extension + if (type == SAVE && filter != null && path.indexOf(".") == -1) + { + path += "." + filter; + } + + onSelect.dispatch(path); + } + else + { + onCancel.dispatch(); + } + + case OPEN_MULTIPLE: + var paths:Array = cast result; + + if (paths != null && paths.length > 0) + { + onSelectMultiple.dispatch(paths); + } + else + { + onCancel.dispatch(); + } + } + }); + + worker.run(); + + return true; + #else + onCancel.dispatch(); + return false; + #end + } + + /** + Shows an open file dialog. If successful, `onOpen` will trigger with the file contents. + + This function only works on desktop targets, and will return `false` otherwise. + @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example, + `"*.jpg"` will match any file ending in `.jpg`. + @param defaultPath The directory in which to start browsing and/or the default filename to + suggest. Defaults to `Sys.getCwd()`, with no default filename. + @param title The title to give the dialog window. + @return Whether `open()` is supported on this target. + **/ + public function open(filter:String = null, defaultPath:String = null, title:String = null):Bool + { + #if (desktop && sys) + var worker = new BackgroundWorker(); + + worker.doWork.add(function(_) + { + #if linux + if (title == null) title = "Open File"; + #end + + var path = null; + #if (!macro && lime_cffi) + #if hl + var bytes = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath); + if (bytes != null) path = @:privateAccess String.fromUTF8(cast bytes); + #else + path = NativeCFFI.lime_file_dialog_open_file(title, filter, defaultPath); + #end + #end + + worker.sendComplete(path); + }); + + worker.onComplete.add(function(path:String) + { + if (path != null) + { + try + { + var data = File.getBytes(path); + onOpen.dispatch(data); + onOpenFile.dispatch(data, path); // Added by @NeeEoo + return; + } + catch (e:Dynamic) {} + } + + onCancel.dispatch(); + }); + + worker.run(); + + return true; + #else + onCancel.dispatch(); + return false; + #end + } + + /** + Shows an open file dialog. If successful, `onSave` will trigger with the selected path. + + This function only works on desktop and HMTL5 targets, and will return `false` otherwise. + @param data The file contents, in `haxe.io.Bytes` format. (Implicit casting possible.) + @param filter A filter to use when browsing. Asterisks are treated as wildcards. For example, + `"*.jpg"` will match any file ending in `.jpg`. Used only if targeting deskop. + @param defaultPath The directory in which to start browsing and/or the default filename to + suggest. When targeting destkop, this defaults to `Sys.getCwd()` with no default filename. When targeting + HTML5, this defaults to the browser's download directory, with a default filename based on the MIME type. + @param title The title to give the dialog window. + @param type The default MIME type of the file, in case the type can't be determined from the + file data. Used only if targeting HTML5. + @return Whether `save()` is supported on this target. + **/ + public function save(data:Resource, filter:String = null, defaultPath:String = null, title:String = null, type:String = "application/octet-stream"):Bool + { + if (data == null) + { + onCancel.dispatch(); + return false; + } + + #if (desktop && sys) + var worker = new BackgroundWorker(); + + worker.doWork.add(function(_) + { + #if linux + if (title == null) title = "Save File"; + #end + + var path = null; + #if (!macro && lime_cffi) + #if hl + var bytes = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath); + path = @:privateAccess String.fromUTF8(cast bytes); + #else + path = NativeCFFI.lime_file_dialog_save_file(title, filter, defaultPath); + #end + #end + + worker.sendComplete(path); + }); + + worker.onComplete.add(function(path:String) + { + if (path != null) + { + try + { + File.saveBytes(path, data); + onSave.dispatch(path); + return; + } + catch (e:Dynamic) {} + } + + onCancel.dispatch(); + }); + + worker.run(); + + return true; + #elseif (js && html5) + // TODO: Cleaner API for mimeType detection + + var defaultExtension = ""; + + if (Image.__isPNG(data)) + { + type = "image/png"; + defaultExtension = ".png"; + } + else if (Image.__isJPG(data)) + { + type = "image/jpeg"; + defaultExtension = ".jpg"; + } + else if (Image.__isGIF(data)) + { + type = "image/gif"; + defaultExtension = ".gif"; + } + else if (Image.__isWebP(data)) + { + type = "image/webp"; + defaultExtension = ".webp"; + } + + var path = defaultPath != null ? Path.withoutDirectory(defaultPath) : "download" + defaultExtension; + var buffer = (data : Bytes).getData(); + buffer = buffer.slice(0, (data : Bytes).length); + + #if commonjs + untyped #if haxe4 js.Syntax.code #else __js__ #end ("require ('file-saver')")(new Blob([buffer], {type: type}), path, true); + #else + untyped window.saveAs(new Blob([buffer], {type: type}), path, true); + #end + onSave.dispatch(path); + return true; + #else + onCancel.dispatch(); + return false; + #end + } +} diff --git a/source/openfl/media/Sound.hx b/source/openfl/media/Sound.hx index b0632e78..cbccc35b 100644 --- a/source/openfl/media/Sound.hx +++ b/source/openfl/media/Sound.hx @@ -695,7 +695,8 @@ class Sound extends EventDispatcher // var samples = (__buffer.data.length * 8) / (__buffer.channels * __buffer.bitsPerSample); // return Std.int(samples / __buffer.sampleRate * 1000); var samples = (Int64.make(0, __buffer.data.length) * Int64.ofInt(8)) / Int64.ofInt(__buffer.channels * __buffer.bitsPerSample); - var value = samples / Int64.ofInt(__buffer.sampleRate) * Int64.ofInt(1000); + var div = Int64.divMod(samples * Int64.ofInt(1000), Int64.ofInt(__buffer.sampleRate)); + var value = (div.quotient + (div.modulus / __buffer.sampleRate)); return Int64.toInt(value); } else if (__buffer.__srcVorbisFile != null)