diff --git a/.gitattributes b/.gitattributes index f2b8f594..4afc5728 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,4 +7,9 @@ pages/** linguist-vendored # Include HScript files as Haxe *.hxs linguist-language=Haxe *.hsc linguist-language=Haxe -*.hscript linguist-language=Haxe \ No newline at end of file +*.hscript linguist-language=Haxe + +# Include Shader files as GLSL +*.frag linguist-language=GLSL +*.vert linguist-language=GLSL +*.glsl linguist-language=GLSL \ No newline at end of file diff --git a/README.md b/README.md index b4d4bcc9..93b36034 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ Also, the command `.\cne test` uses the source assets folder instead of the expo ## Codename Engine Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying modding, along with extensiblity and ease of use.
-Make sure to read the wiki and join the Discord server linked there!
-#### It includes many new features, as seen [here](FEATURES.md)
+### Before making issues or need help with something, check our website [HERE](https://fnf-cne-devs.github.io/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!! +#### The Engine includes many new features, as seen [here](FEATURES.md)
#### Wanna see the new features added in the most recent update? Click [here](PATCHNOTES.md)
## How to download diff --git a/assets/data/characters/bf-car.xml b/assets/data/characters/bf-car.xml index bd276ca8..b73d5a12 100644 --- a/assets/data/characters/bf-car.xml +++ b/assets/data/characters/bf-car.xml @@ -10,9 +10,9 @@ - - - - - + + + + + \ No newline at end of file diff --git a/assets/data/characters/bf-christmas.xml b/assets/data/characters/bf-christmas.xml index 88feb049..1289abdc 100644 --- a/assets/data/characters/bf-christmas.xml +++ b/assets/data/characters/bf-christmas.xml @@ -1,13 +1,13 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/bf-holding-gf.xml b/assets/data/characters/bf-holding-gf.xml index 390504bc..9053ec5c 100644 --- a/assets/data/characters/bf-holding-gf.xml +++ b/assets/data/characters/bf-holding-gf.xml @@ -1,13 +1,13 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/assets/data/characters/bf-pixel.xml b/assets/data/characters/bf-pixel.xml index b1c668a3..621ec9e0 100644 --- a/assets/data/characters/bf-pixel.xml +++ b/assets/data/characters/bf-pixel.xml @@ -1,15 +1,12 @@ - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/bf.xml b/assets/data/characters/bf.xml index 245fbffd..96f90b81 100644 --- a/assets/data/characters/bf.xml +++ b/assets/data/characters/bf.xml @@ -1,18 +1,18 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/assets/data/characters/dad.xml b/assets/data/characters/dad.xml index 39319727..3c8da4c2 100644 --- a/assets/data/characters/dad.xml +++ b/assets/data/characters/dad.xml @@ -1,13 +1,12 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/gf-car.xml b/assets/data/characters/gf-car.xml index a5bc8d41..359a8eea 100644 --- a/assets/data/characters/gf-car.xml +++ b/assets/data/characters/gf-car.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/assets/data/characters/gf-christmas.xml b/assets/data/characters/gf-christmas.xml index 2abd3409..f27cdcbd 100644 --- a/assets/data/characters/gf-christmas.xml +++ b/assets/data/characters/gf-christmas.xml @@ -1,14 +1,14 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/assets/data/characters/gf-pixel.xml b/assets/data/characters/gf-pixel.xml index 73aa39e9..eefc4cba 100644 --- a/assets/data/characters/gf-pixel.xml +++ b/assets/data/characters/gf-pixel.xml @@ -1,5 +1,5 @@ - - - - \ No newline at end of file + + + + diff --git a/assets/data/characters/gf-tankmen.xml b/assets/data/characters/gf-tankmen.xml index 9a9596d9..c498cb57 100644 --- a/assets/data/characters/gf-tankmen.xml +++ b/assets/data/characters/gf-tankmen.xml @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/assets/data/characters/gf.xml b/assets/data/characters/gf.xml index d12f2a0d..5981b647 100644 --- a/assets/data/characters/gf.xml +++ b/assets/data/characters/gf.xml @@ -1,14 +1,14 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/assets/data/characters/mom-car.xml b/assets/data/characters/mom-car.xml index e511498f..ff23aa88 100644 --- a/assets/data/characters/mom-car.xml +++ b/assets/data/characters/mom-car.xml @@ -1,16 +1,13 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + diff --git a/assets/data/characters/mom.xml b/assets/data/characters/mom.xml index 43d52076..88cbc3c9 100644 --- a/assets/data/characters/mom.xml +++ b/assets/data/characters/mom.xml @@ -1,16 +1,14 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/monster-christmas.xml b/assets/data/characters/monster-christmas.xml index 61bae9ee..73eeed57 100644 --- a/assets/data/characters/monster-christmas.xml +++ b/assets/data/characters/monster-christmas.xml @@ -1,8 +1,8 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/monster.xml b/assets/data/characters/monster.xml index df8d70bd..0da80ca9 100644 --- a/assets/data/characters/monster.xml +++ b/assets/data/characters/monster.xml @@ -1,8 +1,8 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/parents-christmas.xml b/assets/data/characters/parents-christmas.xml index 991fff09..4371389f 100644 --- a/assets/data/characters/parents-christmas.xml +++ b/assets/data/characters/parents-christmas.xml @@ -1,12 +1,25 @@ - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/data/characters/pico-speakers.xml b/assets/data/characters/pico-speakers.xml index ffb35177..36ec2528 100644 --- a/assets/data/characters/pico-speakers.xml +++ b/assets/data/characters/pico-speakers.xml @@ -1,16 +1,11 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/assets/data/characters/pico.xml b/assets/data/characters/pico.xml index 74e9baed..e5685dd8 100644 --- a/assets/data/characters/pico.xml +++ b/assets/data/characters/pico.xml @@ -1,16 +1,12 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/senpai-angry.xml b/assets/data/characters/senpai-angry.xml index c81dc67d..a0fab601 100644 --- a/assets/data/characters/senpai-angry.xml +++ b/assets/data/characters/senpai-angry.xml @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/assets/data/characters/senpai.xml b/assets/data/characters/senpai.xml index f243b2e5..762afb67 100644 --- a/assets/data/characters/senpai.xml +++ b/assets/data/characters/senpai.xml @@ -1,10 +1,9 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/assets/data/characters/spirit.xml b/assets/data/characters/spirit.xml index d1ee5dc9..9885a662 100644 --- a/assets/data/characters/spirit.xml +++ b/assets/data/characters/spirit.xml @@ -1,10 +1,8 @@ - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/spooky.xml b/assets/data/characters/spooky.xml index 2a434f21..f60758e4 100644 --- a/assets/data/characters/spooky.xml +++ b/assets/data/characters/spooky.xml @@ -1,9 +1,9 @@ - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/assets/data/characters/tankman.xml b/assets/data/characters/tankman.xml index 38f9abf9..d02f5a41 100644 --- a/assets/data/characters/tankman.xml +++ b/assets/data/characters/tankman.xml @@ -1,10 +1,10 @@ - - - - - - - - + + + + + + + + diff --git a/assets/data/config/credits.xml b/assets/data/config/credits.xml deleted file mode 100644 index 4f300fcb..00000000 --- a/assets/data/config/credits.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/assets/data/config/options.xml b/assets/data/config/options.xml deleted file mode 100644 index 888288d7..00000000 --- a/assets/data/config/options.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/data/scripts/pixel.hx b/assets/data/scripts/pixel.hx index 88bb5af5..26d18251 100644 --- a/assets/data/scripts/pixel.hx +++ b/assets/data/scripts/pixel.hx @@ -12,7 +12,7 @@ public var enablePauseMenu = true; public var isSpooky = false; var oldStageQuality = FlxG.game.stage.quality; -static var daPixelZoom = 6; +public var daPixelZoom = PlayState.daPixelZoom; /** * UI diff --git a/assets/data/scripts/week6-pause.hx b/assets/data/scripts/week6-pause.hx index 6a4455cd..f5fa3394 100644 --- a/assets/data/scripts/week6-pause.hx +++ b/assets/data/scripts/week6-pause.hx @@ -17,7 +17,7 @@ function create(event) { // cancel default pause menu!! event.cancel(); - event.music = isThorns ? "pixel/LunchboxScary" : "pixel/Lunchbox"; + event.music = isThorns ? "pixel/LunchboxScary" : "pixel/breakfast"; cameras = []; diff --git a/assets/data/titlescreen/titlescreen.xml b/assets/data/titlescreen/titlescreen.xml index f16c8b62..323e9ef1 100644 --- a/assets/data/titlescreen/titlescreen.xml +++ b/assets/data/titlescreen/titlescreen.xml @@ -7,7 +7,7 @@ - + diff --git a/assets/data/weeks/characters/bf.xml b/assets/data/weeks/characters/bf.xml index 20e9176d..1f3fa299 100644 --- a/assets/data/weeks/characters/bf.xml +++ b/assets/data/weeks/characters/bf.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/assets/data/weeks/characters/dad.xml b/assets/data/weeks/characters/dad.xml index dfda66e7..2b1cd842 100644 --- a/assets/data/weeks/characters/dad.xml +++ b/assets/data/weeks/characters/dad.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/data/weeks/characters/gf.xml b/assets/data/weeks/characters/gf.xml index 1af08c30..3ac59136 100644 --- a/assets/data/weeks/characters/gf.xml +++ b/assets/data/weeks/characters/gf.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/data/weeks/characters/mom.xml b/assets/data/weeks/characters/mom.xml index 338e2825..78682a7d 100644 --- a/assets/data/weeks/characters/mom.xml +++ b/assets/data/weeks/characters/mom.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/data/weeks/characters/parents-christmas.xml b/assets/data/weeks/characters/parents-christmas.xml index 44070ced..18f95ffb 100644 --- a/assets/data/weeks/characters/parents-christmas.xml +++ b/assets/data/weeks/characters/parents-christmas.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/data/weeks/characters/pico-player.xml b/assets/data/weeks/characters/pico-player.xml new file mode 100644 index 00000000..709ea008 --- /dev/null +++ b/assets/data/weeks/characters/pico-player.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/assets/data/weeks/characters/pico.xml b/assets/data/weeks/characters/pico.xml index f4f32242..424a0e44 100644 --- a/assets/data/weeks/characters/pico.xml +++ b/assets/data/weeks/characters/pico.xml @@ -1,2 +1,4 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/assets/data/weeks/characters/spooky.xml b/assets/data/weeks/characters/spooky.xml index 2466bbbb..e4955b1f 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/senpai.png b/assets/images/characters/senpai.png index fa5a3293..1c7f8f52 100644 Binary files a/assets/images/characters/senpai.png and b/assets/images/characters/senpai.png differ diff --git a/assets/images/characters/senpai.xml b/assets/images/characters/senpai.xml index d8e532e4..fb01b854 100644 --- a/assets/images/characters/senpai.xml +++ b/assets/images/characters/senpai.xml @@ -1,159 +1,164 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/menus/storymenu/characters/pico.png b/assets/images/menus/storymenu/characters/pico.png index 46a4e375..bae0afa3 100644 Binary files a/assets/images/menus/storymenu/characters/pico.png and b/assets/images/menus/storymenu/characters/pico.png differ diff --git a/assets/images/menus/storymenu/characters/pico.xml b/assets/images/menus/storymenu/characters/pico.xml index 9e6832cd..04cfe8e6 100644 --- a/assets/images/menus/storymenu/characters/pico.xml +++ b/assets/images/menus/storymenu/characters/pico.xml @@ -1,19 +1,46 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/music/pixel/breakfast.mp3 b/assets/music/pixel/breakfast.mp3 new file mode 100644 index 00000000..9868a89f Binary files /dev/null and b/assets/music/pixel/breakfast.mp3 differ diff --git a/assets/music/pixel/breakfast.ogg b/assets/music/pixel/breakfast.ogg new file mode 100644 index 00000000..7227bea6 Binary files /dev/null and b/assets/music/pixel/breakfast.ogg differ diff --git a/assets/shaders/engine/README.txt b/assets/shaders/engine/README.txt deleted file mode 100644 index d5534429..00000000 --- a/assets/shaders/engine/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -THOSE SHADERS ARE USED INTERNALLY BY THE ENGINE - -MODIFY THEM AT YOUR OWN RISK!! IF IT CRASHES IT AINT MY FAULT \ No newline at end of file diff --git a/assets/shaders/engine/editorBlur.frag b/assets/shaders/engine/editorBlur.frag index 59b91c72..85754b99 100644 --- a/assets/shaders/engine/editorBlur.frag +++ b/assets/shaders/engine/editorBlur.frag @@ -3,19 +3,17 @@ // Used for editors -int steps = 16; -int stepsInside = 2; -float strength = 0.0075; +const int steps = 16; +const int stepsInside = 2; +const float strength = 0.0075; + vec4 getColor(vec2 pos) { - vec2 ps = (pos); - ps = clamp(ps, vec2(0.0), 1.0 - (1.0 / openfl_TextureSize.xy)); - return flixel_texture2D(bitmap, (ps)); + pos = clamp(pos, vec2(0.0), vec2(1.0));//1.0 - (1.0 / openfl_TextureSize.xy)); + return flixel_texture2D(bitmap, pos); } + void main() { - // vec2 camPos = getCamPos(openfl_TextureCoordv); vec2 camPos = openfl_TextureCoordv; - if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0) - return; vec4 color = getColor(camPos); float fsteps = float(steps); diff --git a/assets/shaders/engine/editorBlurFast.frag b/assets/shaders/engine/editorBlurFast.frag index 43fa6ed2..db476ae8 100644 --- a/assets/shaders/engine/editorBlurFast.frag +++ b/assets/shaders/engine/editorBlurFast.frag @@ -2,53 +2,29 @@ // Used for editors (faster) -//#define iResolution _camSize -#define getTexture textureCam - -#define iResolution openfl_TextureSize -//#define getTexture flixel_texture2D - -float uBlur = 0.015; -float uBrightness = 0.6; +const float uBlur = 0.015; +const float uBrightness = 0.6; vec4 getColor(vec2 pos) { - vec2 ps = (pos); - ps = clamp(ps, vec2(0.0), vec2(1.0));//1.0 - (1.0 / iResolution.xy)); - return flixel_texture2D(bitmap, (ps)); + pos = clamp(pos, vec2(0.0), vec2(1.0));//1.0 - (1.0 / openfl_TextureSize.xy)); + return flixel_texture2D(bitmap, pos); } - -/*vec2 fixvec2(float x, float y) { // makes an uv the same across sizes - vec2 val = vec2(x, y); - val.xy *= vec2(1280.0, 720.0); - val.xy /= iResolution.xy; - return val; -} -vec2 fixvec2(vec2 uv) { // makes an uv the same across sizes - vec2 val = uv; - val.xy *= vec2(1280.0, 720.0); - val.xy /= iResolution.xy; - return val; -}*/ +uniform sampler2D noiseTexture; +uniform vec2 noiseTextureSize; vec2 random(vec2 p) { - p = vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))); - return fract(sin(p)*4375.5); + p *= openfl_TextureSize; + return texture2D(noiseTexture, p / noiseTextureSize).rg - vec2(0.5); } void main() { - vec2 camPos = (openfl_TextureCoordv); - //vec2 camPos = openfl_TextureCoordv; - if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0) - return; + vec2 camPos = openfl_TextureCoordv; + vec2 blur = vec2(uBlur) * vec2(1.0, openfl_TextureSize.x / openfl_TextureSize.y); - //vec4 color = getColor(camPos); - - vec2 blur = vec2(uBlur) * vec2(1.0, iResolution.x / iResolution.y); - - vec4 a = getColor(camPos+(random(camPos)*blur - blur / 2.0)) * uBrightness; - a += getColor(camPos+(random(camPos+0.1)*blur - blur / 2.0)) * uBrightness; - //a += getColor(camPos+(random(camPos+0.2)*blur - blur / 2.0)) * uBrightness; - //a += getColor(camPos+(random(camPos+0.3)*blur - blur / 2.0)) * uBrightness; - gl_FragColor = a / 2.0; + vec4 a = getColor(camPos+random(camPos)*blur); + a += getColor(camPos+random(camPos+0.1)*blur); + //a += getColor(camPos+random(camPos+0.2)*blur); + //a += getColor(camPos+random(camPos+0.3)*blur); + gl_FragColor = (a / 2.0) * uBrightness; } \ No newline at end of file diff --git a/assets/shaders/noise128.png b/assets/shaders/noise128.png new file mode 100644 index 00000000..682cb32a Binary files /dev/null and b/assets/shaders/noise128.png differ diff --git a/assets/shaders/noise256.png b/assets/shaders/noise256.png new file mode 100644 index 00000000..fd2a1410 Binary files /dev/null and b/assets/shaders/noise256.png differ diff --git a/assets/shaders/noise512.png b/assets/shaders/noise512.png new file mode 100644 index 00000000..d50270af Binary files /dev/null and b/assets/shaders/noise512.png differ diff --git a/assets/songs/guns/cutscene.hx b/assets/songs/guns/cutscene.hx index af4d9be9..f445ad3a 100644 --- a/assets/songs/guns/cutscene.hx +++ b/assets/songs/guns/cutscene.hx @@ -1,7 +1,9 @@ import funkin.system.FunkinSprite; var tankman:FunkinSprite; -var tankTalk1, distorto:FlxSound; +var tankTalk, distorto:FlxSound; +var timers:Array = []; +var destroyDistorto:Bool = true; function create() { FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut}); @@ -28,29 +30,39 @@ function create() { tankman.playAnim('tank'); tankTalk.play(); - new FlxTimer().start(4.1, function(ugly:FlxTimer) + timer(4.1, function() { FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.4}, 0.4, {ease: FlxEase.quadOut}); FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.3}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0.45}); game.gf.playAnim('sad', false, "DANCE"); }); - new FlxTimer().start(11, function(tmr:FlxTimer) - { - game.remove(tankman); - tankman.destroy(); - - game.camHUD.visible = true; - game.dad.visible = true; + timer(11, function() { + destroyDistorto = false; distorto.fadeOut((Conductor.crochet / 1000) * 5, 0); - FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0}); - close(); }); } +function timer(duration:Float, callBack:Void->Void) { + timers.push(new FlxTimer().start(duration, function(timer) { + timers.remove(timer); + callBack(); + })); +} + function focusOn(char) { var camPos = char.getCameraPosition(); game.camFollow.setPosition(camPos.x, camPos.y); camPos.put(); +} + +function destroy() { + game.remove(tankman); + game.camHUD.visible = true; + game.dad.visible = true; + for(timer in timers) timer.cancel(); + for(thing in [tankman, tankTalk]) thing.destroy(); + if(destroyDistorto) distorto.destroy(); + FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0}); } \ No newline at end of file diff --git a/assets/songs/stress/cutscene.hx b/assets/songs/stress/cutscene.hx index dd55a3da..40951864 100644 --- a/assets/songs/stress/cutscene.hx +++ b/assets/songs/stress/cutscene.hx @@ -27,11 +27,6 @@ function create() { stressCutscene.play(); stressCutscene.onComplete = function() { - for(spr in [gf, tankman, pico]) { - spr.destroy(); - game.remove(spr); - } - game.camHUD.visible = game.dad.visible = game.gf.visible = true; close(); }; @@ -59,8 +54,6 @@ function create() { } function update(elapsed) { - if (FlxG.keys.justPressed.F5) - FlxG.resetState(); switch(step) { case 0: lipSync(tankman, 0, 16750); @@ -133,4 +126,14 @@ function focusOn(char, snap:Bool = false) { game.camFollow.setPosition(camPos.x, camPos.y); if(snap) FlxG.camera.snapToTarget(); camPos.put(); +} + +function destroy() { + game.boyfriend.animation.finishCallback = null; + game.camHUD.visible = game.boyfriend.visible = game.dad.visible = game.gf.visible = true; + for(thing in [bf, gf, tankman, pico, stressCutscene]) { + if(thing == null) continue; + if(thing != stressCutscene) game.remove(thing); + thing.destroy(); + } } \ No newline at end of file diff --git a/assets/songs/test/dialogue.hx b/assets/songs/test/dialogue.hx new file mode 100644 index 00000000..5d3e7fa0 --- /dev/null +++ b/assets/songs/test/dialogue.hx @@ -0,0 +1,5 @@ +function next(event) + if (!event.playFirst) trace("-"); + +function postNext(event) + trace(curLine.char + " says: " + curLine.text); \ No newline at end of file diff --git a/assets/songs/ugh/cutscene.hx b/assets/songs/ugh/cutscene.hx index f1167d57..f78c6117 100644 --- a/assets/songs/ugh/cutscene.hx +++ b/assets/songs/ugh/cutscene.hx @@ -2,6 +2,8 @@ import funkin.system.FunkinSprite; var tankman:FunkinSprite; var tankTalk1, tankTalk2, bfBeep, distorto:FlxSound; +var timers:Array = []; +var destroyDistorto:Bool = true; function create() { FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut}); @@ -33,35 +35,46 @@ function create() { tankman.playAnim('1'); tankTalk1.play(); - new FlxTimer().start(3, function(tmr:FlxTimer) { + timer(3, function() { focusOn(game.boyfriend); - new FlxTimer().start(1.5, function(t) { + timer(1.5, function() { game.boyfriend.playAnim("singUP"); bfBeep.play(); - new FlxTimer().start(1.5, function(t) { + timer(1.5, function() { focusOn(game.dad); tankTalk2.play(); tankman.playAnim('2'); - new FlxTimer().start(6.1, function(t) { - game.remove(tankman); - tankman.destroy(); - - game.camHUD.visible = true; - game.dad.visible = true; + timer(6.1, function() { + destroyDistorto = false; distorto.fadeOut((Conductor.crochet / 1000) * 5, 0); - close(); - }, 1); - }, 1); - }, 1); - }, 1); + }); + }); + }); + }); +} + +function timer(duration:Float, callBack:Void->Void) { + timers.push(new FlxTimer().start(duration, function(timer) { + timers.remove(timer); + callBack(); + })); } function focusOn(char) { var camPos = char.getCameraPosition(); game.camFollow.setPosition(camPos.x, camPos.y); camPos.put(); +} + +function destroy() { + game.remove(tankman); + game.camHUD.visible = true; + game.dad.visible = true; + for(timer in timers) timer.cancel(); + for(thing in [tankTalk1, tankTalk2, bfBeep, tankman]) thing.destroy(); + if(destroyDistorto) distorto.destroy(); } \ No newline at end of file diff --git a/commandline/commands/Setup.hx b/commandline/commands/Setup.hx index 87927075..3d85705d 100644 --- a/commandline/commands/Setup.hx +++ b/commandline/commands/Setup.hx @@ -18,11 +18,32 @@ class Setup { FileSystem.deleteDirectory(path); } + public static function addMissingFolders(path:String):String { + #if sys + var folders:Array = path.split("/"); + var currentPath:String = ""; + + for (folder in folders) { + currentPath += folder + "/"; + if (!FileSystem.exists(currentPath)) + FileSystem.createDirectory(currentPath); + } + #end + return path; + } + public static function main(args:Array) { - var args = ArgParser.parse(args, ["s" => "silent-progress", "S" => "silent-progress", "silent" => "silent-progress"]); + var args = ArgParser.parse(args, [ + "s" => "silent-progress", + "S" => "silent-progress", + "silent" => "silent-progress", + "f" => "fast", + "F" => "fast" + ]); var CHECK_VSTUDIO = !args.existsOption("no-vscheck"); var REINSTALL_ALL = args.existsOption("reinstall"); var SILENT = args.existsOption("silent-progress"); + var FAST = args.existsOption("fast"); // TODO: add only install missing libs // to prevent messing with currently installed libs @@ -138,6 +159,11 @@ class Setup { var commandSuffix = " --always"; if (SILENT) commandSuffix += " --quiet"; + function command(cmd:String) { + Sys.println(cmd); // TODO: ansi color + Sys.command(cmd); + } + for(event in events) { switch(event.type) { case INSTALL: @@ -148,10 +174,23 @@ class Setup { switch(lib.type) { case "lib": prettyPrint((lib.global == "true" ? "Globally installing" : "Locally installing") + ' "${lib.name}"...'); - Sys.command('haxelib$commandPrefix install ${lib.name} ${lib.version != null ? " " + lib.version : " "}'); + command('haxelib$commandPrefix install ${lib.name} ${lib.version != null ? " " + lib.version : " "}'); case "git": prettyPrint((lib.global == "true" ? "Globally installing" : "Locally installing") + ' "${lib.name}" from git url "${lib.url}"'); - Sys.command('haxelib$commandPrefix git ${lib.name} ${lib.url}${lib.ref != null ? ' ${lib.ref}' : ''}'); + if(FAST) { + var oldcwd = Sys.getCwd(); + var newCwd = oldcwd + "/.haxelib/" + lib.name + "/"; + var newCwdGit = newCwd + "git/"; + addMissingFolders(newCwdGit); + Sys.setCwd(newCwd); + File.saveContent(".current", "git"); + if(FileSystem.exists(newCwdGit)) recursiveDelete(newCwdGit); + command('git clone ${lib.url} ${lib.name} --depth 1${lib.ref != null ? " --branch " + lib.ref : ""} --progress'); + FileSystem.rename(newCwd + lib.name, newCwdGit); + Sys.setCwd(oldcwd); + } else { + command('haxelib$commandPrefix git ${lib.name} ${lib.url}${lib.ref != null ? ' ${lib.ref}' : ''}'); + } default: prettyPrint('Cannot resolve library of type "${lib.type}"'); } @@ -192,7 +231,7 @@ class Setup { for(line in cmd.lines) { final line = StringTools.replace(line, "$PLATFORM", platform); //Sys.println(line); - Sys.command(line); + command(line); } Sys.setCwd(oldCwd); case PRINT: diff --git a/libs.xml b/libs.xml index b824cbcb..c024b95c 100644 --- a/libs.xml +++ b/libs.xml @@ -20,6 +20,8 @@ + + diff --git a/project.xml b/project.xml index 24538c29..c42c312c 100644 --- a/project.xml +++ b/project.xml @@ -118,9 +118,6 @@ - - - @@ -134,11 +131,14 @@ - + - + + + +
@@ -173,6 +173,8 @@ + + diff --git a/source/StringTools.hx b/source/StringTools.hx index 0b495af1..2eac4b10 100644 --- a/source/StringTools.hx +++ b/source/StringTools.hx @@ -230,6 +230,8 @@ class StringTools { return (cast s).startsWith(start); #elseif lua return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start); + #elseif cpp + return untyped s.__StartsWith(start); #else return (s.length >= start.length && s.indexOf(start, 0) == 0); #end @@ -255,6 +257,8 @@ class StringTools { return (cast s).endsWith(end); #elseif lua return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end); + #elseif cpp + return untyped s.__EndsWith(end); #else var elen = end.length; var slen = s.length; diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index a3a0a5f9..6a1da437 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -8,7 +8,7 @@ import flixel.math.FlxRect; import flixel.system.FlxAssets.FlxGraphicAsset; import flixel.util.typeLimit.OneOfTwo; import flxanimate.animate.FlxAnim.FlxSymbolAnimation; -import funkin.backend.scripting.events.PlayAnimContext; +import funkin.backend.scripting.events.sprite.PlayAnimContext; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.interfaces.IOffsetCompatible; import funkin.backend.utils.XMLUtil.AnimData; @@ -56,6 +56,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public var animEnabled:Bool = true; public var zoomFactorEnabled:Bool = true; + public var globalCurFrame(get, set):Int; + /** * ODD interval -> asynced; EVEN interval -> synced */ @@ -142,7 +144,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public function beatHit(curBeat:Int) { if(!animEnabled) return; - if (beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0) + if (lastAnimContext != LOCK && beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0) { if(skipNegativeBeats && curBeat < 0) return; // TODO: find a solution without countedBeat @@ -392,4 +394,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I return beatInterval = v; } + + @:noCompletion private inline function get_globalCurFrame() { + return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0); + } + @:noCompletion private inline function set_globalCurFrame(val:Int) { + return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val); + } } diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx index eca2968c..604f8d1d 100644 --- a/source/funkin/backend/MusicBeatState.hx +++ b/source/funkin/backend/MusicBeatState.hx @@ -164,10 +164,36 @@ class MusicBeatState extends FlxState implements IBeatReceiver persistentUpdate = true; call("postCreate"); if (!skipTransIn) - openSubState(new MusicBeatTransition(null)); + startTransition(null); skipTransIn = false; skipTransOut = false; } + + public function startTransition(?newState:FlxState, skipSubStates:Bool = false):Bool { + if (!skipSubStates) { + var curCheckingSub:FlxSubState = subState; + var found:FlxSubState = null; // always gets the last one - Nex + + while (curCheckingSub != null) { + if (curCheckingSub is MusicBeatTransition) { + found = null; // avoiding infinite loops - Nex + break; + } + + if (curCheckingSub is MusicBeatSubstate && cast(curCheckingSub, MusicBeatSubstate).canOpenCustomTransition) found = curCheckingSub; + curCheckingSub = curCheckingSub.subState; + } + + if (found != null) { + found.openSubState(new MusicBeatTransition(newState)); + return true; // "Started from a substate?" + } + } + + openSubState(new MusicBeatTransition(newState)); + return false; + } + public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { // calls the function on the assigned script if(stateScripts != null) @@ -232,7 +258,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) - super.openSubState(subState); + super.openSubState(e.substate is FlxSubState ? cast e.substate : subState); } public override function onResize(w:Int, h:Int) { @@ -260,11 +286,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver if (e.cancelled) return false; - if (skipTransOut) + if (skipTransOut || (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null)) return true; - if (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null) - return true; - openSubState(new MusicBeatTransition(nextState)); + startTransition(e.substate); persistentUpdate = false; return false; } diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx index aebafb20..cee1ad5b 100644 --- a/source/funkin/backend/MusicBeatSubstate.hx +++ b/source/funkin/backend/MusicBeatSubstate.hx @@ -16,6 +16,10 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver private var lastBeat:Float = 0; private var lastStep:Float = 0; + /** + * Whether this specifc substate can open custom transitions + */ + public var canOpenCustomTransition:Bool = false; /** * Current step */ @@ -221,7 +225,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) - super.openSubState(subState); + super.openSubState(e.substate is FlxSubState ? cast e.substate : subState); } public override function onResize(w:Int, h:Int) { @@ -239,7 +243,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); if (e.cancelled) return false; - return super.switchTo(nextState); + return super.switchTo(e.substate); } public override function onFocus() { @@ -254,19 +258,14 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver public var parent:FlxState; - public function onSubstateOpen() { - - } + public function onSubstateOpen() {} public override function resetSubState() { + super.resetSubState(); if (subState != null && subState is MusicBeatSubstate) { var subState:MusicBeatSubstate = cast subState; subState.parent = this; - super.resetSubState(); - if (subState != null) - cast(subState, MusicBeatSubstate).onSubstateOpen(); - return; + subState.onSubstateOpen(); } - super.resetSubState(); } } diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 2d17e592..ed673088 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -13,12 +13,6 @@ using StringTools; class Paths { - /** - * Preferred sound extension for the game's audio files. - * Currently is set to `mp3` for web targets, and `ogg` for other targets. - */ - inline public static final SOUND_EXT = #if web "mp3" #else "ogg" #end; - public static var assetsTree:AssetsLibraryList; public static var tempFramesCache:Map = []; @@ -32,8 +26,8 @@ class Paths public static inline function getPath(file:String, ?library:String) return library != null ? '$library:assets/$library/$file' : 'assets/$file'; - public static inline function video(key:String, ?ext:String = "mp4") - return getPath('videos/$key.$ext'); + public static inline function video(key:String, ?ext:String) + return getPath('videos/$key.${ext != null ? ext : Flags.VIDEO_EXT}'); public static inline function ndll(key:String) return getPath('ndlls/$key.ndll'); @@ -65,31 +59,33 @@ class Paths inline static public function ps1(key:String, ?library:String) return getPath('data/$key.ps1', library); - static public function sound(key:String, ?library:String) - return getPath('sounds/$key.$SOUND_EXT', library); + static public function sound(key:String, ?library:String, ?ext:String) + return getPath('sounds/$key.${ext != null ? ext : Flags.SOUND_EXT}', library); inline static public function soundRandom(key:String, min:Int, max:Int, ?library:String) return sound(key + FlxG.random.int(min, max), library); - inline static public function music(key:String, ?library:String) - return getPath('music/$key.$SOUND_EXT', library); + inline static public function music(key:String, ?library:String, ?ext:String) + return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library); - inline static public function voices(song:String, difficulty:String = "normal", ?prefix:String = "") - { + 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()}.$SOUND_EXT', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Voices$prefix.$SOUND_EXT', null); + var diff = getPath('songs/$song/song/Voices$prefix-${difficulty.toLowerCase()}.${ext}', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null); } - inline static public function inst(song:String, difficulty:String = "normal", ?prefix:String = "") - { + 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()}.$SOUND_EXT', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Inst$prefix.$SOUND_EXT', null); + var diff = getPath('songs/$song/song/Inst$prefix-${difficulty.toLowerCase()}.${ext}', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null); } - static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String = "png") - { + static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) { + if (ext == null) ext = Flags.IMAGE_EXT; if (checkForAtlas) { var atlasPath = getPath('images/$key/spritemap.$ext', library); var multiplePath = getPath('images/$key/1.$ext', library); @@ -113,9 +109,9 @@ class Paths return scriptPath; } - static public function chart(song:String, ?difficulty:String = "normal"):String + static public function chart(song:String, ?difficulty:String):String { - difficulty = difficulty.toLowerCase(); + difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY).toLowerCase(); song = song.toLowerCase(); return getPath('songs/$song/charts/$difficulty.json', null); @@ -160,33 +156,33 @@ class Paths return getPath('models/$key.awd'); } - inline static public function getSparrowAtlas(key:String, ?library:String) - return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library)); + inline static public function getSparrowAtlas(key:String, ?library:String, ?ext:String) + return FlxAtlasFrames.fromSparrow(image(key, library, ext), file('images/$key.xml', library)); - inline static public function getSparrowAtlasAlt(key:String) - return FlxAtlasFrames.fromSparrow('$key.png', '$key.xml'); + inline static public function getSparrowAtlasAlt(key:String, ?ext:String) + return FlxAtlasFrames.fromSparrow('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.xml'); - inline static public function getPackerAtlas(key:String, ?library:String) - return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library)); + inline static public function getPackerAtlas(key:String, ?library:String, ?ext:String) + return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library, ext), file('images/$key.txt', library)); - inline static public function getPackerAtlasAlt(key:String) - return FlxAtlasFrames.fromSpriteSheetPacker('$key.png', '$key.txt'); + inline static public function getPackerAtlasAlt(key:String, ?ext:String) + return FlxAtlasFrames.fromSpriteSheetPacker('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.txt'); - inline static public function getAsepriteAtlas(key:String, ?library:String) - return FlxAtlasFrames.fromAseprite(image(key, library), file('images/$key.json', library)); + inline static public function getAsepriteAtlas(key:String, ?library:String, ?ext:String) + return FlxAtlasFrames.fromAseprite(image(key, library, ext), file('images/$key.json', library)); - inline static public function getAsepriteAtlasAlt(key:String) - return FlxAtlasFrames.fromAseprite('$key.png', '$key.json'); + inline static public function getAsepriteAtlasAlt(key:String, ?ext:String) + return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json'); inline static public function getAssetsRoot():String - return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets'; + return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end; /** * Gets frames at specified path. * @param key Path to the frames * @param library (Additional) library to load the frames from. */ - public static function getFrames(key:String, assetsPath:Bool = false, ?library:String) { + public static function getFrames(key:String, assetsPath:Bool = false, ?library:String, ?ext:String = null) { if (tempFramesCache.exists(key)) { var frames = tempFramesCache[key]; if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable) @@ -194,7 +190,7 @@ class Paths else tempFramesCache.remove(key); } - return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true)); + return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext); } @@ -206,10 +202,11 @@ class Paths * @param SkipAtlasCheck Whenever the atlas check should be skipped. * @return FlxFramesCollection Frames */ - static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false):FlxFramesCollection { + static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false, ?Ext:String = null):FlxFramesCollection { var noExt = Path.withoutExtension(path); + var ext = Ext != null ? Ext : Flags.IMAGE_EXT; - if (Assets.exists('$noExt/1.png')) { + if (Assets.exists('$noExt/1.${ext}')) { // MULTIPLE SPRITESHEETS!! var graphic = FlxG.bitmap.add("flixel/images/logo/default.png", false, '$noExt/mult'); @@ -220,18 +217,18 @@ class Paths trace("no frames yet for multiple atlases!!"); var cur = 1; var finalFrames = new MultiFramesCollection(graphic); - while(Assets.exists('$noExt/$cur.png')) { - var spr = loadFrames('$noExt/$cur.png'); + while(Assets.exists('$noExt/$cur.${ext}')) { + var spr = loadFrames('$noExt/$cur.${ext}'); finalFrames.addFrames(spr); cur++; } return finalFrames; } else if (Assets.exists('$noExt.xml')) { - return Paths.getSparrowAtlasAlt(noExt); + return Paths.getSparrowAtlasAlt(noExt, ext); } else if (Assets.exists('$noExt.txt')) { - return Paths.getPackerAtlasAlt(noExt); + return Paths.getPackerAtlasAlt(noExt, ext); } else if (Assets.exists('$noExt.json')) { - return Paths.getAsepriteAtlasAlt(noExt); + return Paths.getAsepriteAtlasAlt(noExt, ext); } var graph:FlxGraphic = FlxG.bitmap.add(path, Unique, Key); diff --git a/source/funkin/backend/chart/BaseGameParser.hx b/source/funkin/backend/chart/BaseGameParser.hx new file mode 100644 index 00000000..84b281aa --- /dev/null +++ b/source/funkin/backend/chart/BaseGameParser.hx @@ -0,0 +1,176 @@ +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 firstTimeChng:SwagTimeChange = data.timeChanges[0]; + result.bpm = firstTimeChng.bpm; + result.beatsPerMeasure = firstTimeChng.b != null ? firstTimeChng.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 de4d9621..48ee9d8a 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -12,11 +12,6 @@ import sys.FileSystem; using StringTools; final class Chart { - /** - * Default background colors for songs without bg color - */ - public inline static var defaultColor:FlxColor = 0xFF9271FD; - public static final version:String = "1.6.0"; public static function loadEventsJson(songName:String) { @@ -32,7 +27,9 @@ final class Chart { return data; } - public static function loadChartMeta(songName:String, difficulty:String = "normal", fromMods:Bool = true) { + public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true) { + if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; + var metaPath = Paths.file('songs/${songName.toLowerCase()}/meta.json'); var metaDiffPath = Paths.file('songs/${songName.toLowerCase()}/meta-${difficulty.toLowerCase()}.json'); @@ -52,19 +49,19 @@ final class Chart { if (data == null) data = { - name: songName, - bpm: 100 + name: songName }; data.setFieldDefault("name", songName); - data.setFieldDefault("beatsPerMeasure", 4); - data.setFieldDefault("stepsPerBeat", 4); + data.setFieldDefault("bpm", Flags.DEFAULT_BPM); + data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE); + data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT); data.setFieldDefault("needsVoices", true); - data.setFieldDefault("icon", "face"); + data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON); data.setFieldDefault("difficulties", []); - data.setFieldDefault("coopAllowed", false); - data.setFieldDefault("opponentModeAllowed", false); + 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(defaultColor)); + data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(Flags.DEFAULT_COLOR)); if (data.difficulties.length <= 0) { data.difficulties = [for(f in Paths.getFolderContent('songs/${songName.toLowerCase()}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)]; @@ -88,7 +85,9 @@ final class Chart { return data; } - public static function parse(songName:String, difficulty:String = "normal"):ChartData { + public static function parse(songName:String, ?difficulty:String):ChartData { + if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; + var chartPath = Paths.chart(songName, difficulty); var base:ChartData = { strumLines: [], @@ -97,8 +96,8 @@ final class Chart { meta: { name: null }, - scrollSpeed: 2, - stage: "stage", + scrollSpeed: Flags.DEFAULT_SCROLL_SPEED, + stage: Flags.DEFAULT_STAGE, codenameChart: true, fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS) }; @@ -202,7 +201,8 @@ final class Chart { * @param saveSettings * @return Filtered chart used for saving. */ - public static function save(songFolderPath:String, chart:ChartData, difficulty:String = "normal", ?saveSettings:ChartSaveSettings):ChartData { + public static function save(songFolderPath:String, chart:ChartData, ?difficulty:String, ?saveSettings:ChartSaveSettings):ChartData { + if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (saveSettings == null) saveSettings = {}; if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true; @@ -220,11 +220,11 @@ final class Chart { var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json'; var metaPath = '${songFolderPath}/meta.json'; - CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null)); + 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 ? "\t" : null)); + CoolUtil.safeSaveFile(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null)); #end return filteredChart; } diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx index 186e15ca..e84540b0 100644 --- a/source/funkin/backend/chart/ChartData.hx +++ b/source/funkin/backend/chart/ChartData.hx @@ -20,7 +20,7 @@ typedef ChartMetaData = { public var ?bpm:Float; public var ?displayName:String; public var ?beatsPerMeasure:Float; - public var ?stepsPerBeat:Float; + public var ?stepsPerBeat:Int; public var ?icon:String; public var ?color:Dynamic; public var ?difficulties:Array; @@ -28,6 +28,7 @@ typedef ChartMetaData = { public var ?opponentModeAllowed:Bool; public var ?customValues:Dynamic; public var ?needsVoices:Bool; + public var ?artist:String; // NOT TO BE EXPORTED public var ?parsedColor:FlxColor; diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx index 0d066a61..e4e60d9b 100644 --- a/source/funkin/backend/chart/FNFLegacyParser.hx +++ b/source/funkin/backend/chart/FNFLegacyParser.hx @@ -45,7 +45,7 @@ class FNFLegacyParser { var camFocusedBF:Bool = false; var altAnims:Bool = false; - var beatsPerMeasure:Float = data.beatsPerMeasure.getDefault(4); + var beatsPerMeasure:Float = data.beatsPerMeasure.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE); var curBPM:Float = data.bpm; var curTime:Float = 0; var curCrochet:Float = ((60 / curBPM) * 1000); @@ -132,10 +132,10 @@ class FNFLegacyParser { if ((swagSection.mustHitSection && strumLine.type == OPPONENT) || (!swagSection.mustHitSection && strumLine.type == PLAYER)) sectionNote[1] += 4; - swagSection.sectionNotes.push(sectionNote); + swagSection.sectionNotes.push(sectionNote); } } - + return {song: base}; } @@ -156,9 +156,9 @@ class FNFLegacyParser { for (strumLine in chart.strumLines) switch (strumLine.type) { case OPPONENT: - if (base.player2 == null) base.player2 = strumLine.characters.getDefault(["dad"])[0]; + if (base.player2 == null) base.player2 = strumLine.characters.getDefault([Flags.DEFAULT_OPPONENT])[0]; case PLAYER: - if (base.player1 == null) base.player1 = strumLine.characters.getDefault(["bf"])[0]; + if (base.player1 == null) base.player1 = strumLine.characters.getDefault([Flags.DEFAULT_CHARACTER])[0]; case ADDITIONAL: // do nothing } @@ -221,7 +221,7 @@ typedef SwagSong = // ADDITIONAL STUFF THAT MAY NOT BE PRESENT IN CHART var ?maxHealth:Float; var ?beatsPerMeasure:Float; - var ?stepsPerBeat:Float; + var ?stepsPerBeat:Int; } typedef SwagSection = diff --git a/source/funkin/backend/chart/PsychParser.hx b/source/funkin/backend/chart/PsychParser.hx index 2d5a8b7b..5fb420d3 100644 --- a/source/funkin/backend/chart/PsychParser.hx +++ b/source/funkin/backend/chart/PsychParser.hx @@ -32,7 +32,7 @@ class PsychParser { for (note in strumLine.notes) { var section:Int = Math.floor(Conductor.getStepForTime(note.time) / Conductor.getMeasureLength()); var swagSection:SwagSection = base.notes[section]; - + if (section >= 0 && section < base.notes.length) { var sectionNote:Array = [ note.time, // TIME @@ -47,7 +47,7 @@ class PsychParser { swagSection.sectionNotes.push(sectionNote); } } - + var groupedEvents:Array> = []; var __last:Array = null; var __lastTime:Float = Math.NaN; @@ -106,7 +106,7 @@ class PsychParser { for (psychEvent in psychEvents) for (i in 1...3) // Turn both vals into strings - if (!(psychEvent[i] is String)) + if (!(psychEvent[i] is String)) psychEvent[i] = Std.string(psychEvent[i]); base.events.push([events[0].time, psychEvents]); diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index a0433ba3..12bf14c0 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -19,89 +19,134 @@ class Script extends FlxBasic implements IFlxDestroyable { public static function getDefaultVariables(?script:Script):Map { return [ // Haxe related stuff - "Std" => Std, - "Math" => Math, - "Reflect" => Reflect, - "StringTools" => StringTools, - "Json" => haxe.Json, + "Std" => Std, + "Math" => Math, + "Reflect" => Reflect, + "StringTools" => StringTools, + "Json" => haxe.Json, // OpenFL & Lime related stuff "Assets" => openfl.utils.Assets, - "Application" => lime.app.Application, + "Application" => lime.app.Application, "Main" => funkin.backend.system.Main, "window" => lime.app.Application.current.window, // Flixel related stuff - "FlxG" => flixel.FlxG, - "FlxSprite" => flixel.FlxSprite, - "FlxBasic" => flixel.FlxBasic, - "FlxCamera" => flixel.FlxCamera, - "state" => flixel.FlxG.state, - "FlxEase" => flixel.tweens.FlxEase, - "FlxTween" => flixel.tweens.FlxTween, - "FlxSound" => flixel.sound.FlxSound, - "FlxAssets" => flixel.system.FlxAssets, - "FlxMath" => flixel.math.FlxMath, - "FlxGroup" => flixel.group.FlxGroup, - "FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup, + "state" => flixel.FlxG.state, + "FlxG" => flixel.FlxG, + "FlxSprite" => flixel.FlxSprite, + "FlxBasic" => flixel.FlxBasic, + "FlxCamera" => flixel.FlxCamera, + "FlxEase" => flixel.tweens.FlxEase, + "FlxTween" => flixel.tweens.FlxTween, + "FlxSound" => flixel.sound.FlxSound, + "FlxAssets" => flixel.system.FlxAssets, + "FlxMath" => flixel.math.FlxMath, + "FlxGroup" => flixel.group.FlxGroup, + "FlxTypedGroup" => flixel.group.FlxGroup.FlxTypedGroup, "FlxSpriteGroup" => flixel.group.FlxSpriteGroup, - "FlxTypeText" => flixel.addons.text.FlxTypeText, - "FlxText" => flixel.text.FlxText, - "FlxTimer" => flixel.util.FlxTimer, - "FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"), - "FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"), - "FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"), + "FlxTypeText" => flixel.addons.text.FlxTypeText, + "FlxText" => flixel.text.FlxText, + "FlxTimer" => flixel.util.FlxTimer, + "FlxPoint" => CoolUtil.getMacroAbstractClass("flixel.math.FlxPoint"), + "FlxAxes" => CoolUtil.getMacroAbstractClass("flixel.util.FlxAxes"), + "FlxColor" => CoolUtil.getMacroAbstractClass("flixel.util.FlxColor"), // Engine related stuff "engine" => { - commit: funkin.backend.system.macros.GitCommitMacro.commitNumber, - hash: funkin.backend.system.macros.GitCommitMacro.commitHash, + commit: Flags.COMMIT_NUMBER, + hash: Flags.COMMIT_HASH, build: 2675, // 2675 being the last build num before it was removed name: "Codename Engine" }, - "ModState" => funkin.backend.scripting.ModState, - "ModSubState" => funkin.backend.scripting.ModSubState, - "PlayState" => funkin.game.PlayState, - "GameOverSubstate" => funkin.game.GameOverSubstate, + "ModState" => funkin.backend.scripting.ModState, + "ModSubState" => funkin.backend.scripting.ModSubState, + "PlayState" => funkin.game.PlayState, + "GameOverSubstate" => funkin.game.GameOverSubstate, "HealthIcon" => funkin.game.HealthIcon, - "HudCamera" => funkin.game.HudCamera, - "Note" => funkin.game.Note, - "Strum" => funkin.game.Strum, - "StrumLine" => funkin.game.StrumLine, - "Character" => funkin.game.Character, - "Boyfriend" => funkin.game.Character, // for compatibility - "PauseSubstate" => funkin.menus.PauseSubState, - "FreeplayState" => funkin.menus.FreeplayState, - "MainMenuState" => funkin.menus.MainMenuState, - "PauseSubState" => funkin.menus.PauseSubState, + "HudCamera" => funkin.game.HudCamera, + "Note" => funkin.game.Note, + "Strum" => funkin.game.Strum, + "StrumLine" => funkin.game.StrumLine, + "Character" => funkin.game.Character, + "Boyfriend" => funkin.game.Character, // for compatibility + "PauseSubstate" => funkin.menus.PauseSubState, + "FreeplayState" => funkin.menus.FreeplayState, + "MainMenuState" => funkin.menus.MainMenuState, + "PauseSubState" => funkin.menus.PauseSubState, "StoryMenuState" => funkin.menus.StoryMenuState, "TitleState" => funkin.menus.TitleState, - "Options" => funkin.options.Options, - "Paths" => funkin.backend.assets.Paths, - "Conductor" => funkin.backend.system.Conductor, - "FunkinShader" => funkin.backend.shaders.FunkinShader, - "CustomShader" => funkin.backend.shaders.CustomShader, + "Options" => funkin.options.Options, + "Paths" => funkin.backend.assets.Paths, + "Conductor" => funkin.backend.system.Conductor, + "FunkinShader" => funkin.backend.shaders.FunkinShader, + "CustomShader" => funkin.backend.shaders.CustomShader, "FunkinText" => funkin.backend.FunkinText, "FlxAnimate" => funkin.backend.FlxAnimate, "FunkinSprite" => funkin.backend.FunkinSprite, - "Alphabet" => funkin.menus.ui.Alphabet, + "Alphabet" => funkin.menus.ui.Alphabet, - "CoolUtil" => funkin.backend.utils.CoolUtil, - "IniUtil" => funkin.backend.utils.IniUtil, - "XMLUtil" => funkin.backend.utils.XMLUtil, - #if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end - "MarkdownUtil" => funkin.backend.utils.MarkdownUtil, + "CoolUtil" => funkin.backend.utils.CoolUtil, + "IniUtil" => funkin.backend.utils.IniUtil, + "XMLUtil" => funkin.backend.utils.XMLUtil, + #if sys "ZipUtil" => funkin.backend.utils.ZipUtil, #end + "MarkdownUtil" => funkin.backend.utils.MarkdownUtil, "EngineUtil" => funkin.backend.utils.EngineUtil, "MemoryUtil" => funkin.backend.utils.MemoryUtil, "BitmapUtil" => funkin.backend.utils.BitmapUtil, ]; } + + /** + * Used internally to keep backwards compatibility with old scripts. + * This gets set on `hscript.Interp.importRedirects`, + * if you wanna modify it, please edit `hscript.Interp.importRedirects` directly. + **/ + public static function getDefaultImportRedirects():Map { + var redirects:Map = [ + "funkin.savedata.FunkinSave" => "funkin.backend.utils.FunkinSave" + ]; + + final events = "funkin.backend.scripting.events."; + redirects[events + "CharacterNodeEvent"] = events + "character.CharacterNodeEvent"; + redirects[events + "CharacterXMLEvent"] = events + "character.CharacterXMLEvent"; + redirects[events + "DanceEvent"] = events + "character.DanceEvent"; + redirects[events + "DirectionAnimEvent"] = events + "character.DirectionAnimEvent"; + redirects[events + "DiscordPresenceUpdateEvent"] = events + "discord.DiscordPresenceUpdateEvent"; + redirects[events + "GameOverCreationEvent"] = events + "gameover.GameOverCreationEvent"; + redirects[events + "CamMoveEvent"] = events + "gameplay.CamMoveEvent"; + redirects[events + "CountdownEvent"] = events + "gameplay.CountdownEvent"; + redirects[events + "EventGameEvent"] = events + "gameplay.EventGameEvent"; + redirects[events + "GameOverEvent"] = events + "gameplay.GameOverEvent"; + redirects[events + "RatingUpdateEvent"] = events + "gameplay.RatingUpdateEvent"; + redirects[events + "HealthIconChangeEvent"] = events + "healthicon.HealthIconChangeEvent"; + redirects[events + "FreeplayAlphaUpdateEvent"] = events + "menu.freeplay.FreeplayAlphaUpdateEvent"; + redirects[events + "FreeplaySongSelectEvent"] = events + "menu.freeplay.FreeplaySongSelectEvent"; + redirects[events + "MenuChangeEvent"] = events + "menu.MenuChangeEvent"; + redirects[events + "PauseCreationEvent"] = events + "menu.pause.PauseCreationEvent"; + redirects[events + "WeekSelectEvent"] = events + "menu.storymenu.WeekSelectEvent"; + redirects[events + "InputSystemEvent"] = events + "note.InputSystemEvent"; + redirects[events + "NoteCreationEvent"] = events + "note.NoteCreationEvent"; + redirects[events + "NoteHitEvent"] = events + "note.NoteHitEvent"; + redirects[events + "NoteMissEvent"] = events + "note.NoteMissEvent"; + redirects[events + "NoteUpdateEvent"] = events + "note.NoteUpdateEvent"; + redirects[events + "SimpleNoteEvent"] = events + "note.SimpleNoteEvent"; + redirects[events + "StrumCreationEvent"] = events + "note.StrumCreationEvent"; + redirects[events + "SplashShowEvent"] = events + "splash.SplashShowEvent"; + redirects[events + "PlayAnimContext"] = events + "sprite.PlayAnimContext"; + redirects[events + "PlayAnimEvent"] = events + "sprite.PlayAnimEvent"; + redirects[events + "StageNodeEvent"] = events + "stage.StageNodeEvent"; + redirects[events + "StageXMLEvent"] = events + "stage.StageXMLEvent"; + + return redirects; + } + public static function getDefaultPreprocessors():Map { var defines = funkin.backend.system.macros.DefinesMacro.defines; defines.set("CODENAME_ENGINE", true); defines.set("CODENAME_VER", Application.current.meta.get('version')); defines.set("CODENAME_BUILD", 2675); // 2675 being the last build num before it was removed - defines.set("CODENAME_COMMIT", funkin.backend.system.macros.GitCommitMacro.commitNumber); + defines.set("CODENAME_COMMIT", Flags.COMMIT_NUMBER); return defines; } /** diff --git a/source/funkin/backend/scripting/events/AmountEvent.hx b/source/funkin/backend/scripting/events/AmountEvent.hx index 31ae18c6..f77ed9ec 100644 --- a/source/funkin/backend/scripting/events/AmountEvent.hx +++ b/source/funkin/backend/scripting/events/AmountEvent.hx @@ -5,9 +5,4 @@ final class AmountEvent extends CancellableEvent { * Amount */ public var amount:Null; - - /** - * Shows whether or not psych users complained about this class - */ - public var psychUsersComplained:Bool = true; } \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DrawEvent.hx b/source/funkin/backend/scripting/events/DrawEvent.hx index 94e1aa8b..74788ecd 100644 --- a/source/funkin/backend/scripting/events/DrawEvent.hx +++ b/source/funkin/backend/scripting/events/DrawEvent.hx @@ -1,4 +1,5 @@ package funkin.backend.scripting.events; final class DrawEvent extends CancellableEvent { + // temp empty for now - Nex } \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/README.md b/source/funkin/backend/scripting/events/README.md new file mode 100644 index 00000000..193eef72 --- /dev/null +++ b/source/funkin/backend/scripting/events/README.md @@ -0,0 +1,7 @@ +## NOTE +Keep in mind that a certain criteria was used to place events in their corresponding folders which is simply what the events themselves are mostly related to. + +For example: + +- `GameOverEvent` inside of the `gameplay` folder instead of the `gameover` folder since it gets called in the `PlayState` class (that owns the `gameplay` folder) instead of the `GameOverSubstate` class itself; +- `NoteHitEvent` gets indeed called by the `PlayState` class but it's also used in the `StrumLine` (For those who didn't really know, Strumlines are the Input Notes) class and since the event's name itself starts with the word `Note` it was more accurate to place it in the `note` folder instead of the `gameplay` folder that `PlayState` owns. \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/StateEvent.hx b/source/funkin/backend/scripting/events/StateEvent.hx index d3f35783..7426f319 100644 --- a/source/funkin/backend/scripting/events/StateEvent.hx +++ b/source/funkin/backend/scripting/events/StateEvent.hx @@ -4,7 +4,7 @@ import flixel.FlxState; final class StateEvent extends CancellableEvent { /** - * Substate that is about to be opened/closed + * Substate or State that is about to be opened/closed */ - public var substate:FlxState; + public var substate:FlxState; // WHY is it named substate :sob: - Nex } \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/CharacterNodeEvent.hx b/source/funkin/backend/scripting/events/character/CharacterNodeEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/CharacterNodeEvent.hx rename to source/funkin/backend/scripting/events/character/CharacterNodeEvent.hx index 9577a64f..85eb46c8 100644 --- a/source/funkin/backend/scripting/events/CharacterNodeEvent.hx +++ b/source/funkin/backend/scripting/events/character/CharacterNodeEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.character; import funkin.game.Character; import haxe.xml.Access; diff --git a/source/funkin/backend/scripting/events/CharacterXMLEvent.hx b/source/funkin/backend/scripting/events/character/CharacterXMLEvent.hx similarity index 70% rename from source/funkin/backend/scripting/events/CharacterXMLEvent.hx rename to source/funkin/backend/scripting/events/character/CharacterXMLEvent.hx index e4f107fd..7375bafd 100644 --- a/source/funkin/backend/scripting/events/CharacterXMLEvent.hx +++ b/source/funkin/backend/scripting/events/character/CharacterXMLEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.character; import funkin.game.Character; import haxe.xml.Access; @@ -7,7 +7,7 @@ final class CharacterXMLEvent extends CancellableEvent { /** * The character instance */ - public var character:Character; + public var character:Character; /** * The xml diff --git a/source/funkin/backend/scripting/events/DanceEvent.hx b/source/funkin/backend/scripting/events/character/DanceEvent.hx similarity index 60% rename from source/funkin/backend/scripting/events/DanceEvent.hx rename to source/funkin/backend/scripting/events/character/DanceEvent.hx index b2300f8f..4f7f7154 100644 --- a/source/funkin/backend/scripting/events/DanceEvent.hx +++ b/source/funkin/backend/scripting/events/character/DanceEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.character; final class DanceEvent extends CancellableEvent { public var danced:Bool; diff --git a/source/funkin/backend/scripting/events/DirectionAnimEvent.hx b/source/funkin/backend/scripting/events/character/DirectionAnimEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/DirectionAnimEvent.hx rename to source/funkin/backend/scripting/events/character/DirectionAnimEvent.hx index dce2eaff..2bc027fa 100644 --- a/source/funkin/backend/scripting/events/DirectionAnimEvent.hx +++ b/source/funkin/backend/scripting/events/character/DirectionAnimEvent.hx @@ -1,4 +1,6 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.character; + +import funkin.backend.scripting.events.sprite.PlayAnimContext; final class DirectionAnimEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx new file mode 100644 index 00000000..7a5e3eac --- /dev/null +++ b/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx @@ -0,0 +1,14 @@ +package funkin.backend.scripting.events.dialogue; + +/** + * CANCEL this event to prevent continuing with the next dialogue. + * + * **NOTE**: To access the current, past or next lines, use `curLine`, `lastLine` or also `dialogueLines` in the `DialogueCutscene` class (they're globals and can be accessed at any time as long as the Dialogue Cutscene is active)! + */ +final class DialogueNextLineEvent extends CancellableEvent +{ + /** + * If the dialogue has just been opened. + */ + public var playFirst:Bool = false; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx b/source/funkin/backend/scripting/events/discord/DiscordPresenceUpdateEvent.hx similarity index 81% rename from source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx rename to source/funkin/backend/scripting/events/discord/DiscordPresenceUpdateEvent.hx index c17cbb68..19884e42 100644 --- a/source/funkin/backend/scripting/events/DiscordPresenceUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/discord/DiscordPresenceUpdateEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.discord; final class DiscordPresenceUpdateEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/GameOverCreationEvent.hx b/source/funkin/backend/scripting/events/gameover/GameOverCreationEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/GameOverCreationEvent.hx rename to source/funkin/backend/scripting/events/gameover/GameOverCreationEvent.hx index bb9b04c0..39762aeb 100644 --- a/source/funkin/backend/scripting/events/GameOverCreationEvent.hx +++ b/source/funkin/backend/scripting/events/gameover/GameOverCreationEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameover; /** * CANCEL this event to prevent default behaviour! diff --git a/source/funkin/backend/scripting/events/CamMoveEvent.hx b/source/funkin/backend/scripting/events/gameplay/CamMoveEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/CamMoveEvent.hx rename to source/funkin/backend/scripting/events/gameplay/CamMoveEvent.hx index 3dcbda78..06792c48 100644 --- a/source/funkin/backend/scripting/events/CamMoveEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/CamMoveEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameplay; import flixel.math.FlxPoint; import funkin.game.StrumLine; diff --git a/source/funkin/backend/scripting/events/CountdownEvent.hx b/source/funkin/backend/scripting/events/gameplay/CountdownEvent.hx similarity index 95% rename from source/funkin/backend/scripting/events/CountdownEvent.hx rename to source/funkin/backend/scripting/events/gameplay/CountdownEvent.hx index 7c6b4308..a86d8bae 100644 --- a/source/funkin/backend/scripting/events/CountdownEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/CountdownEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameplay; import flixel.sound.FlxSound; import flixel.tweens.FlxTween; diff --git a/source/funkin/backend/scripting/events/EventGameEvent.hx b/source/funkin/backend/scripting/events/gameplay/EventGameEvent.hx similarity index 71% rename from source/funkin/backend/scripting/events/EventGameEvent.hx rename to source/funkin/backend/scripting/events/gameplay/EventGameEvent.hx index 836e44c9..2e063d90 100644 --- a/source/funkin/backend/scripting/events/EventGameEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/EventGameEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameplay; import funkin.backend.chart.ChartData; diff --git a/source/funkin/backend/scripting/events/GameOverEvent.hx b/source/funkin/backend/scripting/events/gameplay/GameOverEvent.hx similarity index 94% rename from source/funkin/backend/scripting/events/GameOverEvent.hx rename to source/funkin/backend/scripting/events/gameplay/GameOverEvent.hx index b0e83795..28521f5b 100644 --- a/source/funkin/backend/scripting/events/GameOverEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/GameOverEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameplay; import funkin.game.Character; diff --git a/source/funkin/backend/scripting/events/RatingUpdateEvent.hx b/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx similarity index 78% rename from source/funkin/backend/scripting/events/RatingUpdateEvent.hx rename to source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx index 96ad7e7e..59a3305d 100644 --- a/source/funkin/backend/scripting/events/RatingUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/gameplay/RatingUpdateEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.gameplay; final class RatingUpdateEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/HealthIconChangeEvent.hx b/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx similarity index 78% rename from source/funkin/backend/scripting/events/HealthIconChangeEvent.hx rename to source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx index a98493ef..dbb4cb68 100644 --- a/source/funkin/backend/scripting/events/HealthIconChangeEvent.hx +++ b/source/funkin/backend/scripting/events/healthicon/HealthIconChangeEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.healthicon; final class HealthIconChangeEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/MenuChangeEvent.hx b/source/funkin/backend/scripting/events/menu/MenuChangeEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/MenuChangeEvent.hx rename to source/funkin/backend/scripting/events/menu/MenuChangeEvent.hx index d74ef2a4..90b03dcf 100644 --- a/source/funkin/backend/scripting/events/MenuChangeEvent.hx +++ b/source/funkin/backend/scripting/events/menu/MenuChangeEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.menu; final class MenuChangeEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/FreeplayAlphaUpdateEvent.hx b/source/funkin/backend/scripting/events/menu/freeplay/FreeplayAlphaUpdateEvent.hx similarity index 90% rename from source/funkin/backend/scripting/events/FreeplayAlphaUpdateEvent.hx rename to source/funkin/backend/scripting/events/menu/freeplay/FreeplayAlphaUpdateEvent.hx index 639645c2..d8ab6f0b 100644 --- a/source/funkin/backend/scripting/events/FreeplayAlphaUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/menu/freeplay/FreeplayAlphaUpdateEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.menu.freeplay; final class FreeplayAlphaUpdateEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/FreeplaySongSelectEvent.hx b/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx similarity index 86% rename from source/funkin/backend/scripting/events/FreeplaySongSelectEvent.hx rename to source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx index b97847b0..e2ee1fc7 100644 --- a/source/funkin/backend/scripting/events/FreeplaySongSelectEvent.hx +++ b/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.menu.freeplay; final class FreeplaySongSelectEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/PauseCreationEvent.hx b/source/funkin/backend/scripting/events/menu/pause/PauseCreationEvent.hx similarity index 83% rename from source/funkin/backend/scripting/events/PauseCreationEvent.hx rename to source/funkin/backend/scripting/events/menu/pause/PauseCreationEvent.hx index a02b0e1a..290ac847 100644 --- a/source/funkin/backend/scripting/events/PauseCreationEvent.hx +++ b/source/funkin/backend/scripting/events/menu/pause/PauseCreationEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.menu.pause; /** * CANCEL this event to prevent default behaviour! diff --git a/source/funkin/backend/scripting/events/WeekSelectEvent.hx b/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx similarity index 89% rename from source/funkin/backend/scripting/events/WeekSelectEvent.hx rename to source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx index e255d361..c9afe956 100644 --- a/source/funkin/backend/scripting/events/WeekSelectEvent.hx +++ b/source/funkin/backend/scripting/events/menu/storymenu/WeekSelectEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.menu.storymenu; import funkin.menus.StoryMenuState.WeekData; diff --git a/source/funkin/backend/scripting/events/InputSystemEvent.hx b/source/funkin/backend/scripting/events/note/InputSystemEvent.hx similarity index 95% rename from source/funkin/backend/scripting/events/InputSystemEvent.hx rename to source/funkin/backend/scripting/events/note/InputSystemEvent.hx index 3855c31f..06ea39a4 100644 --- a/source/funkin/backend/scripting/events/InputSystemEvent.hx +++ b/source/funkin/backend/scripting/events/note/InputSystemEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.StrumLine; diff --git a/source/funkin/backend/scripting/events/NoteCreationEvent.hx b/source/funkin/backend/scripting/events/note/NoteCreationEvent.hx similarity index 94% rename from source/funkin/backend/scripting/events/NoteCreationEvent.hx rename to source/funkin/backend/scripting/events/note/NoteCreationEvent.hx index 3f7d1890..f070099f 100644 --- a/source/funkin/backend/scripting/events/NoteCreationEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteCreationEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Note; diff --git a/source/funkin/backend/scripting/events/NoteHitEvent.hx b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx similarity index 87% rename from source/funkin/backend/scripting/events/NoteHitEvent.hx rename to source/funkin/backend/scripting/events/note/NoteHitEvent.hx index 4bac078e..4c4a2053 100644 --- a/source/funkin/backend/scripting/events/NoteHitEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteHitEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Character; import funkin.game.Note; @@ -24,9 +24,19 @@ final class NoteHitEvent extends CancellableEvent { */ public var countScore:Bool = true; /** - * Whenever ratings should be shown or not. + * Whenever ratings (Rating sprite, Digits sprites and Combo sprite) should be shown or not. + * + * NOTE: Whether it's `true` use `displayRating` and `displayCombo` (plus `minDigitDisplay` in the PlayState class) to change what's going to pop up! */ public var showRating:Null = null; + /** + * Whenever the Rating sprites should be shown or not. + */ + public var displayRating:Bool; + /** + * Whenever the Combo sprite should be shown or not (like old Week 7 patches). + */ + public var displayCombo:Bool; /** * Note that has been pressed */ @@ -123,6 +133,13 @@ final class NoteHitEvent extends CancellableEvent { @:dox(hide) public function cancelDeletion() {preventDeletion();} + /** + * Forces the note to be deleted. + **/ + public function forceDeletion() { + deleteNote = true; + } + /** * Prevents the vocals volume from being set to 1 after pressing the note. */ diff --git a/source/funkin/backend/scripting/events/NoteMissEvent.hx b/source/funkin/backend/scripting/events/note/NoteMissEvent.hx similarity index 98% rename from source/funkin/backend/scripting/events/NoteMissEvent.hx rename to source/funkin/backend/scripting/events/note/NoteMissEvent.hx index 72f87ddd..9499d612 100644 --- a/source/funkin/backend/scripting/events/NoteMissEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteMissEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Character; import funkin.game.Note; diff --git a/source/funkin/backend/scripting/events/NoteUpdateEvent.hx b/source/funkin/backend/scripting/events/note/NoteUpdateEvent.hx similarity index 95% rename from source/funkin/backend/scripting/events/NoteUpdateEvent.hx rename to source/funkin/backend/scripting/events/note/NoteUpdateEvent.hx index 45676ec5..d004fa0e 100644 --- a/source/funkin/backend/scripting/events/NoteUpdateEvent.hx +++ b/source/funkin/backend/scripting/events/note/NoteUpdateEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Note; import funkin.game.Strum; diff --git a/source/funkin/backend/scripting/events/SimpleNoteEvent.hx b/source/funkin/backend/scripting/events/note/SimpleNoteEvent.hx similarity index 75% rename from source/funkin/backend/scripting/events/SimpleNoteEvent.hx rename to source/funkin/backend/scripting/events/note/SimpleNoteEvent.hx index 3899958b..9d031fcf 100644 --- a/source/funkin/backend/scripting/events/SimpleNoteEvent.hx +++ b/source/funkin/backend/scripting/events/note/SimpleNoteEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Note; diff --git a/source/funkin/backend/scripting/events/StrumCreationEvent.hx b/source/funkin/backend/scripting/events/note/StrumCreationEvent.hx similarity index 94% rename from source/funkin/backend/scripting/events/StrumCreationEvent.hx rename to source/funkin/backend/scripting/events/note/StrumCreationEvent.hx index d591d958..517075c3 100644 --- a/source/funkin/backend/scripting/events/StrumCreationEvent.hx +++ b/source/funkin/backend/scripting/events/note/StrumCreationEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.note; import funkin.game.Strum; diff --git a/source/funkin/backend/scripting/events/shader/ShaderProcessEvent.hx b/source/funkin/backend/scripting/events/shader/ShaderProcessEvent.hx new file mode 100644 index 00000000..e8c6b746 --- /dev/null +++ b/source/funkin/backend/scripting/events/shader/ShaderProcessEvent.hx @@ -0,0 +1,8 @@ +package funkin.backend.scripting.events.shader; + +final class ShaderProcessEvent extends CancellableEvent +{ + public var source:String; + + public var storageType:String; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/soundtray/SoundTrayShowEvent.hx b/source/funkin/backend/scripting/events/soundtray/SoundTrayShowEvent.hx new file mode 100644 index 00000000..7d9b29bd --- /dev/null +++ b/source/funkin/backend/scripting/events/soundtray/SoundTrayShowEvent.hx @@ -0,0 +1,6 @@ +package funkin.backend.scripting.events.soundtray; + +final class SoundTrayShowEvent extends CancellableEvent +{ + public var up:Bool; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/soundtray/SoundTrayTextEvent.hx b/source/funkin/backend/scripting/events/soundtray/SoundTrayTextEvent.hx new file mode 100644 index 00000000..1010df06 --- /dev/null +++ b/source/funkin/backend/scripting/events/soundtray/SoundTrayTextEvent.hx @@ -0,0 +1,12 @@ +package funkin.backend.scripting.events.soundtray; + +final class SoundTrayTextEvent extends CancellableEvent +{ + public var checkIfNull:Bool; + + public var reloadDefaultTextFormat:Bool; + + public var displayTxt:String; + + public var y:Float; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/SplashShowEvent.hx b/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx similarity index 89% rename from source/funkin/backend/scripting/events/SplashShowEvent.hx rename to source/funkin/backend/scripting/events/splash/SplashShowEvent.hx index 46504d44..6cdea0ee 100644 --- a/source/funkin/backend/scripting/events/SplashShowEvent.hx +++ b/source/funkin/backend/scripting/events/splash/SplashShowEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.splash; import funkin.game.SplashGroup; import funkin.game.Strum; diff --git a/source/funkin/backend/scripting/events/PlayAnimContext.hx b/source/funkin/backend/scripting/events/sprite/PlayAnimContext.hx similarity index 94% rename from source/funkin/backend/scripting/events/PlayAnimContext.hx rename to source/funkin/backend/scripting/events/sprite/PlayAnimContext.hx index f3c9e211..67f8c857 100644 --- a/source/funkin/backend/scripting/events/PlayAnimContext.hx +++ b/source/funkin/backend/scripting/events/sprite/PlayAnimContext.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.sprite; /** Contains all contexts possible for `PlayAnimEvent`. diff --git a/source/funkin/backend/scripting/events/PlayAnimEvent.hx b/source/funkin/backend/scripting/events/sprite/PlayAnimEvent.hx similarity index 92% rename from source/funkin/backend/scripting/events/PlayAnimEvent.hx rename to source/funkin/backend/scripting/events/sprite/PlayAnimEvent.hx index f51c8243..0db69a2b 100644 --- a/source/funkin/backend/scripting/events/PlayAnimEvent.hx +++ b/source/funkin/backend/scripting/events/sprite/PlayAnimEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.sprite; final class PlayAnimEvent extends CancellableEvent { /** diff --git a/source/funkin/backend/scripting/events/StageNodeEvent.hx b/source/funkin/backend/scripting/events/stage/StageNodeEvent.hx similarity index 89% rename from source/funkin/backend/scripting/events/StageNodeEvent.hx rename to source/funkin/backend/scripting/events/stage/StageNodeEvent.hx index 93465fa6..505e92b8 100644 --- a/source/funkin/backend/scripting/events/StageNodeEvent.hx +++ b/source/funkin/backend/scripting/events/stage/StageNodeEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.stage; import funkin.game.Stage; import haxe.xml.Access; diff --git a/source/funkin/backend/scripting/events/StageXMLEvent.hx b/source/funkin/backend/scripting/events/stage/StageXMLEvent.hx similarity index 85% rename from source/funkin/backend/scripting/events/StageXMLEvent.hx rename to source/funkin/backend/scripting/events/stage/StageXMLEvent.hx index 16693d33..9bc6f10b 100644 --- a/source/funkin/backend/scripting/events/StageXMLEvent.hx +++ b/source/funkin/backend/scripting/events/stage/StageXMLEvent.hx @@ -1,4 +1,4 @@ -package funkin.backend.scripting.events; +package funkin.backend.scripting.events.stage; import funkin.game.Stage; import haxe.xml.Access; diff --git a/source/funkin/backend/shaders/CustomShader.hx b/source/funkin/backend/shaders/CustomShader.hx index 93d480c6..0166e08e 100644 --- a/source/funkin/backend/shaders/CustomShader.hx +++ b/source/funkin/backend/shaders/CustomShader.hx @@ -25,6 +25,10 @@ class CustomShader extends FunkinShader { var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null; var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null; + fileName = name; + fragFileName = fragShaderPath; + vertFileName = vertShaderPath; + path = fragShaderPath+vertShaderPath; if (fragCode == null && vertCode == null) diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx index 0b282ffb..b7c1be14 100644 --- a/source/funkin/backend/shaders/FunkinShader.hx +++ b/source/funkin/backend/shaders/FunkinShader.hx @@ -1,15 +1,17 @@ package funkin.backend.shaders; +import flixel.system.FlxAssets.FlxShader; +import flixel.util.FlxSignal.FlxTypedSignal; import haxe.Exception; import hscript.IHScriptCustomBehaviour; -import flixel.system.FlxAssets.FlxShader; - import lime.utils.Float32Array; - -import openfl.display.ShaderParameterType; -import openfl.display.ShaderParameter; import openfl.display.BitmapData; import openfl.display.ShaderInput; +import openfl.display.ShaderParameter; +import openfl.display.ShaderParameterType; +import openfl.display3D._internal.GLProgram; +import openfl.display3D._internal.GLShader; +import openfl.utils._internal.Log; using StringTools; @:access(openfl.display3D.Context3D) @@ -18,8 +20,20 @@ using StringTools; @:access(openfl.display.ShaderParameter) class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { private static var __instanceFields = Type.getInstanceFields(FunkinShader); + private static var FRAGMENT_SHADER = 0; + private static var VERTEX_SHADER = 1; + + public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); + public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>(); public var glslVer:String = "120"; + public var fileName:String = "FunkinShader"; + public var fragFileName:String = "FunkinShader"; + public var vertFileName:String = "FunkinShader"; + + public var shaderPrefix:String = ""; + public var fragmentPrefix:String = ""; + public var vertexPrefix:String = ""; /** * Creates a new shader from the specified fragment and vertex source. @@ -38,11 +52,171 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { super(); } + static var IMPORT_REGEX = ~/#import\s+<(.*)>/; + + private function processImports(value:String, type:Int):String + { + while(IMPORT_REGEX.match(value)) + { + var importPath = IMPORT_REGEX.matched(1); + var importSource = Assets.getText("assets/shaders/" + importPath); + if(importSource == null) { + var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName; + Logs.traceColored([ + Logs.logText('[Shader] ', RED), + Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED), + ]); + } else { + value = value.replace(IMPORT_REGEX.matched(0), importSource); + } + } + return value; + } + + static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g; + static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g; + static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g; + @:noCompletion private override function __createGLShader(source:String, type:Int):GLShader + { + var gl = __context.gl; + + var shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + var shaderInfoLog = gl.getShaderInfoLog(shader); + var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != ""; + var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + + if (hasInfoLog || compileStatus == 0) + { + var isVertexShader = type == gl.VERTEX_SHADER; + var messageBuf = new StringBuf(); + messageBuf.add((compileStatus == 0) ? "Error" : "Info"); + if(isVertexShader) { + messageBuf.add(" compiling vertex shader"); + if(vertFileName != null && vertFileName.length > 0) { + messageBuf.add(" (" + vertFileName + ")"); + } + } else { + messageBuf.add(" compiling fragment shader"); + if(fragFileName != null && fragFileName.length > 0) { + messageBuf.add(" (" + fragFileName + ")"); + } + } + messageBuf.add("\n"); + var errorPositions = []; + var regex = null; + var tmp = shaderInfoLog; + if(shaderInfoLog.contains(" : error ")) { + regex = ERROR_REGEX_2; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4))); + tmp = regex.matchedRight(); + } + } else if(shaderInfoLog.contains("ERROR: ")) { + regex = ERROR_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } else { + regex = ERROR_POS_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } + var splitSource = source.split("\n"); + for(error in errorPositions) { + messageBuf.add("ERROR: Line: " + error.line); + if(error.column > 0) { + messageBuf.add(", Column: " + error.column); + } + messageBuf.add(", " + error.message); + if(error.line < splitSource.length) { + messageBuf.add("\nLine: "); + messageBuf.add(splitSource[error.line-1].trim()); + } + messageBuf.add("\n\n"); + } + var hasErrorPosition = errorPositions.length > 0; + if(hasErrorPosition) { + messageBuf.add("Raw shader info log:\n"); + } + messageBuf.add(shaderInfoLog); + messageBuf.add("\n"); + messageBuf.add(source); + + var message = messageBuf.toString(); + if (compileStatus == 0) Log.error(message); + else if (hasInfoLog) Log.debug(message); + } + + return shader; + } + + @:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram + { + var program:GLProgram = null; + try + { + var gl = __context.gl; + + var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER); + var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER); + + program = gl.createProgram(); + + // Fix support for drivers that don't draw if attribute 0 is disabled + for (param in __paramFloat) + { + if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_")) + { + gl.bindAttribLocation(program, 0, param.name); + break; + } + } + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0) + { + var messageBuf = new StringBuf(); + messageBuf.add("Unable to initialize the shader program"); + messageBuf.add("\n"); + messageBuf.add(gl.getProgramInfoLog(program)); + var message = messageBuf.toString(); + Log.error(message); + } + } + catch (error:Dynamic) + { + Logs.traceColored([ + Logs.logText('[Shader] ', BLUE), + Logs.logText('Failed to compile shader ${fileName}: ', RED), + Logs.logText(Std.string(error)) + ], TRACE); + } + return program; + + return program; + } + + var glRawFragmentSource:String; + var glRawVertexSource:String; + @:noCompletion override private function set_glFragmentSource(value:String):String { if(value == null) value = ShaderTemplates.defaultFragmentSource; + glRawFragmentSource = value; value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody); + value = processImports(value, FRAGMENT_SHADER); if (value != __glFragmentSource) { __glSourceDirty = true; @@ -55,7 +229,9 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { if(value == null) value = ShaderTemplates.defaultVertexSource; + glRawVertexSource = value; value = value.replace("#pragma header", ShaderTemplates.vertHeader).replace("#pragma body", ShaderTemplates.vertBody); + value = processImports(value, VERTEX_SHADER); if (value != __glVertexSource) { __glSourceDirty = true; @@ -64,6 +240,11 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { return __glVertexSource = value; } + override function __updateGL():Void { + onGLUpdate.dispatch(); + super.__updateGL(); + } + @:noCompletion private override function __initGL():Void { if (__glSourceDirty || __paramBool == null) @@ -83,23 +264,28 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { if (__context != null && program == null) { - var prefix = '#version ${glslVer}\n'; + var prefixBuf = new StringBuf(); + prefixBuf.add('#version ${glslVer}\n'); + prefixBuf.add(shaderPrefix); var gl = __context.gl; - prefix += "#ifdef GL_ES - " - + (precisionHint == FULL ? "#ifdef GL_FRAGMENT_PRECISION_HIGH - precision highp float; - #else - precision mediump float; - #endif" : "precision lowp float;") - + " - #endif - "; + prefixBuf.add("#ifdef GL_ES\n"); + if (precisionHint == FULL) { + prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n"); + prefixBuf.add("precision highp float;\n"); + prefixBuf.add("#else\n"); + prefixBuf.add("precision mediump float;\n"); + prefixBuf.add("#endif\n"); + } else { + prefixBuf.add("precision lowp float;\n"); + } + prefixBuf.add("#endif\n"); - var vertex = prefix + glVertexSource; - var fragment = prefix + glFragmentSource; + var prefix = prefixBuf.toString(); + + var vertex = prefix + vertexPrefix + glVertexSource; + var fragment = prefix + fragmentPrefix + glFragmentSource; var id = vertex + fragment; @@ -110,9 +296,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { else { program = __context.createProgram(GLSL); - program.__glProgram = __createGLProgram(vertex, fragment); - __context.__programs.set(id, program); } @@ -120,56 +304,35 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { glProgram = program.__glProgram; - for (input in __inputBitmapData) - { + for (input in __inputBitmapData) { - if (input.__isUniform) - { + if (input.__isUniform) { input.index = gl.getUniformLocation(glProgram, input.name); - } - else - { + } else { input.index = gl.getAttribLocation(glProgram, input.name); } } - for (parameter in __paramBool) - { - - if (parameter.__isUniform) - { + for (parameter in __paramBool) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramFloat) - { - - if (parameter.__isUniform) - { + for (parameter in __paramFloat) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramInt) - { - - if (parameter.__isUniform) - { - + for (parameter in __paramInt) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { - + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } @@ -178,8 +341,12 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { } } + var __cancelNextProcessGLData:Bool = false; @:noCompletion private override function __processGLData(source:String, storageType:String):Void { + onProcessGLData.dispatch(source, storageType); + if (__cancelNextProcessGLData != (__cancelNextProcessGLData = false)) + return; var lastMatch = 0, position, regex, name, type; if (storageType == "uniform") @@ -202,160 +369,146 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { } var isUniform = (storageType == "uniform"); - - if (StringTools.startsWith(type, "sampler")) - { - var input = new ShaderInput(); - input.name = name; - @:privateAccess - input.__isUniform = isUniform; - __inputBitmapData.push(input); - - switch (name) - { - case "openfl_Texture": - __texture = input; - case "bitmap": - __bitmap = input; - default: - } - - Reflect.setField(__data, name, input); - try{Reflect.setField(this, name, input);} catch(e) {} - } - else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null) - { - var parameterType:ShaderParameterType = switch (type) - { - case "bool": BOOL; - case "double", "float": FLOAT; - case "int", "uint": INT; - case "bvec2": BOOL2; - case "bvec3": BOOL3; - case "bvec4": BOOL4; - case "ivec2", "uvec2": INT2; - case "ivec3", "uvec3": INT3; - case "ivec4", "uvec4": INT4; - case "vec2", "dvec2": FLOAT2; - case "vec3", "dvec3": FLOAT3; - case "vec4", "dvec4": FLOAT4; - case "mat2", "mat2x2": MATRIX2X2; - case "mat2x3": MATRIX2X3; - case "mat2x4": MATRIX2X4; - case "mat3x2": MATRIX3X2; - case "mat3", "mat3x3": MATRIX3X3; - case "mat3x4": MATRIX3X4; - case "mat4x2": MATRIX4X2; - case "mat4x3": MATRIX4X3; - case "mat4", "mat4x4": MATRIX4X4; - default: null; - } - - var length = switch (parameterType) - { - case BOOL2, INT2, FLOAT2: 2; - case BOOL3, INT3, FLOAT3: 3; - case BOOL4, INT4, FLOAT4, MATRIX2X2: 4; - case MATRIX3X3: 9; - case MATRIX4X4: 16; - default: 1; - } - - var arrayLength = switch (parameterType) - { - case MATRIX2X2: 2; - case MATRIX3X3: 3; - case MATRIX4X4: 4; - default: 1; - } - - switch (parameterType) - { - case BOOL, BOOL2, BOOL3, BOOL4: - var parameter = new ShaderParameter(); - parameter.name = name; - @:privateAccess - parameter.type = parameterType; - @:privateAccess - parameter.__arrayLength = arrayLength; - @:privateAccess - parameter.__isBool = true; - @:privateAccess - parameter.__isUniform = isUniform; - @:privateAccess - parameter.__length = length; - __paramBool.push(parameter); - - if (name == "openfl_HasColorTransform") - { - __hasColorTransform = parameter; - } - - Reflect.setField(__data, name, parameter); - try{Reflect.setField(this, name, parameter);} catch(e) {} - - case INT, INT2, INT3, INT4: - var parameter = new ShaderParameter(); - parameter.name = name; - @:privateAccess - parameter.type = parameterType; - @:privateAccess - parameter.__arrayLength = arrayLength; - @:privateAccess - parameter.__isInt = true; - @:privateAccess - parameter.__isUniform = isUniform; - @:privateAccess - parameter.__length = length; - @:privateAccess - __paramInt.push(parameter); - Reflect.setField(__data, name, parameter); - try{Reflect.setField(this, name, parameter);} catch(e) {} - - default: - var parameter = new ShaderParameter(); - parameter.name = name; - @:privateAccess - parameter.type = parameterType; - @:privateAccess - parameter.__arrayLength = arrayLength; - #if lime - @:privateAccess - if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength); - #end - @:privateAccess - parameter.__isFloat = true; - @:privateAccess - parameter.__isUniform = isUniform; - @:privateAccess - parameter.__length = length; - __paramFloat.push(parameter); - - if (StringTools.startsWith(name, "openfl_")) - { - switch (name) - { - case "openfl_Alpha": __alpha = parameter; - case "openfl_ColorMultiplier": __colorMultiplier = parameter; - case "openfl_ColorOffset": __colorOffset = parameter; - case "openfl_Matrix": __matrix = parameter; - case "openfl_Position": __position = parameter; - case "openfl_TextureCoord": __textureCoord = parameter; - case "openfl_TextureSize": __textureSize = parameter; - default: - } - } - - Reflect.setField(__data, name, parameter); - try{Reflect.setField(this, name, parameter);} catch(e) {} - } - } + registerParameter(name, type, isUniform); position = regex.matchedPos(); lastMatch = position.pos + position.len; } } + function registerParameter(name:String, type:String, isUniform:Bool):Void + { + if (StringTools.startsWith(type, "sampler")) + { + var input = new ShaderInput(); + input.name = name; + input.__isUniform = isUniform; + __inputBitmapData.push(input); + + switch (name) + { + case "openfl_Texture": + __texture = input; + case "bitmap": + __bitmap = input; + default: + } + + Reflect.setField(__data, name, input); + try{Reflect.setField(this, name, input);} catch(e) {} + } + else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null) + { + var parameterType:ShaderParameterType = switch (type) + { + case "bool": BOOL; + case "double", "float": FLOAT; + case "int", "uint": INT; + case "bvec2": BOOL2; + case "bvec3": BOOL3; + case "bvec4": BOOL4; + case "ivec2", "uvec2": INT2; + case "ivec3", "uvec3": INT3; + case "ivec4", "uvec4": INT4; + case "vec2", "dvec2": FLOAT2; + case "vec3", "dvec3": FLOAT3; + case "vec4", "dvec4": FLOAT4; + case "mat2", "mat2x2": MATRIX2X2; + case "mat2x3": MATRIX2X3; + case "mat2x4": MATRIX2X4; + case "mat3x2": MATRIX3X2; + case "mat3", "mat3x3": MATRIX3X3; + case "mat3x4": MATRIX3X4; + case "mat4x2": MATRIX4X2; + case "mat4x3": MATRIX4X3; + case "mat4", "mat4x4": MATRIX4X4; + default: null; + } + + var length = switch (parameterType) + { + case BOOL2, INT2, FLOAT2: 2; + case BOOL3, INT3, FLOAT3: 3; + case BOOL4, INT4, FLOAT4, MATRIX2X2: 4; + case MATRIX3X3: 9; + case MATRIX4X4: 16; + default: 1; + } + + var arrayLength = switch (parameterType) + { + case MATRIX2X2: 2; + case MATRIX3X3: 3; + case MATRIX4X4: 4; + default: 1; + } + + switch (parameterType) + { + case BOOL, BOOL2, BOOL3, BOOL4: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + parameter.__isBool = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramBool.push(parameter); + + if (name == "openfl_HasColorTransform") + { + __hasColorTransform = parameter; + } + + Reflect.setField(__data, name, parameter); + try{Reflect.setField(this, name, parameter);} catch(e) {} + + case INT, INT2, INT3, INT4: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + parameter.__isInt = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramInt.push(parameter); + Reflect.setField(__data, name, parameter); + try{Reflect.setField(this, name, parameter);} catch(e) {} + + default: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + #if lime + if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength); + #end + parameter.__isFloat = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramFloat.push(parameter); + + if (StringTools.startsWith(name, "openfl_")) + { + switch (name) + { + case "openfl_Alpha": __alpha = parameter; + case "openfl_ColorMultiplier": __colorMultiplier = parameter; + case "openfl_ColorOffset": __colorOffset = parameter; + case "openfl_Matrix": __matrix = parameter; + case "openfl_Position": __position = parameter; + case "openfl_TextureCoord": __textureCoord = parameter; + case "openfl_TextureSize": __textureSize = parameter; + default: + } + } + + Reflect.setField(__data, name, parameter); + try{Reflect.setField(this, name, parameter);} catch(e) {} + } + } + } + public function hget(name:String):Dynamic { if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}')) return Reflect.getProperty(this, name); @@ -394,7 +547,6 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { var cl:String = Type.getClassName(Type.getClass(field)); // cant do "field is ShaderInput" for some reason if (cl.startsWith("openfl.display.ShaderParameter")) { - @:privateAccess if (field.__length <= 1) { if ((val is Array)) return field.value = val; @@ -546,4 +698,16 @@ class ShaderTypeException extends Exception { this.name = name; super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.'); } +} + +class ShaderErrorPosition { + public var column:Int; + public var line:Int; + public var message:String; + + public function new(line:String, column:String, message:String) { + this.line = Std.parseInt(line); + this.column = Std.parseInt(column); + this.message = message; + } } \ No newline at end of file diff --git a/source/funkin/backend/shaders/ScriptableShader.hx b/source/funkin/backend/shaders/ScriptableShader.hx new file mode 100644 index 00000000..72a4a150 --- /dev/null +++ b/source/funkin/backend/shaders/ScriptableShader.hx @@ -0,0 +1,111 @@ +package funkin.backend.shaders; + +import hscript.IHScriptCustomBehaviour; +import funkin.backend.scripting.events.shader.ShaderProcessEvent; +import funkin.backend.scripting.ScriptPack; +import funkin.backend.scripting.Script; + +@:structInit +class FieldInfo { + public var get:Void->Dynamic; + public var set:Dynamic->Dynamic; +} + +@:access(funkin.backend.shaders.FunkinShader) +class ScriptableShader extends FlxBasic implements IHScriptCustomBehaviour { + private static var __instanceFields = Type.getInstanceFields(ScriptableShader); + + public var shader:FunkinShader; + public var script:Script; + + var fields:Map = []; + + public function new(shader:FunkinShader, ?scriptName:String, ?parentScriptPack:ScriptPack) { + super(); + + if(parentScriptPack == null && PlayState.instance != null) + parentScriptPack = PlayState.instance.scripts; + + if(scriptName == null) + if((shader is CustomShader)) scriptName = cast(shader, CustomShader).fileName; + else throw "Missing name for shader script, please provide a scriptName, or use CustomShader"; + + script = Script.create(Paths.script('shaders/$scriptName')); + script.setParent(shader); + script.set("shader", shader); + script.set("registerParameter", shader.registerParameter); + script.set("registerField", (name:String, get: Void->Dynamic, set: Dynamic->Dynamic) -> { + registerField(name, get, set); + }); + script.set("registerShaderField", (name:String, shaderField:String) -> { + var get = () -> shader.hget(shaderField); + var set = (value:Dynamic) -> { + var val = shader.hset(shaderField, value); + script.set(name, val); + return val; + }; + registerField(name, get, set); + var gg = script.get(name); + if(gg != null) { + shader.hset(shaderField, gg); + } + }); + if(parentScriptPack != null) + script.setPublicMap(parentScriptPack.publicVariables); + script.load(); + script.call("create"); + + shader.onGLUpdate.add(updateGL); + shader.onProcessGLData.add(processGLData); + } + + function registerField(name:String, get: Void->Dynamic, set: Dynamic->Dynamic) { + fields.set(name, { + get: get, + set: set + }); + script.set("get_"+name, get); + script.set("set_"+name, set); + } + + public override function update(elapsed:Float) script.call("update", [elapsed]); + public override function draw() script.call("draw", []); + public function updateGL() script.call("updateGL", []); + public function processGLData(source:String, storageType:String) { + var event = EventManager.get(ShaderProcessEvent).recycle(source, storageType); + script.call("processGLData", [event]); + if(event.cancelled) + shader.__cancelNextProcessGLData = true; + script.call("processGLDataPost", [event]); + } + + public override function destroy() { + shader.onGLUpdate.remove(updateGL); + shader.onProcessGLData.remove(processGLData); + if(script != null) { + script.call("destroy"); + script.destroy(); + } + fields = null; + super.destroy(); + } + + public function hget(name:String):Dynamic { + if(fields.exists(name)) + return fields.get(name).get(); + if (__instanceFields.contains(name) || __instanceFields.contains('get_${name}')) + return Reflect.getProperty(this, name); + return shader.hget(name); + } + + public function hset(name:String, val:Dynamic):Dynamic { + if (fields.exists(name)) { + return fields.get(name).set(val); + } + if (__instanceFields.contains(name) || __instanceFields.contains('set_${name}')) { + Reflect.setProperty(this, name, val); + return val; + } + return shader.hset(name, val); + } +} \ No newline at end of file diff --git a/source/funkin/backend/system/Conductor.hx b/source/funkin/backend/system/Conductor.hx index f9758b94..56dbfcd9 100644 --- a/source/funkin/backend/system/Conductor.hx +++ b/source/funkin/backend/system/Conductor.hx @@ -45,7 +45,7 @@ class Conductor /** * Number of steps per beat (bottom number in time signature). Defaults to 4. */ - public static var stepsPerBeat:Float = 4; + public static var stepsPerBeat:Int = 4; /** @@ -262,7 +262,7 @@ class Conductor } } - public static function changeBPM(newBpm:Float, beatsPerMeasure:Float = 4, stepsPerBeat:Float = 4) + public static function changeBPM(newBpm:Float, beatsPerMeasure:Float = 4, stepsPerBeat:Int = 4) { bpm = newBpm; diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 2ccee499..03a6b48c 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -2,7 +2,10 @@ package funkin.backend.system; import lime.utils.AssetLibrary; 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; @@ -11,13 +14,129 @@ import funkin.backend.assets.ScriptedAssetLibrary; */ @:build(funkin.backend.system.macros.FlagMacro.build()) class Flags { + // -- Codename's Default Flags -- + public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber; + public static var COMMIT_HASH:String = GitCommitMacro.commitHash; + public static var COMMIT_MESSAGE:String = 'Commit $COMMIT_NUMBER ($COMMIT_HASH)'; + @:lazy public static var VERSION_MESSAGE:String = 'Codename Engine v${Application.current.meta.get('version')}'; + + public static var REPO_NAME:String = "CodenameEngine"; + public static var REPO_OWNER:String = "FNF-CNE-Devs"; + 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. + */ + public static var SOUND_EXT:String = #if web "mp3" #else "ogg" #end; // we also support wav + public static var VIDEO_EXT:String = "mp4"; + 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_LOGO_TEXT:String = "Codename Engine"; + + @:also(funkin.game.Character.FALLBACK_CHARACTER) + public static var DEFAULT_CHARACTER:String = "bf"; + public static var DEFAULT_GIRLFRIEND:String = "gf"; + public static var DEFAULT_OPPONENT:String = "dad"; + + @:also(funkin.game.PlayState.difficulty) + public static var DEFAULT_DIFFICULTY:String = "normal"; + public static var DEFAULT_STAGE:String = "stage"; + public static var DEFAULT_SCROLL_SPEED:Float = 2.0; + public static var DEFAULT_HEALTH_ICON:String = "face"; + public static var SONGS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override"; public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override"; - public static var CHARTER_ADVANCED_SETTINGS:Bool = false; + public static var DEFAULT_BPM:Float = 100.0; + public static var DEFAULT_BEATS_PER_MEASURE:Float = 4; + public static var DEFAULT_STEPS_PER_BEAT:Int = 4; - // Internal stuff + 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; + + /** + * Default background colors for songs without bg color + */ + public static var DEFAULT_COLOR:FlxColor = 0xFF9271FD; + public static var DEFAULT_COOP_ALLOWED:Bool = false; + public static var DEFAULT_OPPONENT_MODE_ALLOWED:Bool = false; + + @:also(funkin.game.PlayState.coopMode) + public static var DEFAULT_COOP_MODE:Bool = false; // used in playstate if it doesnt find it + @:also(funkin.game.PlayState.opponentMode) + public static var DEFAULT_OPPONENT_MODE:Bool = false; + + public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500; + public static var DEFAULT_NOTE_SCALE:Float = 0.7; + + @:also(funkin.game.Character.FALLBACK_DEAD_CHARACTER) + public static var DEFAULT_GAMEOVER_CHARACTER:String = "bf-dead"; + public static var DEFAULT_GAMEOVER_SONG:String = "gameOver"; + public static var DEFAULT_GAMEOVER_LOSS_SFX:String = "gameOverSFX"; + public static var DEFAULT_GAMEOVER_RETRY_SFX:String = "gameOverEnd"; + + public static var DEFAULT_CAM_ZOOM_INTERVAL:Int = 4; + 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 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']; + public static var DEFAULT_GITAROO:Bool = true; + public static var GITAROO_CHANCE:Float = 0.1; + public static var DEFAULT_CAN_ACCESS_DEBUG_MENUS:Bool = true; + public static var DEFAULT_MUTE_VOCALS_ON_MISS:Bool = true; + + public static var DEFAULT_MAX_HEALTH:Float = 2.0; + public static var DEFAULT_HEALTH:Null = null;//DEFAULT_MAX_HEALTH / 2.0; + public static var BOP_ICON_SCALE:Float = 1.2; + public static var ICON_OFFSET:Float = 26; + public static var ICON_LERP:Float = 0.33; + + public static var CAM_BOP_STRENGTH:Float = 0.015; + public static var HUD_BOP_STRENGTH:Float = 0.03; + public static var DEFAULT_CAM_ZOOM_LERP:Float = 0.05; + public static var DEFAULT_HUD_ZOOM_LERP:Float = 0.05; + + public static var MAX_SPLASHES:Int = 8; + public static var STUNNED_TIME:Float = 5 / 60; + + @:also(funkin.game.PlayState.daPixelZoom) + public static var PIXEL_ART_SCALE:Float = 6.0; + + public static var DEFAULT_COMBO_GROUP_MAX_SIZE:Int = 25; + + public static var DEFAULT_STRUM_AMOUNT:Int = 4; + public static var DEFAULT_CAMERA_FOLLOW_SPEED:Float = 0.04; + + public static var VOCAL_OFFSET_VIOLATION_THRESHOLD:Float = 25; + + // Usage: Codename Credits + public static var MAIN_DEVS_COLOR:FlxColor = 0xFF9C35D5; + public static var MIN_CONTRIBUTIONS_COLOR:FlxColor = 0xFFB4A7DA; + + public static var UNDO_PREFIX:String = "* "; + public static var JSON_PRETTY_PRINT:String = "\t"; + // -- End of Codename's Default Flags -- + + /** + * Flags that Codename couldn't recognize as it's own defaults (they can only be `string`!). + */ @:bypass public static var customFlags:Map = []; public static function load() { diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx index ce590882..755d86f8 100644 --- a/source/funkin/backend/system/Logs.hx +++ b/source/funkin/backend/system/Logs.hx @@ -11,8 +11,7 @@ final class Logs { public static var nativeTrace = Log.trace; public static function init() { - - Log.trace = function(v : Dynamic, ?infos : Null) { + Log.trace = function(v:Dynamic, ?infos:Null) { var data = [ logText('${infos.fileName}:${infos.lineNumber}: ', CYAN), logText(Std.string(v)) @@ -28,22 +27,48 @@ final class Logs { __showInConsole(prepareColoredTrace(data, TRACE)); }; - LogFrontEnd.onLogs = function(Data, Style, FireOnce) { + LogFrontEnd.onLogs = function(data, style, fireOnce) { var prefix = "[FLIXEL]"; var color:ConsoleColor = LIGHTGRAY; var level:Level = INFO; - if (Style == LogStyle.CONSOLE) {prefix = "> "; color = WHITE; level = INFO; } else - if (Style == LogStyle.ERROR) {prefix = "[FLIXEL]"; color = RED; level = ERROR; } else - if (Style == LogStyle.NORMAL) {prefix = "[FLIXEL]"; color = WHITE; level = INFO; } else - if (Style == LogStyle.NOTICE) {prefix = "[FLIXEL]"; color = GREEN; level = VERBOSE;} else - if (Style == LogStyle.WARNING) {prefix = "[FLIXEL]"; color = YELLOW; level = WARNING;} + if (style == LogStyle.CONSOLE) // cant place a switch here as these arent inline values - Nex + { + prefix = "> "; + color = WHITE; + level = INFO; + } + else if (style == LogStyle.ERROR) + { + prefix = "[FLIXEL]"; + color = RED; + level = ERROR; + } + else if (style == LogStyle.NORMAL) + { + prefix = "[FLIXEL]"; + color = WHITE; + level = INFO; + } + else if (style == LogStyle.NOTICE) + { + prefix = "[FLIXEL]"; + color = GREEN; + level = VERBOSE; + } + else if (style == LogStyle.WARNING) + { + prefix = "[FLIXEL]"; + color = YELLOW; + level = WARNING; + } - var d:Dynamic = Data; + var d:Dynamic = data; if (!(d is Array)) d = [d]; var a:Array = d; var strs = [for(e in a) Std.string(e)]; - for(e in strs) { + for(e in strs) + { Logs.trace('$prefix $e', level, color); } }; @@ -55,12 +80,18 @@ final class Logs { logText('[ '), logText('${Std.string(time.getHours()).addZeros(2)}:${Std.string(time.getMinutes()).addZeros(2)}:${Std.string(time.getSeconds()).addZeros(2)}', DARKMAGENTA), logText(' |'), - switch(level) { - case WARNING: logText(' WARNING ', DARKYELLOW); - case ERROR: logText(' ERROR ', DARKRED); - case TRACE: logText(' TRACE ', GRAY); - case VERBOSE: logText(' VERBOSE ', DARKMAGENTA); - default: logText(' INFORMATION ', CYAN); + switch (level) + { + case WARNING: + logText(' WARNING ', DARKYELLOW); + case ERROR: + logText(' ERROR ', DARKRED); + case TRACE: + logText(' TRACE ', GRAY); + case VERBOSE: + logText(' VERBOSE ', DARKMAGENTA); + default: + logText(' INFORMATION ', CYAN); }, logText('] ') ]; @@ -77,7 +108,7 @@ final class Logs { } public static function __showInConsole(text:Array) { - #if sys + #if (sys && !mobile) while(__showing) { Sys.sleep(0.05); } @@ -89,20 +120,30 @@ final class Logs { NativeAPI.setConsoleColors(); Sys.print("\r\n"); __showing = false; + #elseif mobile + while(__showing) { + Sys.sleep(0.05); + } + __showing = true; + @:privateAccess + Sys.print([for(t in text) t.text].join("")); + __showing = false; #else @:privateAccess nativeTrace([for(t in text) t.text].join("")); #end } - public static function traceColored(text:Array, level:Level = INFO) { + public static function traceColored(text:Array, level:Level = INFO) __showInConsole(prepareColoredTrace(text, level)); - } + public static function trace(text:String, level:Level = INFO, color:ConsoleColor = LIGHTGRAY) { - traceColored([{ - text: text, - color: color - }], level); + traceColored([ + { + text: text, + color: color + } + ], level); } } @@ -113,7 +154,8 @@ enum abstract Level(Int) { var TRACE = 3; var VERBOSE = 4; } + typedef LogText = { var text:String; var color:ConsoleColor; -} \ No newline at end of file +} diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx index ee70213d..5617484f 100644 --- a/source/funkin/backend/system/Main.hx +++ b/source/funkin/backend/system/Main.hx @@ -30,10 +30,6 @@ import android.os.Build; class Main extends Sprite { - // make this empty once you guys are done with the project. - // good luck /gen <3 @crowplexus - public static final releaseCycle:String = "Beta"; - public static var instance:Main; public static var modToLoad:String = null; @@ -123,6 +119,9 @@ class Main extends Sprite ShaderResizeFix.init(); Logs.init(); Paths.init(); + + hscript.Interp.importRedirects = funkin.backend.scripting.Script.getDefaultImportRedirects(); + #if GLOBAL_SCRIPT funkin.backend.scripting.GlobalScript.init(); #end @@ -145,7 +144,7 @@ class Main extends Sprite Assets.registerLibrary('default', lib); funkin.options.PlayerSettings.init(); - funkin.savedata.FunkinSave.init(); + funkin.backend.utils.FunkinSave.init(); Options.load(); FlxG.fixedTimestep = false; @@ -171,13 +170,22 @@ class Main extends Sprite initTransition(); } - public static function refreshAssets() { - FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume'); - FlxSoundTray.volumeUpChangeSFX = null; - FlxSoundTray.volumeDownChangeSFX = null; + public static function refreshAssets() @:privateAccess { + WindowUtils.resetTitle(); - if (FlxG.game.soundTray != null) - FlxG.game.soundTray.text.setTextFormat(new TextFormat(Paths.font("vcr.ttf"))); + var game = FlxG.game; + var daSndTray = Type.createInstance(game._customSoundTray = funkin.menus.ui.FunkinSoundTray, []); + var index:Int = game.numChildren - 1; + + if(game.soundTray != null) + { + var newIndex:Int = game.getChildIndex(game.soundTray); + if(newIndex != -1) index = newIndex; + game.removeChild(game.soundTray); + game.soundTray.__cleanup(); + } + + game.addChildAt(game.soundTray = daSndTray, index); } public static function initTransition() { diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 7a354081..1ea04d22 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -18,8 +18,10 @@ class MainState extends FlxState { public static var betaWarningShown:Bool = false; public override function create() { super.create(); - if (!initiated) + if (!initiated) { Main.loadGameSettings(); + } + initiated = true; #if sys @@ -55,9 +57,9 @@ class MainState extends FlxState { } #end - Main.refreshAssets(); - ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); Flags.load(); + ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); // Loads global.hx + Main.refreshAssets(); DiscordUtil.init(); EventsData.reloadEvents(); ControlsUtil.loadCustomControls(); @@ -73,6 +75,8 @@ class MainState extends FlxState { betaWarningShown = true; } + #if sys CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN); + #end } } \ No newline at end of file diff --git a/source/funkin/backend/system/framerate/CodenameBuildField.hx b/source/funkin/backend/system/framerate/CodenameBuildField.hx index 5519f4d8..aa7a9b7b 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 ${Main.releaseCycle}\nCommit ${GitCommitMacro.commitNumber} (${GitCommitMacro.commitHash})'; + text = 'Codename Engine ${Flags.RELEASE_CYCLE}\n${Flags.COMMIT_MESSAGE}'; } diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx index 259b7f8f..297fcde7 100644 --- a/source/funkin/backend/system/framerate/FlixelInfo.hx +++ b/source/funkin/backend/system/framerate/FlixelInfo.hx @@ -1,5 +1,7 @@ package funkin.backend.system.framerate; +import funkin.backend.scripting.ModState; + class FlixelInfo extends FramerateCategory { public function new() { super("Flixel Info"); @@ -14,7 +16,12 @@ class FlixelInfo extends FramerateCategory { for(_ in FlxG.bitmap._cache.keys()) c++; - _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + if((FlxG.state is ModState)) { + var state:ModState = cast FlxG.state; + _text = "Mod State: " + state.scriptName; + } else { + _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + } _text += '\nObject Count: ${FlxG.state.members.length}'; _text += '\nCamera Count: ${FlxG.cameras.list.length}'; _text += '\nBitmaps Count: ${c}'; @@ -22,9 +29,9 @@ class FlixelInfo extends FramerateCategory { _text += '\nFlxG.game Childs Count: ${FlxG.game.numChildren}'; // _text += '\nCached objects count: ${cachedObjects}'; #if FLX_POINT_POOL - var points = flixel.math.FlxPoint.FlxBasePoint.pool; + //var points = flixel.math.FlxPoint.FlxBasePoint.pool; //_text += '\nPoint Count: ${points._count} | +${points.made} | -${points.gotten} | ${points.balance} | >${points.putted}'; - _text += '\nPoint Count: ${points._count}'; + //_text += '\nPoint Count: ${points._count}'; #end } diff --git a/source/funkin/backend/system/github/GitHub.hx b/source/funkin/backend/system/github/GitHub.hx index 6129a2d9..ce7e61dc 100644 --- a/source/funkin/backend/system/github/GitHub.hx +++ b/source/funkin/backend/system/github/GitHub.hx @@ -4,6 +4,7 @@ import haxe.Exception; #if GITHUB_API import haxe.Json; #end +import haxe.Exception; // TODO: Document further and perhaps make this a Haxelib. class GitHub { @@ -82,7 +83,7 @@ class GitHub { * @param onError Error Callback * @return Members List */ - public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array { + public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array { #if GITHUB_API try { var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members')); @@ -105,7 +106,7 @@ class GitHub { * @param onError Error Callback * @return User/Organization */ - public static function getUser(user:String, ?onError:Exception->Void):GitHubUser { + public static function getUser(user:String, ?onError:Exception->Void):GitHubUser { #if GITHUB_API try { var url = 'https://api.github.com/users/$user'; diff --git a/source/funkin/backend/system/macros/BuildInfo.hx b/source/funkin/backend/system/macros/BuildInfo.hx index 535f6118..37c0f197 100644 --- a/source/funkin/backend/system/macros/BuildInfo.hx +++ b/source/funkin/backend/system/macros/BuildInfo.hx @@ -6,13 +6,21 @@ using StringTools; class BuildInfo { public static function printBuildInfo() { if(haxe.macro.Context.defined("display")) return; - var haxeVersion = haxe.macro.Context.definedValue("haxe"); - Sys.println('--- BUILD INFO ---'); + var haxeVersion = haxe.macro.Context.definedValue("haxe").trim(); + Sys.println('[ BUILD INFO ]'); Sys.println('Haxe Version: ${haxeVersion}'); try { var lastBuiltWith:Null = null; - var compiling = #if final "final" #elseif debug "debug" #else "release" #end; - var target = #if windows "windows" #elseif mac "macos" #elseif linux "linux" #elseif android "android" #elseif ios "ios" #elseif html5 "html5" #else "" #end; + var compiling = #if final "release" #elseif debug "debug" #else "release" #end; + var target = #if hl "hl" + #elseif html5 "html5" + #elseif ios "ios" + #elseif android "android" + #elseif windows "windows" + #elseif (mac || macos) "macos" + #elseif linux "linux" + #else "" + #end; if(target == "") throw "Unknown target"; var exportPath = Sys.getCwd() + "/export/" + compiling + "/" + target + "/"; exportPath += "obj/Options.txt"; @@ -20,24 +28,27 @@ class BuildInfo { var options = sys.io.File.getContent(exportPath); for(option in options.split("\n")) { if(option.startsWith("haxe=")) { - lastBuiltWith = option.substr(5); + lastBuiltWith = option.substr(5).trim(); break; } } - if(lastBuiltWith != null && 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 !!!!]'); } catch(e) {} - Sys.println('Target Platform: ${ - #if windows "Windows" - #elseif mac "Mac" - #elseif linux "Linux" - #elseif android "Android" + var targetPlatform = #if html5 "Web (HTML5)" #elseif ios "iOS" - #elseif html5 "HTML5" + #elseif android "Android" + #elseif windows "Windows" + #elseif (mac || macos) "Mac" + #elseif linux "Linux" #else "Unknown" - #end - }'); + #end; + + if(haxe.macro.Context.defined("hl")) { + targetPlatform = "Hashlink (" + targetPlatform + ")"; + } + Sys.println('Target Platform: ' + targetPlatform); Sys.println('Build Date: ${Date.now().toString()}'); Sys.println(''); } diff --git a/source/funkin/backend/system/macros/FlagMacro.macro.hx b/source/funkin/backend/system/macros/FlagMacro.macro.hx index 3f827d7b..d18c10d1 100644 --- a/source/funkin/backend/system/macros/FlagMacro.macro.hx +++ b/source/funkin/backend/system/macros/FlagMacro.macro.hx @@ -7,7 +7,18 @@ using StringTools; using haxe.macro.Tools; +using funkin.backend.system.macros.FlagMacro; + class FlagMacro { + private static function hasMeta(meta:Array, name:String):Bool { + for(m in meta) { + if(m.name == name) { + return true; + } + } + return false; + } + public static function build():Array { var fields = Context.getBuildFields(); @@ -21,26 +32,50 @@ class FlagMacro { var parserExprs:Array = []; for (field in fields) { - var skip = false; - for(meta in field.meta) { - if(meta.name == ":bypass") { - skip = true; - break; - } - } + var skip = field.meta.hasMeta(":bypass"); + var hasLazy = field.meta.hasMeta(":lazy"); if(skip) continue; switch (field.kind) { case FVar(type, expr): + var isNullable = false; + switch(type) { + case TPath({name: "Null", pack: [], params: [TPType(p)]}): + type = p; + isNullable = true; + default: + } + + var alsos:Array = []; + for(meta in field.meta) { + if(meta.name == ":also") { + for(param in meta.params) { + switch(param.expr) { + case EField(_, _): + alsos.push(param); + default: + Context.error("Invalid :also parameter", param.pos); + } + } + } + } + if(expr == null) Context.error('Flag ' + field.name + ' must have a default value', field.pos); - switch(expr.expr) { - case EConst(CIdent("true")) | EConst(CIdent("false")): - type = macro: Bool; - default: - if(type == null) + if(type == null) { + switch(expr.expr) { + case EConst(CIdent("true")) | EConst(CIdent("false")): + type = macro: Bool; + case EConst(CInt(_)): + type = macro: Int; + case EConst(CFloat(_)): + type = macro: Float; + case EConst(CString(_)): + type = macro: String; + default: Context.error('Flag ' + field.name + ' must have a type', field.pos); + } } var parser:Expr = null; @@ -52,7 +87,7 @@ class FlagMacro { parser = macro value.split(",").map((e) -> e.trim()); case macro: Array: parser = macro value.split(","); - case macro: Array: + case (macro: Array) | (macro: Array): parser = macro value.split(",").map((e) -> Std.parseInt(e)); case macro: Array: parser = macro value.split(",").map((e) -> Std.parseFloat(e)); @@ -61,12 +96,15 @@ class FlagMacro { e = e.trim(); e == "true" || e == "t" || e == "1"; }); - case macro: Int: + case macro: TrimmedString: + field.kind = FVar(macro: String, expr); + parser = macro value.trim(); + case macro: String: + parser = macro value; + case (macro: Int) | (macro: FlxColor): parser = macro Std.parseInt(value); case macro: Float: parser = macro Std.parseFloat(value); - case macro: String: - parser = macro value; case macro: Bool: parser = macro value == "true" || value == "t" || value == "1"; case TPath({name: "Allow", pack: [], params: params}): @@ -128,8 +166,7 @@ class FlagMacro { if(chosenType == STRING) { parser = macro value; field.kind = FVar(macro: String, expr); - } - else if(chosenType == INT) { + } else if(chosenType == INT) { parser = macro Std.parseInt(value); field.kind = FVar(macro: Int, expr); } else { @@ -147,7 +184,7 @@ class FlagMacro { } case TPath({name: "Array", pack: []}): - Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString", field.pos); + Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString or Int or Float", field.pos); case TPath({name: "Map", pack: []}): Context.error("Flag " + field.name + " cannot be a Map", field.pos); default: @@ -159,6 +196,14 @@ class FlagMacro { continue; } + if(isNullable) { + switch(field.kind) { + case FVar(t, e): + field.kind = FVar(TPath({name: "Null", pack: [], params: [TPType(t)]}), e); + default: + } + } + resetExprs.push(macro $i{field.name} = ${expr}); // parse(name: String, value: String) @@ -166,16 +211,39 @@ class FlagMacro { if(customCheck != null) { parserExprs.push(customCheck); } else { - parserExprs.push(macro @:mergeBlock { - if(name == $v{field.name}) { - $i{field.name} = $parser; - return true; - } - }); + var alsoExpr = alsos.length > 0 ? macro @:mergeBlock $b{alsos.map((e) -> return macro $e = val)} : macro {}; + + if(isNullable) { + parserExprs.push(macro @:mergeBlock { + if(name == $v{field.name}) { + var val = value == "NULL" ? null : $parser; + $i{field.name} = val; + $alsoExpr; + return true; + } + }); + } else { + parserExprs.push(macro @:mergeBlock { + if(name == $v{field.name}) { + var val = $parser; + $i{field.name} = val; + $alsoExpr; + return true; + } + }); + } } default: // nothing } + + if(hasLazy) { + switch(field.kind) { + case FVar(t, expr): + field.kind = FVar(t, null); + default: + } + } } fields.push({ @@ -209,6 +277,9 @@ class FlagMacro { }); //var printer = new haxe.macro.Printer(); + //for(field in fields) { + // trace(printer.printField(field)); + //} //trace(printer.printField(fields[fields.length - 2])); //trace(printer.printField(fields[fields.length - 1])); diff --git a/source/funkin/backend/system/macros/HashLinkFixer.hx b/source/funkin/backend/system/macros/HashLinkFixer.hx index 046929e8..697d3c01 100644 --- a/source/funkin/backend/system/macros/HashLinkFixer.hx +++ b/source/funkin/backend/system/macros/HashLinkFixer.hx @@ -42,26 +42,12 @@ class HashLinkFixer { var cl = clRef.get(); if (cl.isAbstract || cl.isExtern || cl.isInterface) return fields; - if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC") && !cl.name.endsWith("_HLFHelper")) { - var metas = cl.meta.get(); - + if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC")) { if(cl.params.length > 0) return fields; - if(cl.module == "EReg") return fields; // private typedef in same class - if(cl.module == "hl.Format") return fields; // enum in same class - var definedFields = []; - var helperClass = macro class { - - }; - - helperClass.pos = cl.pos; - - var module = cl.module + "_HLFHelper"; - var hcClassName = cl.name + "_HLFHelper"; - for(f in fields.copy()) { if (f == null) continue; @@ -70,10 +56,12 @@ class HashLinkFixer { if(definedFields.contains(f.name)) continue; // no duplicate fields + var hlNativeMeta = null; var hasHlNative = false; for(m in f.meta) if (m.name == ":hlNative") { hasHlNative = true; + hlNativeMeta = m; break; } @@ -92,16 +80,39 @@ class HashLinkFixer { var overrideExpr:Expr; var returns:Bool = !fun.ret.match(TPath({name: "Void"})); + var printer = new haxe.macro.Printer(); + if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + var name = 'hlf_${f.name}'; var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}]; - var funcExpr:Expr = returns ? { - //macro return $i{name}($a{arguments}); - macro return @:privateAccess $i{hcClassName}.$name($a{arguments}); - } : { - macro @:privateAccess $i{hcClassName}.$name($a{arguments}); - }; + var funcExpr:Expr = macro @:privateAccess $i{name}($a{arguments}); + if(returns) funcExpr = macro return $funcExpr; + + var cleanMeta = f.meta.copy().filter(function(m) return m.name != ":hlNative"); + var hasBareMeta = hlNativeMeta.params.length == 0; + + var meta = f.meta.copy(); + switch hlNativeMeta { + case {params: []}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CString(name))]}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CFloat(version))]}: + var curVersion = Context.definedValue("hl_ver"); + if(curVersion == null) curVersion = ""; + if(version > curVersion) { + meta = cleanMeta; + if(f.meta.filter((m) -> m.name == ":noExpr").length > 0) + Context.error("Missing function body", f.pos); + funcExpr = fun.expr; // restore to default behaviour + } else { + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + } + default: + } var fiel:Field = { name: name, @@ -109,50 +120,32 @@ class HashLinkFixer { kind: FFun({ ret: fun.ret, params: fun.params.copy(), - expr: fun.expr, + expr: funcExpr, args: fun.args.copy() }), - access: f.access.copy(), - meta: f.meta.copy() + access: f.access.copy().filter(function(a) return a != APublic && a != APrivate).concat([APrivate]), + meta: meta }; - helperClass.fields.push(fiel); + fields.push(fiel); definedFields.push(f.name); + // Remove meta from original function for(m in f.meta.copy()) if (m.name == ":hlNative") { f.meta.remove(m); } - fun.expr = funcExpr; default: } } - helperClass.pack = cl.pack.copy(); - helperClass.pos = cl.pos; - helperClass.name = hcClassName; - - if(definedFields.length > 0) { + /*if(definedFields.length > 0) { trace(cl.module); - /*for(m in metas.copy()) { - trace(" " + m.name); - if(m.name == ":coreApi") { - metas.remove(m); - } - }*/ - - var imports = Context.getLocalImports().copy(); - Context.defineModule(module, [helperClass], imports); - - Context.getLocalImports().push({ - path: [for(m in module.split(".")) { - name: m, - pos: Context.currentPos() - }], - mode: INormal - }); - } + var printer = new haxe.macro.Printer(); + for(field in fields) if(field.name.startsWith("hlf_")) + Sys.println(printer.printField(field)); + }*/ } return fields; diff --git a/source/funkin/backend/system/updating/AsyncUpdater.hx b/source/funkin/backend/system/updating/AsyncUpdater.hx index ac31b044..36b79ad4 100644 --- a/source/funkin/backend/system/updating/AsyncUpdater.hx +++ b/source/funkin/backend/system/updating/AsyncUpdater.hx @@ -99,7 +99,7 @@ class AsyncUpdater { progress.files = files.length; progress.step = DOWNLOADING_ASSETS; trace('starting assets download'); - doFile([for(e in files) e], fileNames.copy(), function() { + doFile(files.copy(), fileNames.copy(), function() { progress.curFile = -1; progress.curFileName = null; progress.files = 1; diff --git a/source/funkin/backend/system/updating/UpdateScreen.hx b/source/funkin/backend/system/updating/UpdateScreen.hx index d7da6539..51af11ab 100644 --- a/source/funkin/backend/system/updating/UpdateScreen.hx +++ b/source/funkin/backend/system/updating/UpdateScreen.hx @@ -56,7 +56,7 @@ class UpdateScreen extends MusicBeatState { generalProgress.alignment = CENTER; add(generalProgress); - overSound = FlxG.sound.load(Paths.sound('gameOverEnd')); + overSound = FlxG.sound.load(Paths.sound(Flags.DEFAULT_GAMEOVER_RETRY_SFX)); updater.execute(); diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 7236a18e..f4214029 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -122,11 +122,15 @@ final class CoolUtil * @return The attributes through the `FileAttributeWrapper` */ @:noUsing public static inline function safeGetAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper { + #if sys addMissingFolders(Path.directory(path)); var result = NativeAPI.getFileAttributes(path, useAbsol); 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; + #end } /** @@ -139,11 +143,15 @@ final class CoolUtil */ @:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree, useAbsol:Bool = true):Int { // yes, i'm aware that FileAttribute is also an Int so need to include it too, but at least like this we don't have to make cast sometimes while passing the arguments - Nex + #if sys addMissingFolders(Path.directory(path)); var result = NativeAPI.setFileAttributes(path, attrib, useAbsol); if(result == 0) Logs.trace('Failed to set attributes to $path with a code of: $result', WARNING); return result; + #else + return 0; + #end } /** @@ -155,11 +163,15 @@ final class CoolUtil * @return The result code: `0` means that it failed setting */ @:noUsing public static inline function safeAddAttributes(path:String, attrib:OneOfTwo, useAbsol:Bool = true):Int { + #if sys addMissingFolders(Path.directory(path)); var result = NativeAPI.addFileAttributes(path, attrib, useAbsol); if(result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING); return result; + #else + return 0; + #end } /** @@ -171,11 +183,15 @@ final class CoolUtil * @return The result code: `0` means that it failed setting */ @:noUsing public static inline function safeRemoveAttributes(path:String, attrib:OneOfTwo, useAbsol:Bool = true):Int { + #if sys addMissingFolders(Path.directory(path)); var result = NativeAPI.removeFileAttributes(path, attrib, useAbsol); if(result == 0) Logs.trace('Failed to remove attributes to $path with a code of: $result', WARNING); return result; + #else + return 0; + #end } /** @@ -393,7 +409,7 @@ final class CoolUtil * @param Looped Whenever the music loops (true) * @param Group A group that this music belongs to (default) */ - @:noUsing public static function playMusic(path:String, Persist:Bool = false, Volume:Float = 1, Looped:Bool = true, DefaultBPM:Int = 102, ?Group:FlxSoundGroup) { + @:noUsing public static function playMusic(path:String, Persist:Bool = false, Volume:Float = 1, Looped:Bool = true, DefaultBPM:Float = 102, ?Group:FlxSoundGroup) { Conductor.reset(); FlxG.sound.playMusic(path, Volume, Looped, Group); if (FlxG.sound.music != null) { @@ -404,17 +420,17 @@ final class CoolUtil if (Assets.exists(infoPath)) { var musicInfo = IniUtil.parseAsset(infoPath, [ "BPM" => null, - "TimeSignature" => "4/4" + "TimeSignature" => Flags.DEFAULT_BEATS_PER_MEASURE + "/" + Flags.DEFAULT_STEPS_PER_BEAT ]); var timeSignParsed:Array> = musicInfo["TimeSignature"] == null ? [] : [for(s in musicInfo["TimeSignature"].split("/")) Std.parseFloat(s)]; - var beatsPerMeasure:Float = 4; - var stepsPerBeat:Float = 4; + var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE; + var stepsPerBeat:Int = Flags.DEFAULT_STEPS_PER_BEAT; // Check later, i dont think timeSignParsed can contain null, only nan if (timeSignParsed.length == 2 && !timeSignParsed.contains(null)) { - beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? 4 : cast timeSignParsed[0]; - stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? 4 : cast timeSignParsed[1]; + beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? Flags.DEFAULT_BEATS_PER_MEASURE : cast timeSignParsed[0]; + stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? Flags.DEFAULT_STEPS_PER_BEAT : cast timeSignParsed[1]; } var bpm:Null = Std.parseFloat(musicInfo["BPM"]).getDefault(DefaultBPM); @@ -762,7 +778,14 @@ final class CoolUtil * @param url */ @:noUsing public static inline function openURL(url:String) { + #if linux + // generally `xdg-open` should work in every distro + var cmd = Sys.command("xdg-open", [url]); + // run old command JUST IN CASE it fails, which it shouldn't + if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [url]); + #else FlxG.openURL(url); + #end } /** diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index 0aed4952..0d8dbc1c 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -5,7 +5,7 @@ import flixel.sound.FlxSound; import flixel.util.typeLimit.OneOfTwo; import funkin.backend.scripting.*; // lazy import funkin.backend.scripting.events.CancellableEvent; -import funkin.backend.scripting.events.DiscordPresenceUpdateEvent; +import funkin.backend.scripting.events.discord.*; import funkin.backend.system.macros.Utils; import haxe.Json; import openfl.display.BitmapData; @@ -86,9 +86,9 @@ final class DiscordUtil if (config == null) config = {}; - config.logoKey = config.logoKey.getDefault("icon"); - config.logoText = config.logoText.getDefault(Application.current.meta.get('title')); - config.clientID = config.clientID.getDefault("1027994136193810442"); + config.logoKey = config.logoKey.getDefault(Flags.DEFAULT_DISCORD_LOGO_KEY); + config.logoText = config.logoText.getDefault(Flags.DEFAULT_DISCORD_LOGO_TEXT); + config.clientID = config.clientID.getDefault(Flags.DEFAULT_DISCORD_CLIENT_ID); currentID = config.clientID; #end } @@ -525,7 +525,7 @@ typedef DPresence = var ?button1Url:String; /* max 512 bytes */ var ?button2Label:String; /* max 32 bytes */ var ?button2Url:String; /* max 512 bytes */ - var ?activityType:ActivityType; + var ?activityType:#if DISCORD_RPC ActivityType #else Dynamic #end; var ?streamUrl:String; /* max 512 bytes */ } diff --git a/source/funkin/savedata/FunkinSave.hx b/source/funkin/backend/utils/FunkinSave.hx similarity index 99% rename from source/funkin/savedata/FunkinSave.hx rename to source/funkin/backend/utils/FunkinSave.hx index 20337df6..82167fe6 100644 --- a/source/funkin/savedata/FunkinSave.hx +++ b/source/funkin/backend/utils/FunkinSave.hx @@ -1,4 +1,4 @@ -package funkin.savedata; +package funkin.backend.utils; import flixel.util.FlxSave; import lime.app.Application; diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx index e646aba1..9a85bcc9 100644 --- a/source/funkin/backend/utils/MemoryUtil.hx +++ b/source/funkin/backend/utils/MemoryUtil.hx @@ -140,14 +140,16 @@ final class MemoryUtil { reg.match(process.stdout.readAll().toString()); if (process.exitCode() == 0) return reg.matched(1); #elseif linux - var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]); + /*var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]); if (process.exitCode() != 0) return "Unknown"; var lines = process.stdout.readAll().toString().split("\n"); for (line in lines) { if (line.startsWith("Type:")) { return line.substring("Type:".length).trim(); } - } + }*/ + // TODO: sort of unsafe? also requires users to use `sudo` + // when launching the engine through the CLI, REIMPLEMENT LATER. #end return "Unknown"; } diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index 8ff95a6b..d3c6fcc4 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -147,6 +147,8 @@ final class XMLUtil { var graphicSizey:Null = Std.parseInt(node.att.graphicSizey); if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey); } + + if (node.has.flipX && node.att.flipX == "true") spr.flipX = true; if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox(); if (node.has.zoomfactor) diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index fd2a3f1e..be097756 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -362,7 +362,7 @@ class CharacterEditor extends UIState { } animationText.text = '"${character.getAnimName()}"'; - if (Options.characterDragging) + if (Options.characterDragging) handleMouseOffsets(); super.update(elapsed); diff --git a/source/funkin/editors/charter/ChartCreationScreen.hx b/source/funkin/editors/charter/ChartCreationScreen.hx index 3909869d..3a4eba1a 100644 --- a/source/funkin/editors/charter/ChartCreationScreen.hx +++ b/source/funkin/editors/charter/ChartCreationScreen.hx @@ -73,7 +73,7 @@ class ChartCreationScreen extends UISubstateWindow { strumLineList.addButton.callback = function() { strumLineList.add(new StrumLineButton(strumLineList.buttons.length, { - characters: ["dad"], + characters: [Flags.DEFAULT_OPPONENT], type: 0, notes: null, position: "DAD", @@ -84,7 +84,7 @@ class ChartCreationScreen extends UISubstateWindow { // DEFAULTS strumLineList.add(new StrumLineButton(0, { - characters: ["dad"], + characters: [Flags.DEFAULT_OPPONENT], type: 0, notes: null, position: "DAD", @@ -93,7 +93,7 @@ class ChartCreationScreen extends UISubstateWindow { scrollSpeed: 1, }, strumLineList)); strumLineList.add(new StrumLineButton(1, { - characters: ["bf"], + characters: [Flags.DEFAULT_CHARACTER], type: 1, notes: null, position: "BOYFRIEND", @@ -102,7 +102,7 @@ class ChartCreationScreen extends UISubstateWindow { scrollSpeed: 1, }, strumLineList)); strumLineList.add(new StrumLineButton(2, { - characters: ["gf"], + characters: [Flags.DEFAULT_GIRLFRIEND], type: 2, notes: null, position: "GIRLFRIEND", @@ -333,7 +333,7 @@ class CompactCharacterButton extends UIButton { char = funkin.game.Character.getIconFromCharName(char); var image = Paths.image("icons/" + char); if(!Assets.exists(image)) - image = Paths.image("icons/face"); + image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON); charIcon.loadGraphic(image, true, 150, 150); charIcon.updateHitbox(); } diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 597ef7ae..4d408800 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1325,7 +1325,7 @@ class Charter extends UIState { if (strumLines != null) strumlineLockButton.button.animation.play(strumLines.draggable ? "1" : "0", true); - WindowUtils.prefix = undos.unsaved ? "* " : ""; + WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : ""; SaveWarning.showWarning = undos.unsaved; __firstFrame = false; @@ -1382,7 +1382,7 @@ class Charter extends UIState { } function _file_saveas(_) { - openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? "\t" : null), { + openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { defaultSaveFile: '${__diff.toLowerCase()}.json' })); undos.save(); @@ -1398,7 +1398,7 @@ class Charter extends UIState { } function _file_saveas_no_events(_) { - openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? "\t" : null), { + openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { defaultSaveFile: '${__diff.toLowerCase()}.json' })); undos.save(); @@ -1408,7 +1408,7 @@ class Charter extends UIState { #if sys CoolUtil.safeSaveFile( '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json', - Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t") + Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, Flags.JSON_PRETTY_PRINT) ); #else _file_meta_saveas(_); @@ -1416,20 +1416,20 @@ class Charter extends UIState { } function _file_meta_saveas(_) { - openSubState(new SaveSubstate(Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t"), { // always pretty print meta + openSubState(new SaveSubstate(Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, Flags.JSON_PRETTY_PRINT), { // always pretty print meta defaultSaveFile: 'meta.json' })); } function _file_saveas_fnflegacy(_) { - openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), { - defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json', + openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { + defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', })); } - + function _file_saveas_psych(_) { - openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), { - defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json', + openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { + defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', })); } @@ -1437,7 +1437,7 @@ class Charter extends UIState { #if sys CoolUtil.safeSaveFile( '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json', - Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null) + Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null) ); #else _file_events_saveas(_); @@ -1446,7 +1446,7 @@ class Charter extends UIState { function _file_events_saveas(_) { #if sys - openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null), { + openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { defaultSaveFile: 'events.json' })); #end @@ -1775,7 +1775,7 @@ class Charter extends UIState { changeNoteSustain(-1); function _note_selectall(_) { - selection = [for (note in notesGroup.members) note]; + selection = cast notesGroup.members.copy(); } function _note_selectmeasure(_) { diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx index 60df5d9a..35c0dd1c 100644 --- a/source/funkin/editors/charter/CharterEventScreen.hx +++ b/source/funkin/editors/charter/CharterEventScreen.hx @@ -164,7 +164,11 @@ class CharterEventScreen extends UISubstateWindow { colorWheel; case TDropDown(options): addLabel(); - var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, Std.int(Math.abs(options.indexOf(cast value)))); + var optionIndex = options.indexOf(cast value); + if(optionIndex < 0) { + optionIndex = 0; + } + var dropdown = new UIDropDown(eventName.x, y, 320, 32, options, optionIndex); paramsPanel.add(dropdown); paramsFields.push(dropdown); dropdown; case TCharacter: diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx index 660e3d89..0ab7900c 100644 --- a/source/funkin/editors/charter/CharterMetaDataScreen.hx +++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx @@ -129,7 +129,7 @@ class CharterMetaDataScreen extends UISubstateWindow { @:privateAccess iconSprite.animation.clearAnimations(); var path:String = Paths.image('icons/$icon'); - if (!Assets.exists(path)) path = Paths.image('icons/face'); + 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); diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx index 24fe55f4..a3e0910d 100644 --- a/source/funkin/editors/charter/CharterSelection.hx +++ b/source/funkin/editors/charter/CharterSelection.hx @@ -109,9 +109,9 @@ class CharterSelection extends EditorTreeMenu { sys.FileSystem.createDirectory('$songFolder/charts'); // Save Files - CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, "\t")); - if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Paths.SOUND_EXT}', creation.instBytes); - if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Paths.SOUND_EXT}', creation.voicesBytes); + CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, Flags.JSON_PRETTY_PRINT)); + 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); #end var option = new EditorIconOption(creation.meta.name, "Press ACCEPT to choose a difficulty to edit.", creation.meta.icon, function() { @@ -147,7 +147,7 @@ class CharterSelection extends EditorTreeMenu { var songFolder:String = '${Paths.getAssetsRoot()}/songs/${curSong.name}'; // Save Files - CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, "\t")); + CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, Flags.JSON_PRETTY_PRINT)); // Add to List curSong.difficulties.push(name); diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx index 702afd9b..42d543c9 100644 --- a/source/funkin/editors/charter/CharterStrumlineScreen.hx +++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx @@ -48,7 +48,7 @@ class CharterStrumlineScreen extends UISubstateWindow { if (creatingStrumLine) strumLine = { - characters: ["dad"], + characters: [Flags.DEFAULT_OPPONENT], type: OPPONENT, notes: [], position: "dad", @@ -256,7 +256,7 @@ class CharacterButton extends UIButton { char = Character.getIconFromCharName(char); var image = Paths.image("icons/" + char); if(!Assets.exists(image)) - image = Paths.image("icons/face"); + image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON); charIcon.loadGraphic(image, true, 150, 150); charIcon.updateHitbox(); } diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 80549cf7..d6d526e5 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -80,7 +80,9 @@ class SongCreationScreen extends UISubstateWindow { stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); songDataGroup.add(stepsPerBeatStepper); - instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Paths.SOUND_EXT, function (res) { + var voicesUIText:UIText = null; + + instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (res) { var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res); instExplorer.members.push(audioPlayer); instExplorer.uiElement = audioPlayer; @@ -90,7 +92,7 @@ class SongCreationScreen extends UISubstateWindow { "Inst Audio File $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); - voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Paths.SOUND_EXT, function (res) { + voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Flags.SOUND_EXT, function (res) { var audioPlayer:UIAudioPlayer = new UIAudioPlayer(voicesExplorer.x + 8, voicesExplorer.y + 8, res); voicesExplorer.members.push(audioPlayer); voicesExplorer.uiElement = audioPlayer; @@ -211,7 +213,7 @@ class SongCreationScreen extends UISubstateWindow { @:privateAccess iconSprite.animation.clearAnimations(); var path:String = Paths.image('icons/$icon'); - if (!Assets.exists(path)) path = Paths.image('icons/face'); + 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); diff --git a/source/funkin/editors/ui/UISubstateWindow.hx b/source/funkin/editors/ui/UISubstateWindow.hx index 736c5472..be975868 100644 --- a/source/funkin/editors/ui/UISubstateWindow.hx +++ b/source/funkin/editors/ui/UISubstateWindow.hx @@ -1,5 +1,8 @@ package funkin.editors.ui; +import openfl.display.ShaderParameter; +import openfl.display.ShaderInput; +import openfl.filters.ShaderFilter; import flixel.tweens.FlxTween; import funkin.backend.shaders.CustomShader; import openfl.filters.ShaderFilter; @@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter; // TODO: make UIWarningSubstate extend this class UISubstateWindow extends MusicBeatSubstate { var camShaders:Array = []; - var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + var blurShader:CustomShader = { + var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + if(!Options.intensiveBlur) { + var noiseTexture:ShaderInput = _.data.noiseTexture; + noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png"); + noiseTexture.wrap = REPEAT; + var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize; + noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height]; + } + _; + }; var titleSpr:UIText; var messageSpr:UIText; diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx index 718980f0..1440c0bc 100644 --- a/source/funkin/editors/ui/UIWarningSubstate.hx +++ b/source/funkin/editors/ui/UIWarningSubstate.hx @@ -1,5 +1,8 @@ package funkin.editors.ui; +import openfl.display.ShaderParameter; +import openfl.display.ShaderInput; +import openfl.filters.ShaderFilter; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import funkin.backend.shaders.CustomShader; @@ -7,7 +10,17 @@ import openfl.filters.ShaderFilter; class UIWarningSubstate extends MusicBeatSubstate { var camShaders:Array = []; - var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + var blurShader:CustomShader = { + var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + if(!Options.intensiveBlur) { + var noiseTexture:ShaderInput = _.data.noiseTexture; + noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png"); + noiseTexture.wrap = REPEAT; + var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize; + noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height]; + } + _; + }; var title:String; var message:String; diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index 84bc5094..ff6ae27e 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -12,13 +12,11 @@ import funkin.backend.FunkinSprite; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; import funkin.backend.scripting.ScriptPack; -import funkin.backend.scripting.events.CharacterNodeEvent; -import funkin.backend.scripting.events.CharacterXMLEvent; -import funkin.backend.scripting.events.DanceEvent; -import funkin.backend.scripting.events.DirectionAnimEvent; -import funkin.backend.scripting.events.PlayAnimContext; -import funkin.backend.scripting.events.PlayAnimEvent; +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.character.*; +import funkin.backend.scripting.events.sprite.*; import funkin.backend.scripting.events.PointEvent; +import funkin.backend.scripting.events.DrawEvent; import funkin.backend.system.Conductor; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.interfaces.IOffsetCompatible; @@ -36,8 +34,8 @@ using StringTools; @:allow(funkin.game.PlayState) class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible implements IPrePostDraw { public var isPlayer:Bool = false; - public var curCharacter:String = 'bf'; - public var sprite:String = 'bf'; + public var curCharacter:String = Flags.DEFAULT_CHARACTER; + public var sprite:String = Flags.DEFAULT_CHARACTER; public var lastHit:Float = Math.NEGATIVE_INFINITY; public var holdTime:Float = 4; @@ -68,12 +66,12 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset @:noCompletion var __switchAnims:Bool = true; - public function new(x:Float, y:Float, ?character:String = "bf", isPlayer:Bool = false, switchAnims:Bool = true, disableScripts:Bool = false) + public function new(x:Float, y:Float, ?character:String, isPlayer:Bool = false, switchAnims:Bool = true, disableScripts:Bool = false) { super(x, y); animOffsets = new Map(); - curCharacter = character; + curCharacter = character != null ? character : Flags.DEFAULT_CHARACTER; this.isPlayer = isPlayer; __switchAnims = switchAnims; @@ -130,7 +128,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset scripts.call("update", [elapsed]); if (stunned) { __stunnedTime += elapsed; - if (__stunnedTime > 5 / 60) + if (__stunnedTime > Flags.STUNNED_TIME) stunned = false; } @@ -156,6 +154,11 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } public function tryDance() { + var event = new CancellableEvent(); + script.call("onTryDance", [event]); + if (event.cancelled) + return; + switch (lastAnimContext) { case SING | MISS: if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition) @@ -182,6 +185,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset tryDance(); } + public override function measureHit(curMeasure:Int) + script.call("measureHit", [curMeasure]); + public override function stepHit(curStep:Int) scripts.call("stepHit", [curStep]); @@ -234,6 +240,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var ghostDraw:Bool = false; public override function draw() { + var e = EventManager.get(DrawEvent).recycle(); + script.call("draw", [e]); + preDraw(); super.draw(); postDraw(); @@ -242,6 +251,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if (debugHitbox) drawHitbox(); if (debugCamera) drawCamera(); } + + script.call("postDraw", [e]); } public var debugHitbox:Bool = false; @@ -353,15 +364,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset @:noCompletion var __reverseTrailProcedure:Bool = false; - // When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;` + // When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;` and `trail.afterCache = char.afterTrailCache;` public dynamic function beforeTrailCache() if (isFlippedOffsets()) { flipX = !flipX; scale.x *= -1; __reverseTrailProcedure = true; } - - // When using trails on characters you should do `trail.afterCache = char.afterTrailCache;` + public dynamic function afterTrailCache() if (__reverseTrailProcedure) { flipX = !flipX; @@ -540,6 +550,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset @:noCompletion private function get_danceInterval() return beatInterval; + public static var FALLBACK_CHARACTER:String = Flags.DEFAULT_CHARACTER; + public static var FALLBACK_DEAD_CHARACTER:String = Flags.DEFAULT_GAMEOVER_CHARACTER; + private function set_script(script:Script):Script { if (scripts == null) (scripts = new ScriptPack("Character")).setParent(this); @@ -557,8 +570,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset // ---- end of Backwards compat ---- - public static var FALLBACK_CHARACTER:String = "bf"; - public static var FALLBACK_DEAD_CHARACTER:String = "bf-dead"; public static function getXMLFromCharName(character:OneOfTwo):Access { var char:Character = null; if (character is Character) { @@ -595,7 +606,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } public static function getIconFromCharName(?character:String, ?defaultIcon:String = null) { - if(character == null) return "face"; + if(character == null) return Flags.DEFAULT_HEALTH_ICON; if(defaultIcon == null) defaultIcon = character; var icon:String = defaultIcon; @@ -618,4 +629,4 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset list.push(CoolUtil.getFilename(path)); return list; } -} \ No newline at end of file +} diff --git a/source/funkin/game/GameOverSubstate.hx b/source/funkin/game/GameOverSubstate.hx index 9b8d01e5..a871422f 100644 --- a/source/funkin/game/GameOverSubstate.hx +++ b/source/funkin/game/GameOverSubstate.hx @@ -5,7 +5,7 @@ import flixel.util.FlxColor; import flixel.util.FlxTimer; import funkin.backend.scripting.Script; import funkin.backend.scripting.events.CancellableEvent; -import funkin.backend.scripting.events.GameOverCreationEvent; +import funkin.backend.scripting.events.gameover.*; import funkin.backend.system.Conductor; import funkin.editors.charter.Charter; import funkin.menus.FreeplayState; @@ -28,6 +28,7 @@ class GameOverSubstate extends MusicBeatSubstate public var gameoverScript:Script; public var game:PlayState = PlayState.instance; // shortcut + public static var instance:GameOverSubstate = null; private var __cancelDefault:Bool = false; @@ -36,20 +37,16 @@ class GameOverSubstate extends MusicBeatSubstate public var lossSFX:FlxSound; - public function new(x:Float, y:Float, character:String = null, player:Bool = true, gameOverSong:String = "gameOver", lossSFX:String = "gameOverSFX", retrySFX:String = "gameOverEnd") + public function new(x:Float, y:Float, ?character:String, player:Bool = true, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String) { super(); this.x = x; this.y = y; - - characterName = character; - if (characterName == null) - characterName = Character.FALLBACK_DEAD_CHARACTER; - this.player = player; - this.gameOverSong = gameOverSong; - this.lossSFXName = lossSFX; - this.retrySFX = retrySFX; + this.characterName = character != null ? character : Flags.DEFAULT_GAMEOVER_CHARACTER; + this.gameOverSong = gameOverSong != null ? gameOverSong : Flags.DEFAULT_GAMEOVER_SONG; + this.lossSFXName = lossSFX != null ? lossSFX : Flags.DEFAULT_GAMEOVER_LOSS_SFX; + this.retrySFX = retrySFX != null ? retrySFX : Flags.DEFAULT_GAMEOVER_RETRY_SFX; } public override function create() @@ -60,7 +57,7 @@ class GameOverSubstate extends MusicBeatSubstate FlxG.sound.music.stop(); gameoverScript = Script.create(Paths.script(script)); - gameoverScript.setParent(this); + gameoverScript.setParent(instance = this); gameoverScript.load(); var event = EventManager.get(GameOverCreationEvent).recycle(x, y, characterName, player, gameOverSong, gameOverSongBPM, lossSFXName, retrySFX); @@ -131,7 +128,7 @@ class GameOverSubstate extends MusicBeatSubstate if (!isEnding && ((!lossSFX.playing) || (character.getAnimName() == "firstDeath" && character.isAnimFinished())) && (FlxG.sound.music == null || !FlxG.sound.music.playing)) { - CoolUtil.playMusic(Paths.music(gameOverSong), false, 1, true, 100); + CoolUtil.playMusic(Paths.music(gameOverSong), false, 1, true, Flags.DEFAULT_BPM); beatHit(0); } } @@ -202,5 +199,6 @@ class GameOverSubstate extends MusicBeatSubstate gameoverScript.destroy(); super.destroy(); + instance = null; } } \ No newline at end of file diff --git a/source/funkin/game/HealthIcon.hx b/source/funkin/game/HealthIcon.hx index 1c7bb59d..5d2c8225 100644 --- a/source/funkin/game/HealthIcon.hx +++ b/source/funkin/game/HealthIcon.hx @@ -27,7 +27,7 @@ class HealthIcon extends FlxSprite /** * current animation state */ - public var curAnimState:Int = -1; + public var curAnimState:Int = -1; /** * Helper for HScript who can't make maps @@ -48,12 +48,12 @@ class HealthIcon extends FlxSprite ]; } - public function new(char:String = 'bf', isPlayer:Bool = false) + public function new(?char:String, isPlayer:Bool = false) { super(); health = 0.5; this.isPlayer = isPlayer; - setIcon(char); + setIcon(char != null ? char : Flags.DEFAULT_CHARACTER); scrollFactor.set(); } @@ -62,7 +62,7 @@ class HealthIcon extends FlxSprite 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/face'); + if (!Assets.exists(path)) path = Paths.image('icons/' + Flags.DEFAULT_HEALTH_ICON); loadGraphic(path, true, width, height); @@ -90,14 +90,12 @@ class HealthIcon extends FlxSprite 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; - } + 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.HealthIconChangeEvent).recycle(i, this); + var event = EventManager.get(funkin.backend.scripting.events.healthicon.HealthIconChangeEvent).recycle(i, this); funkin.backend.scripting.GlobalScript.event("onHealthIconAnimChange", event); if (!event.cancelled) animation.curAnim.curFrame = event.amount; diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index 5cc35051..f7cac647 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -3,7 +3,7 @@ package funkin.game; import flixel.math.FlxPoint; import flixel.math.FlxRect; import funkin.backend.chart.ChartData; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.note.NoteCreationEvent; import funkin.backend.system.Conductor; using StringTools; @@ -86,7 +86,7 @@ class Note extends FlxSprite return PlayState.instance.getNoteType(noteTypeID); } - public static var swagWidth:Float = 160 * 0.7; + public static var swagWidth:Float = 160 * 0.7; // TODO: remove this private static var __customNoteTypeExists:Map = []; @@ -117,11 +117,8 @@ class Note extends FlxSprite this.isSustainNote = sustain; this.sustainLength = sustainLength; this.strumLine = strumLine; - for(field in Reflect.fields(noteData)) { - if(!DEFAULT_FIELDS.contains(field)) { - this.extra.set(field, Reflect.field(noteData, field)); - } - } + for(field in Reflect.fields(noteData)) if(!DEFAULT_FIELDS.contains(field)) + this.extra.set(field, Reflect.field(noteData, field)); x += 50; // MAKE SURE ITS DEFINITELY OFF SCREEN? @@ -132,10 +129,10 @@ class Note extends FlxSprite var customType = Paths.image('game/notes/${this.noteType}'); var event = EventManager.get(NoteCreationEvent).recycle(this, strumID, this.noteType, noteTypeID, PlayState.instance.strumLines.members.indexOf(strumLine), mustPress, - (this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * 0.7, animSuffix); + (this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * Flags.DEFAULT_NOTE_SCALE, animSuffix); if (PlayState.instance != null) - event = PlayState.instance.scripts.event("onNoteCreation", event); + event = PlayState.instance.gameAndCharsEvent("onNoteCreation", event); this.animSuffix = event.animSuffix; if (!event.cancelled) { @@ -189,7 +186,7 @@ class Note extends FlxSprite if (PlayState.instance != null) { PlayState.instance.splashHandler.getSplashGroup(splash); - PlayState.instance.scripts.event("onPostNoteCreation", event); + PlayState.instance.gameAndCharsEvent("onPostNoteCreation", event); } } diff --git a/source/funkin/game/NoteGroup.hx b/source/funkin/game/NoteGroup.hx index 8c611581..8213a900 100644 --- a/source/funkin/game/NoteGroup.hx +++ b/source/funkin/game/NoteGroup.hx @@ -12,7 +12,7 @@ class NoteGroup extends FlxTypedGroup { /** * How many ms it should show a note before it should be hit **/ - public var limit:Float = 1500; + public var limit:Float = Flags.DEFAULT_NOTE_MS_LIMIT; /** * Preallocates the members array with nulls, but if theres anything in the array already it clears it diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index f3962944..01b50108 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -18,6 +18,8 @@ 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.gameplay.*; +import funkin.backend.scripting.events.note.*; import funkin.backend.scripting.events.*; import funkin.backend.system.Conductor; import funkin.backend.system.RotatingSpriteGroup; @@ -28,7 +30,7 @@ import funkin.game.SplashHandler; import funkin.game.cutscenes.*; import funkin.menus.*; import funkin.menus.StoryMenuState.WeekData; -import funkin.savedata.FunkinSave; +import funkin.backend.utils.FunkinSave; import haxe.io.Path; using StringTools; @@ -61,7 +63,7 @@ class PlayState extends MusicBeatState /** * The selected difficulty name */ - public static var difficulty:String = "normal"; + public static var difficulty:String = Flags.DEFAULT_DIFFICULTY; /** * Whenever the week is coming from the mods folder or not. */ @@ -73,11 +75,11 @@ class PlayState extends MusicBeatState /** * Whenever the song has been started with opponent mode on. */ - public static var opponentMode:Bool = false; + public static var opponentMode:Bool = Flags.DEFAULT_OPPONENT_MODE; /** * Whenever the song has been started with co-op mode on. */ - public static var coopMode:Bool = false; + public static var coopMode:Bool = Flags.DEFAULT_COOP_MODE; /** * Script Pack of all the scripts being ran. @@ -96,15 +98,15 @@ class PlayState extends MusicBeatState /** * Game Over Song. (assets/music/gameOver.ogg) */ - public var gameOverSong:String = "gameOver"; + public var gameOverSong:String = Flags.DEFAULT_GAMEOVER_SONG; /** * Game Over Song. (assets/sounds/gameOverSFX.ogg) */ - public var lossSFX:String = "gameOverSFX"; + public var lossSFX:String = Flags.DEFAULT_GAMEOVER_LOSS_SFX; /** * Game Over End SFX, used when retrying. (assets/sounds/gameOverEnd.ogg) */ - public var retrySFX:String = "gameOverEnd"; + public var retrySFX:String = Flags.DEFAULT_GAMEOVER_RETRY_SFX; /** * Current Stage. @@ -209,15 +211,15 @@ class PlayState extends MusicBeatState /** * Whenever the vocals should be muted when a note is missed. */ - public var muteVocalsOnMiss:Bool = true; + public var muteVocalsOnMiss:Bool = Flags.DEFAULT_MUTE_VOCALS_ON_MISS; /** * Whenever the player can press 7, 8 or 9 to access the debug menus. */ - public var canAccessDebugMenus:Bool = true; + public var canAccessDebugMenus:Bool = Flags.DEFAULT_CAN_ACCESS_DEBUG_MENUS; /** * Whether or not to show the secret gitaroo pause. */ - public var allowGitaroo:Bool = true; + public var allowGitaroo:Bool = Flags.DEFAULT_GITAROO; /** * Whether or not to bop the icons on beat. */ @@ -231,19 +233,23 @@ class PlayState extends MusicBeatState * Interval of cam zooming (beats). * For example: if set to 4, the camera will zoom every 4 beats. */ - public var camZoomingInterval:Int = 4; + public var camZoomingInterval:Int = Flags.DEFAULT_CAM_ZOOM_INTERVAL; /** * How strong the cam zooms should be (defaults to 1) */ - public var camZoomingStrength:Float = 1; + public var camZoomingStrength:Float = Flags.DEFAULT_CAM_ZOOM_STRENGTH; /** * Maximum amount of zoom for the camera. */ - public var maxCamZoom:Float = 1.35; + public var maxCamZoom:Float = Flags.MAX_CAMERA_ZOOM; /** * Current song name (lowercase) */ public var curSong:String = ""; + /** + * Current song name (lowercase and spaces to dashes) + */ + public var curSongID:String = ""; /** * Current stage name */ @@ -262,7 +268,7 @@ class PlayState extends MusicBeatState /** * Maximum health the player can have. Defaults to 2. */ - @:isVar public var maxHealth(get, set):Float = 2; + @:isVar public var maxHealth(get, set):Float = Flags.DEFAULT_MAX_HEALTH; /** * Current combo. */ @@ -362,25 +368,25 @@ class PlayState extends MusicBeatState /** * Camera zoom at which the game lerps to. */ - public var defaultCamZoom:Float = 1.05; + public var defaultCamZoom:Float = Flags.DEFAULT_CAM_ZOOM; /** * Speed at which the game camera zoom lerps to. */ - public var camGameZoomLerp:Float = 0.05; + public var camGameZoomLerp:Float = Flags.DEFAULT_CAM_ZOOM_LERP; /** * Camera zoom at which the hud lerps to. */ - public var defaultHudZoom:Float = 1.0; + public var defaultHudZoom:Float = Flags.DEFAULT_HUD_ZOOM; /** * Speed at which the hud camera zoom lerps to. */ - public var camHUDZoomLerp:Float = 0.05; + public var camHUDZoomLerp:Float = Flags.DEFAULT_HUD_ZOOM_LERP; /** * Zoom for the pixel assets. */ - public static var daPixelZoom:Float = 6; + public static var daPixelZoom:Float = Flags.PIXEL_ART_SCALE; /** * Whenever the game is currently in a cutscene or not. @@ -459,6 +465,22 @@ class PlayState extends MusicBeatState * Group containing all of the combo sprites. */ public var comboGroup:RotatingSpriteGroup; + /** + * Whenever the Rating sprites should be shown or not. + * + * NOTE: This is just a default value for the final value, the final value can be changed through notes hit events. + */ + public var defaultDisplayRating:Bool = true; + /** + * Whenever the Combo sprite should be shown or not (like old Week 7 patches). + * + * NOTE: This is just a default value for the final value, the final value can be changed through notes hit events. + */ + public var defaultDisplayCombo:Bool = false; + /** + * Minimum Combo Count to display the combo digits. Anything less than 0 means it won't be shown. + */ + public var minDigitDisplay:Int = 10; /** * Array containing all of the note types names. */ @@ -516,7 +538,7 @@ class PlayState extends MusicBeatState if (e.percent <= acc && (rating == null || rating.percent < e.percent)) rating = e; - var event = scripts.event("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating)); + var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating)); if (!event.cancelled) curRating = event.rating; } @@ -538,6 +560,24 @@ class PlayState extends MusicBeatState return name; } + public inline function callOnCharacters(func:String, ?parameters:Array) { + if(strumLines != null) strumLines.forEachAlive(function (strLine:StrumLine) { + if (strLine.characters != null) for (character in strLine.characters) + if (character != null) character.script.call(func, parameters); + }); + } + + public inline function gameAndCharsCall(func:String, ?parameters:Array, ?charsFunc:String) { + scripts.call(func, parameters); + callOnCharacters(charsFunc != null ? charsFunc : func, parameters); + } + + public inline function gameAndCharsEvent(func:String, ?event:T, ?charsFunc:String):T { + scripts.event(func, event); + callOnCharacters(charsFunc != null ? charsFunc : func, [event]); + return event; + } + @:dox(hide) override public function create() { Note.__customNoteTypeExists = []; @@ -581,7 +621,7 @@ class PlayState extends MusicBeatState // CHARACTER INITIALIZATION #if REGION comboGroup = new RotatingSpriteGroup(FlxG.width * 0.55, (FlxG.height * 0.5) - 60); - comboGroup.maxSize = 25; + comboGroup.maxSize = Flags.DEFAULT_COMBO_GROUP_MAX_SIZE; #end // CAMERA FOLLOW, SCRIPTS & STAGE INITIALIZATION @@ -589,7 +629,7 @@ class PlayState extends MusicBeatState camFollow = new FlxObject(0, 0, 2, 2); add(camFollow); - if (SONG.stage == null || SONG.stage.trim() == "") SONG.stage = "stage"; + if (SONG.stage == null || SONG.stage.trim() == "") SONG.stage = Flags.DEFAULT_STAGE; add(stage = new Stage(SONG.stage)); if (!chartingMode || Options.charterEnablePlaytestScripts) { @@ -702,15 +742,15 @@ class PlayState extends MusicBeatState // HUD INITIALIZATION & CAMERA INITIALIZATION #if REGION var event = EventManager.get(AmountEvent).recycle(null); - if (!scripts.event("onPreGenerateStrums", event).cancelled) { + if (!gameAndCharsEvent("onPreGenerateStrums", event).cancelled) { generateStrums(event.amount); - scripts.event("onPostGenerateStrums", event); + gameAndCharsEvent("onPostGenerateStrums", event); } for(str in strumLines) str.generate(str.data, (chartingMode && Charter.startHere) ? Charter.startTime : null); - FlxG.camera.follow(camFollow, LOCKON, 0.04); + FlxG.camera.follow(camFollow, LOCKON, Flags.DEFAULT_CAMERA_FOLLOW_SPEED); FlxG.camera.zoom = defaultCamZoom; // camHUD.zoom = defaultHudZoom; @@ -739,10 +779,11 @@ class PlayState extends MusicBeatState healthBar.createFilledBar(leftColor, rightColor); add(healthBar); - health = maxHealth / 2; + if (Flags.DEFAULT_HEALTH != null) health = Flags.DEFAULT_HEALTH; + else health = maxHealth / 2; - iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : "face", true); - iconP2 = new HealthIcon(dad != null ? dad.getIcon() : "face", false); + 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]) { icon.y = healthBar.y - (icon.height / 2); add(icon); @@ -779,7 +820,7 @@ class PlayState extends MusicBeatState FlxG.sound.load(Paths.sound(s)); if (chartingMode) { - WindowUtils.prefix = Charter.undos.unsaved ? "* " : ""; + WindowUtils.prefix = Charter.undos.unsaved ? Flags.UNDO_PREFIX : ""; WindowUtils.suffix = " (Chart Playtesting)"; SaveWarning.showWarning = Charter.undos.unsaved; @@ -804,7 +845,7 @@ class PlayState extends MusicBeatState __updateNote_event = EventManager.get(NoteUpdateEvent); - scripts.call("postCreate"); + gameAndCharsCall("postCreate", null, "gamePostCreate"); } /** @@ -835,7 +876,7 @@ class PlayState extends MusicBeatState inCutscene = true; var videoCutscene = Paths.video('$songName-${prefix}cutscene'); - var videoCutsceneAlt = Paths.file('songs/$songName/${prefix}cutscene.mp4'); + var videoCutsceneAlt = Paths.file('songs/$songName/${prefix}cutscene.${Flags.VIDEO_EXT}'); var dialogue = Paths.file('songs/$songName/${prefix}dialogue.xml'); persistentUpdate = true; var toCall:Void->Void = function() { @@ -868,7 +909,7 @@ class PlayState extends MusicBeatState _startCountdownCalled = true; inCutscene = false; - if (scripts.event("onStartCountdown", new CancellableEvent()).cancelled) return; + if (gameAndCharsEvent("onStartCountdown", new CancellableEvent()).cancelled) return; } startedCountdown = true; @@ -881,14 +922,14 @@ class PlayState extends MusicBeatState countdown(swagCounter++); }, introLength); } - scripts.call("onPostStartCountdown"); + gameAndCharsCall("onPostStartCountdown"); } /** * Creates a fake countdown. */ public function countdown(swagCounter:Int) { - var event:CountdownEvent = scripts.event("onCountdown", EventManager.get(CountdownEvent).recycle( + var event:CountdownEvent = gameAndCharsEvent("onCountdown", EventManager.get(CountdownEvent).recycle( swagCounter, 1, introSounds[swagCounter], @@ -931,12 +972,12 @@ class PlayState extends MusicBeatState event.spriteTween = tween; event.cancelled = false; - scripts.event("onPostCountdown", event); + gameAndCharsEvent("onPostCountdown", event); } @:dox(hide) function startSong():Void { - scripts.call("onSongStart"); + gameAndCharsCall("onSongStart"); startingSong = false; inst.onComplete = endSong; @@ -963,7 +1004,7 @@ class PlayState extends MusicBeatState updateDiscordPresence(); - scripts.call("onStartSong"); + gameAndCharsCall("onStartSong"); } public override function destroy() { @@ -1010,11 +1051,15 @@ class PlayState extends MusicBeatState return FlxSort.byValues(FlxSort.DESCENDING, p1.time, p2.time); }); - camZoomingInterval = cast songData.meta.beatsPerMeasure.getDefault(4); + var beatsPerMeasure:Float = songData.meta.beatsPerMeasure.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE); + var stepsPerBeat:Int = songData.meta.stepsPerBeat.getDefault(Flags.DEFAULT_STEPS_PER_BEAT); - Conductor.changeBPM(songData.meta.bpm, cast songData.meta.beatsPerMeasure.getDefault(4), cast songData.meta.stepsPerBeat.getDefault(4)); + camZoomingInterval = Std.int(beatsPerMeasure); + + Conductor.changeBPM(songData.meta.bpm, beatsPerMeasure, stepsPerBeat); curSong = songData.meta.name.toLowerCase(); + curSongID = curSong.replace(" ", "-"); inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty)); if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true @@ -1044,7 +1089,7 @@ class PlayState extends MusicBeatState @:dox(hide) override function openSubState(SubState:FlxSubState) { - var event = scripts.event("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState)); + var event = gameAndCharsEvent("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState)); if (!postCreated) MusicBeatState.skipTransIn = true; @@ -1064,13 +1109,13 @@ class PlayState extends MusicBeatState startTimer.active = false; } - super.openSubState(SubState); + super.openSubState(event.substate is FlxSubState ? cast event.substate : SubState); } @:dox(hide) override function closeSubState() { - var event = scripts.event("onSubstateClose", EventManager.get(StateEvent).recycle(subState)); + var event = gameAndCharsEvent("onSubstateClose", EventManager.get(StateEvent).recycle(subState)); if (event.cancelled) return; if (paused) @@ -1104,7 +1149,7 @@ class PlayState extends MusicBeatState inst.resume(); vocals.resume(); } - scripts.call("onFocus"); + gameAndCharsCall("onFocus"); updateDiscordPresence(); super.onFocus(); } @@ -1117,7 +1162,7 @@ class PlayState extends MusicBeatState inst.pause(); vocals.pause(); } - scripts.call("onFocusLost"); + gameAndCharsCall("onFocusLost"); updateDiscordPresence(); super.onFocusLost(); } @@ -1136,14 +1181,14 @@ class PlayState extends MusicBeatState strumLine.vocals.play(); } vocals.play(); - scripts.call("onVocalsResync"); + gameAndCharsCall("onVocalsResync"); } /** * Pauses the game. */ public function pauseGame() { - var e = scripts.event("onGamePause", new CancellableEvent()); + var e = gameAndCharsEvent("onGamePause", new CancellableEvent()); if (e.cancelled) return; persistentUpdate = false; @@ -1151,7 +1196,7 @@ class PlayState extends MusicBeatState paused = true; // 1 / 1000 chance for Gitaroo Man easter egg - if (allowGitaroo && FlxG.random.bool(0.1)) + if (allowGitaroo && FlxG.random.bool(Flags.GITAROO_CHANCE)) { // gitaroo man easter egg FlxG.switchState(new GitarooPause()); @@ -1208,7 +1253,7 @@ class PlayState extends MusicBeatState } function updateIconPositions() { - var iconOffset:Int = 26; + var iconOffset = Flags.ICON_OFFSET; var center:Float = healthBar.x + healthBar.width * FlxMath.remapToRange(healthBar.percent, 0, 100, 1, 0); @@ -1267,8 +1312,9 @@ class PlayState extends MusicBeatState } if (doIconBop) { - iconP1.scale.set(lerp(iconP1.scale.x, 1, 0.33), lerp(iconP1.scale.y, 1, 0.33)); - iconP2.scale.set(lerp(iconP2.scale.x, 1, 0.33), lerp(iconP2.scale.y, 1, 0.33)); + 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)); iconP1.updateHitbox(); iconP2.updateHitbox(); @@ -1287,7 +1333,7 @@ class PlayState extends MusicBeatState var instTime = FlxG.sound.music.time; var isOffsync = vocals.time != instTime || [for(strumLine in strumLines.members) strumLine.vocals.time != instTime].contains(true); __vocalOffsetViolation = Math.max(0, __vocalOffsetViolation + (isOffsync ? elapsed : -elapsed / 2)); - if (__vocalOffsetViolation > 25) { + if (__vocalOffsetViolation > Flags.VOCAL_OFFSET_VIOLATION_THRESHOLD) { resyncVocals(); __vocalOffsetViolation = 0; } @@ -1312,7 +1358,7 @@ class PlayState extends MusicBeatState pos.x /= r; pos.y /= r; - var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r)); + var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r)); if (!event.cancelled) camFollow.setPosition(pos.x, pos.y); } @@ -1357,10 +1403,12 @@ class PlayState extends MusicBeatState public var scrollSpeedTween:FlxTween = null; public function executeEvent(event:ChartEvent) @:privateAccess { - if (event == null) return; - if (event.params == null) return; + if (event == null || event.params == null) return; - if (scripts.event("onEvent", EventManager.get(EventGameEvent).recycle(event)).cancelled) return; + var e = EventManager.get(EventGameEvent).recycle(event); + gameAndCharsEvent("onEvent", e); + if (e.cancelled) return; + var event = e.event; switch(event.name) { case "HScript Call": @@ -1420,11 +1468,11 @@ class PlayState extends MusicBeatState */ public function gameOver(?character:Character, ?deathCharID:String, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String) { var charToUse:Character = character.getDefault(opponentMode ? dad : boyfriend); // Imma still make it check null later just in case dad or bf are also null for some weird scripts - Nex - var event:GameOverEvent = scripts.event("onGameOver", EventManager.get(GameOverEvent).recycle( + var event:GameOverEvent = gameAndCharsEvent("onGameOver", EventManager.get(GameOverEvent).recycle( charToUse == null ? 0 : charToUse.x, charToUse == null ? 0 : charToUse.y, charToUse, - deathCharID.getDefault(charToUse != null ? charToUse.gameOverCharacter : Character.FALLBACK_DEAD_CHARACTER), + deathCharID.getDefault(charToUse != null ? charToUse.gameOverCharacter : Flags.DEFAULT_GAMEOVER_CHARACTER), charToUse != null ? charToUse.isPlayer : true, gameOverSong.getDefault(this.gameOverSong), lossSFX.getDefault(this.lossSFX), @@ -1449,7 +1497,7 @@ class PlayState extends MusicBeatState openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX)); - scripts.event("onPostGameOver", event); + gameAndCharsEvent("onPostGameOver", event); } /** @@ -1457,7 +1505,7 @@ class PlayState extends MusicBeatState */ public function endSong():Void { - scripts.call("onSongEnd"); + gameAndCharsCall("onSongEnd"); canPause = false; inst.volume = 0; vocals.volume = 0; @@ -1578,7 +1626,7 @@ class PlayState extends MusicBeatState var directionID:Null = note == null ? direction : note.strumID; if (playerID == null || directionID == null || playerID == -1) return; - var event:NoteMissEvent = scripts.event("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) return; @@ -1662,11 +1710,13 @@ class PlayState extends MusicBeatState var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick")); + event = 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"); else - event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false)); + event = 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.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); - scripts.event("onNoteHit", event); + gameAndCharsEvent("onNoteHit", event); if (!event.cancelled) { if (!note.isSustainNote) { @@ -1681,7 +1731,8 @@ class PlayState extends MusicBeatState if (event.showRating || (event.showRating == null && event.player)) { displayCombo(event); - displayRating(event.rating, event); + if (event.displayRating) + displayRating(event.rating, event); ratingNum += 1; } } @@ -1711,7 +1762,7 @@ class PlayState extends MusicBeatState } } - if (event.deleteNote && !note.isSustainNote) strumLine.deleteNote(note); + if (event.deleteNote) strumLine.deleteNote(note); } public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void { @@ -1740,14 +1791,12 @@ class PlayState extends MusicBeatState } public function displayCombo(?evt:NoteHitEvent = null):Void { - var hasEvent = evt != null; - var pre:String = hasEvent ? evt.ratingPrefix : ""; - var suf:String = hasEvent ? evt.ratingSuffix : ""; + if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) { + var hasEvent = evt != null; + var pre:String = hasEvent ? evt.ratingPrefix : ""; + var suf:String = hasEvent ? evt.ratingSuffix : ""; - var separatedScore:String = Std.string(combo).addZeros(3); - - if (combo == 0 || combo >= 10) { - if (combo >= 10) { + if (evt.displayCombo) { var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}')); comboSpr.resetSprite(comboGroup.x, comboGroup.y); comboSpr.acceleration.y = 600; @@ -1769,6 +1818,7 @@ class PlayState extends MusicBeatState }); } + var separatedScore:String = Std.string(combo).addZeros(3); for (i in 0...separatedScore.length) { var numScore:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}')); @@ -1820,14 +1870,15 @@ class PlayState extends MusicBeatState if (camZoomingInterval < 1) camZoomingInterval = 1; if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom && curBeat % camZoomingInterval == 0) { - FlxG.camera.zoom += 0.015 * camZoomingStrength; - camHUD.zoom += 0.03 * camZoomingStrength; + FlxG.camera.zoom += Flags.CAM_BOP_STRENGTH * camZoomingStrength; + camHUD.zoom += Flags.HUD_BOP_STRENGTH * camZoomingStrength; } if (doIconBop) { - iconP1.scale.set(1.2, 1.2); - iconP2.scale.set(1.2, 1.2); + var iconScale = Flags.BOP_ICON_SCALE; + iconP1.scale.set(iconScale, iconScale); + iconP2.scale.set(iconScale, iconScale); iconP1.updateHitbox(); iconP2.updateHitbox(); @@ -1915,7 +1966,8 @@ class PlayState extends MusicBeatState * @param weekData Week Data * @param difficulty Week Difficulty */ - public static function loadWeek(weekData:WeekData, difficulty:String = "normal") { + 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]; isStoryMode = true; @@ -1935,7 +1987,8 @@ class PlayState extends MusicBeatState * @param opponentMode Whenever opponent mode is on * @param coopMode Whenever co-op mode is on. */ - public static function loadSong(_name:String, _difficulty:String = "normal", _opponentMode:Bool = false, _coopMode:Bool = false) { + public static function loadSong(_name:String, ?_difficulty:String, _opponentMode:Bool = false, _coopMode:Bool = false) { + if (_difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; isStoryMode = false; opponentMode = _opponentMode; chartingMode = false; diff --git a/source/funkin/game/SplashGroup.hx b/source/funkin/game/SplashGroup.hx index 7d8f9211..7da89aad 100644 --- a/source/funkin/game/SplashGroup.hx +++ b/source/funkin/game/SplashGroup.hx @@ -43,7 +43,7 @@ class SplashGroup extends FlxTypedGroup { Logs.trace('Couldn\'t parse splash data for "${path}": ${e.toString()}', ERROR); valid = false; } - maxSize = 8; + maxSize = Flags.MAX_SPLASHES; } var _scale:Float = 1.0; diff --git a/source/funkin/game/SplashHandler.hx b/source/funkin/game/SplashHandler.hx index 31a06234..ad14ad8c 100644 --- a/source/funkin/game/SplashHandler.hx +++ b/source/funkin/game/SplashHandler.hx @@ -1,6 +1,6 @@ package funkin.game; -import funkin.backend.scripting.events.SplashShowEvent; +import funkin.backend.scripting.events.splash.*; class SplashHandler extends FlxTypedGroup { /** @@ -42,13 +42,15 @@ class SplashHandler extends FlxTypedGroup { var __grp:SplashGroup; public function showSplash(name:String, strum:Strum) { __grp = getSplashGroup(name); + var event = EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp); - event = PlayState.instance.scripts.event("onSplashShown", event); + event = PlayState.instance.gameAndCharsEvent("onSplashShown", event); + if (!event.cancelled) add(event.splash); // max 8 rendered splashes - while(members.length > 8) + while(members.length > Flags.MAX_SPLASHES) remove(members[0], true); } } \ No newline at end of file diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index e3f0ad4c..7ce0f48c 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -8,8 +8,7 @@ import funkin.backend.utils.XMLUtil.XMLImportedScriptInfo; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; -import funkin.backend.scripting.events.StageNodeEvent; -import funkin.backend.scripting.events.StageXMLEvent; +import funkin.backend.scripting.events.stage.*; import funkin.backend.system.interfaces.IBeatReceiver; import haxe.io.Path; import haxe.xml.Access; @@ -106,8 +105,8 @@ class Stage extends FlxBasic implements IBeatReceiver { } if (PlayState.instance == state) { - event = EventManager.get(StageXMLEvent).recycle(this, xml, elems); - elems = PlayState.instance.scripts.event("onStageXMLParsed", event).elems; + event = EventManager.get(StageXMLEvent).recycle(this, stageXML, elems); + elems = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", event).elems; } if(onXMLLoaded != null) { elems = onXMLLoaded(xml, elems); @@ -163,7 +162,7 @@ class Stage extends FlxBasic implements IBeatReceiver { } if(PlayState.instance == state) { - sprite = PlayState.instance.scripts.event("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite; + sprite = PlayState.instance.gameAndCharsEvent("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite; } if(onNodeLoaded != null) { sprite = onNodeLoaded(node, sprite); @@ -213,7 +212,6 @@ class Stage extends FlxBasic implements IBeatReceiver { if(onXMLPostLoaded != null) { elems = onXMLPostLoaded(xml, elems); } - } public static function getDefaultPos(name:String):StageCharPosInfo { diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index d1b8fa1a..2477d60c 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -5,7 +5,7 @@ import flixel.sound.FlxSound; import flixel.tweens.FlxTween; import flixel.util.FlxSignal.FlxTypedSignal; import funkin.backend.chart.ChartData; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.note.*; import funkin.backend.system.Conductor; import funkin.backend.system.Controls; @@ -153,9 +153,11 @@ class StrumLine extends FlxTypedGroup { if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed; if(scrollSpeed == null) scrollSpeed = 1; - // TODOD: Make this work by accounting zoom and scrollspeed changes - Nex + // TODO: Make this work by accounting zoom and scrollspeed changes - Nex /*var limit = FlxG.height / 0.45; - notes.limit = limit / scrollSpeed;*/ + notes.limit = limit / scrollSpeed; + OR + notes.limit = Flags.DEFAULT_NOTE_MS_LIMIT / scrollSpeed;*/ } public override function update(elapsed:Float) { @@ -250,7 +252,8 @@ class StrumLine extends FlxTypedGroup { __justReleased.push(s.__getJustReleased(this)); } - var event = PlayState.instance.scripts.event("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id)); + var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id); + if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onInputUpdate", event); if (event.cancelled) return; __pressed = CoolUtil.getDefault(event.pressed, []); @@ -285,13 +288,14 @@ class StrumLine extends FlxTypedGroup { forEach(function(str:Strum) { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); }); - PlayState.instance.scripts.call("onPostInputUpdate"); + PlayState.instance.gameAndCharsCall("onPostInputUpdate"); } public inline function addHealth(health:Float) PlayState.instance.health += health * (opponentSide ? -1 : 1); - public inline function generateStrums(amount:Int = 4) { + public inline function generateStrums(amount:Int = -1) { + if(amount == -1) amount = Flags.DEFAULT_STRUM_AMOUNT; for (i in 0...amount) add(createStrum(i)); } @@ -319,7 +323,7 @@ class StrumLine extends FlxTypedGroup { 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 = PlayState.instance.scripts.event("onStrumCreation", event); + if (PlayState.instance != null) event = PlayState.instance.gameAndCharsEvent("onStrumCreation", event); if (!event.cancelled) { babyArrow.frames = Paths.getFrames(event.sprite); @@ -329,7 +333,7 @@ class StrumLine extends FlxTypedGroup { babyArrow.animation.addByPrefix('red', 'arrowRIGHT'); babyArrow.antialiasing = true; - babyArrow.setGraphicSize(Std.int((babyArrow.width * 0.7) * strumScale)); + babyArrow.setGraphicSize(Std.int((babyArrow.width * Flags.DEFAULT_NOTE_SCALE) * strumScale)); babyArrow.animation.addByPrefix('static', 'arrow${event.animPrefix.toUpperCase()}'); babyArrow.animation.addByPrefix('pressed', '${event.animPrefix} press', 24, false); @@ -350,7 +354,7 @@ class StrumLine extends FlxTypedGroup { insert(i, babyArrow); - PlayState.instance.scripts.event("onPostStrumCreation", event); + if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onPostStrumCreation", event); return babyArrow; } diff --git a/source/funkin/game/cutscenes/Cutscene.hx b/source/funkin/game/cutscenes/Cutscene.hx index cd572812..41f7e233 100644 --- a/source/funkin/game/cutscenes/Cutscene.hx +++ b/source/funkin/game/cutscenes/Cutscene.hx @@ -1,18 +1,77 @@ package funkin.game.cutscenes; +import funkin.backend.scripting.events.NameEvent; +import funkin.menus.PauseSubState; + /** * Substate made for cutscenes. */ class Cutscene extends MusicBeatSubstate { var __callback:Void->Void; + var __pausable:Bool; var game:PlayState = PlayState.instance; - public function new(callback:Void->Void) { - super(); - __callback = callback; + public var paused:Bool = false; + public var pauseItems:Array; + public var skippable(default, set):Bool; + + public function set_skippable(val:Bool):Bool { + if(!val) pauseItems.remove('Skip Cutscene'); + else if(!pauseItems.contains('Skip Cutscene')) pauseItems.insert(1, 'Skip Cutscene'); + return skippable = val; } + + public function new(callback:Void->Void, allowPause:Bool = true, canSkip:Bool = true) { + super(); + canOpenCustomTransition = true; + + __callback = callback; + __pausable = allowPause; + + pauseItems = Flags.DEFAULT_CUTSCENE_PAUSE_ITEMS; + skippable = canSkip; + } + public override function update(elapsed:Float) { super.update(elapsed); + if (pauseCheck()) pauseCutscene(); + } + + public function pauseCheck():Bool + return controls.PAUSE && __pausable; + + var _before:Array = [false, true]; + public function pauseCutscene() { + _before = [game.persistentUpdate, game.persistentDraw]; + game.persistentUpdate = persistentUpdate = false; + game.persistentDraw = persistentDraw = true; + paused = true; + + openSubState(new PauseSubState(pauseItems, selectPauseOption)); + } + + public override function closeSubState() { + if (paused) { + game.persistentUpdate = _before[0]; + game.persistentDraw = _before[1]; + paused = false; + } + + super.closeSubState(); + } + + public function onSkipCutscene(event:NameEvent) close(); + public function onRestartCutscene(event:NameEvent) event.name = "Restart Song"; // Making the whole PlayState restart just for precaution - Nex + public function onResumeCutscene(event:NameEvent) event.name = "Resume"; + + public function selectPauseOption(event:NameEvent) { + switch(event.name) { + case 'Skip Cutscene': onSkipCutscene(event); + case 'Restart Cutscene': onRestartCutscene(event); + case 'Resume Cutscene': onResumeCutscene(event); + } + + return true; } public override function close() { diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index b3b16c8e..d7ebd934 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -13,7 +13,7 @@ import haxe.xml.Access; /** * Substate made for dialogue cutscenes. To use it in a scripted cutscene, call `startDialogue`. */ -class DialogueCutscene extends Cutscene { +class DialogueCutscene extends ScriptedCutscene { public var dialoguePath:String; public var dialogueData:Access; @@ -28,23 +28,29 @@ class DialogueCutscene extends Cutscene { public var curMusic:FlxSound = null; public static var cutscene:DialogueCutscene; - public var dialogueScript:Script; + public var dialogueScript(get, set):Script; public function set_curLine(val:DialogueLine) { lastLine = curLine; return curLine = val; } + // Backwards comapt funcs - Nex + public function set_dialogueScript(val:Script) return script = val; + public function get_dialogueScript() return script; + public function new(dialoguePath:String, callback:Void->Void) { - super(callback); - this.dialoguePath = dialoguePath; + super(this.dialoguePath = dialoguePath, callback); + camera = dialogueCamera = new FlxCamera(); dialogueCamera.bgColor = 0; FlxG.cameras.add(dialogueCamera, false); - dialogueScript = Script.create(Paths.script(Path.withoutExtension(dialoguePath), null, dialoguePath.startsWith('assets'))); - dialogueScript.setParent(cutscene = this); - dialogueScript.load(); + cutscene = this; + } + + public override function onErrorScriptLoading() { + Logs.trace('Could not find script for dialogue cutscene at "${scriptPath}"', WARNING, YELLOW); } var parentDisabler:FunkinParentDisabler; @@ -89,7 +95,7 @@ class DialogueCutscene extends Cutscene { musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).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.music(node.att.nextSound)) : null, + nextSound: node.has.nextSound ? FlxG.sound.load(Paths.sound(node.att.nextSound)) : null, textSound: null }; @@ -110,22 +116,21 @@ class DialogueCutscene extends Cutscene { next(true); } catch(e) { - Logs.trace('Error while loading dialogue at ${dialoguePath}: ${e.toString()}', ERROR); - trace(CoolUtil.getLastExceptionStack()); + var message:String = e.toString(); + Logs.trace('Error while loading dialogue at ${dialoguePath}: $message', ERROR, RED); + dialogueScript.call("loadingError", [message]); close(); } + dialogueScript.call("postCreate"); } - public override function beatHit(curBeat:Int) { - super.beatHit(curBeat); - dialogueScript.call("beatHit", [curBeat]); + public override function pauseCheck():Bool { + return super.pauseCheck() && !controls.ACCEPT; // Avoids next() and pause() being called at the same time - Nex } public override function update(elapsed:Float) { super.update(elapsed); - dialogueScript.call("update", [elapsed]); - if(controls.ACCEPT) { if(dialogueBox.dialogueEnded) next(); else dialogueBox.text.skip(); @@ -138,8 +143,8 @@ class DialogueCutscene extends Cutscene { public var canProceed:Bool = true; public function next(playFirst:Bool = false) { - var event = EventManager.get(DynamicEvent).recycle(playFirst); - dialogueScript.call("next", [playFirst]); + var event = EventManager.get(DialogueNextLineEvent).recycle(playFirst); + dialogueScript.call("next", [event]); if(event.cancelled || !canProceed) return; if ((curLine = dialogueLines.shift()) == null) { @@ -154,7 +159,7 @@ class DialogueCutscene extends Cutscene { } if (curLine.callback != null) - dialogueScript.call(curLine.callback, [playFirst]); + dialogueScript.call(curLine.callback, [event.playFirst]); for (k=>c in charMap) if (k != curLine.char) @@ -167,8 +172,8 @@ class DialogueCutscene extends Cutscene { dialogueBox.popupChar(char, force); } - var finalSuffix:String = playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : ""; - dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !playFirst); + var finalSuffix:String = event.playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : ""; + dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !event.playFirst); if(curLine.playSound != null) curLine.playSound.play(); if(curLine.changeMusic != null) { @@ -178,7 +183,7 @@ class DialogueCutscene extends Cutscene { curMusic.fadeIn(1, 0, curMusic.volume); } else if(curLine.musicVolume != null && curMusic != null) curMusic.volume = curLine.musicVolume; - dialogueScript.call("postNext", [playFirst]); + dialogueScript.call("postNext", [event]); } public override function close() { @@ -193,8 +198,6 @@ class DialogueCutscene extends Cutscene { public override function destroy() { if(curMusic != null && !curMusic.persist) curMusic.destroy(); - dialogueScript.call("destroy"); - dialogueScript.destroy(); super.destroy(); cutscene = null; @@ -215,4 +218,4 @@ typedef DialogueLine = { var nextSound:FlxSound; var textSound:FlxSound; var changeDefAnim:String; -} \ No newline at end of file +} diff --git a/source/funkin/game/cutscenes/ScriptedCutscene.hx b/source/funkin/game/cutscenes/ScriptedCutscene.hx index b305cca0..20a01aa2 100644 --- a/source/funkin/game/cutscenes/ScriptedCutscene.hx +++ b/source/funkin/game/cutscenes/ScriptedCutscene.hx @@ -1,20 +1,25 @@ package funkin.game.cutscenes; +import haxe.io.Path; +import flixel.FlxSubState; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.NameEvent; +import funkin.backend.scripting.events.StateEvent; /** * Substate made for scripted cutscenes. * To add cutscenes to your songs, add a `cutscene.hx` file in your song's directory (ex: `songs/song/cutscene.hx`) */ class ScriptedCutscene extends Cutscene { + var scriptPath:String; var script:Script; - var scriptPath:String; public function new(scriptPath:String, callback:Void->Void) { super(callback); - script = Script.create(this.scriptPath = scriptPath); + script = Script.create(this.scriptPath = Paths.script(Path.withoutExtension(scriptPath), null, scriptPath.startsWith('assets'))); script.setPublicMap(PlayState.instance.scripts.publicVariables); script.setParent(this); script.load(); @@ -22,32 +27,91 @@ class ScriptedCutscene extends Cutscene { public override function create() { super.create(); - trace("fuck you"); script.call("create"); - if (Std.isOfType(script, DummyScript)) { - Logs.trace('Could not find script for scripted cutscene at ${scriptPath}', ERROR, RED); - close(); - } + + if(Std.isOfType(script, DummyScript)) + onErrorScriptLoading(); } - public override function update(elapsed:Float) { + public function onErrorScriptLoading() { + Logs.trace('Could not find script for scripted cutscene at "${scriptPath}"', ERROR, RED); + close(); + } + + public override function pauseCheck():Bool + { + var shouldClose = super.pauseCheck(); + script.call("pauseCheck", [shouldClose]); + return shouldClose; + } + + public override function update(elapsed:Float) + { script.call("update", [elapsed]); super.update(elapsed); script.call("postUpdate", [elapsed]); } - public override function beatHit(curBeat:Int) { + public override function pauseCutscene() + { + var event = new CancellableEvent(); + script.call("pauseCutscene", [event]); + if(!event.cancelled) super.pauseCutscene(); + } + + public override function onSkipCutscene(event:NameEvent) + { + script.call("onSkipCutscene", [event]); + if(!event.cancelled) super.onSkipCutscene(event); + } + + public override function onRestartCutscene(event:NameEvent) + { + script.call("onRestartCutscene", [event]); + if(!event.cancelled) super.onRestartCutscene(event); + } + + public override function onResumeCutscene(event:NameEvent) + { + script.call("onResumeCutscene", [event]); + if(!event.cancelled) super.onResumeCutscene(event); + } + + public override function measureHit(curMeasure:Int) + { + super.measureHit(curMeasure); + script.call("measureHit", [curMeasure]); + } + + public override function beatHit(curBeat:Int) + { super.beatHit(curBeat); script.call("beatHit", [curBeat]); } - public override function stepHit(curStep:Int) { + public override function stepHit(curStep:Int) + { super.stepHit(curStep); script.call("stepHit", [curStep]); } + public override function openSubState(sub:FlxSubState) + { + var event = EventManager.get(StateEvent).recycle(sub); + script.call("onSubstateClose", [event]); + if(!event.cancelled) super.openSubState(event.substate is FlxSubState ? cast event.substate : sub); + } + + public override function closeSubState() + { + var event = EventManager.get(StateEvent).recycle(subState); + script.call("onSubstateOpen", [event]); + if(!event.cancelled) super.closeSubState(); + } + public override function destroy() { script.call("destroy"); + script.destroy(); super.destroy(); } diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx index f12210eb..9216c340 100644 --- a/source/funkin/game/cutscenes/VideoCutscene.hx +++ b/source/funkin/game/cutscenes/VideoCutscene.hx @@ -1,5 +1,6 @@ package funkin.game.cutscenes; +import flixel.tweens.FlxTween; import flixel.addons.display.FlxBackdrop; import flixel.util.FlxColor; import flixel.util.FlxTimer; @@ -12,7 +13,7 @@ import haxe.xml.Access; import sys.io.File; #end #if VIDEO_CUTSCENES -import hxvlc.flixel.FlxVideo; +import hxvlc.flixel.FlxVideoSprite; #end /** @@ -24,22 +25,24 @@ class VideoCutscene extends Cutscene { var localPath:String; #if VIDEO_CUTSCENES - var video:FlxVideo; - public var skippable:Bool = true; - #end + var video:FlxVideoSprite; + final mutex = new sys.thread.Mutex(); + var cutsceneCamera:FlxCamera; var text:FunkinText; var loadingBackdrop:FlxBackdrop; - var videoReady:Bool = false; + private var __loaded:Bool = false; var bg:FlxSprite; var subtitle:FunkinText; public var subtitles:Array = []; + var curSubtitle:Int = 0; + #end public function new(path:String, callback:Void->Void) { - super(callback); + super(callback, false); localPath = Assets.getPath(this.path = path); } @@ -48,21 +51,17 @@ class VideoCutscene extends Cutscene { // TODO: get vlc to stop autoloading those goddamn subtitles (use different file ext??) + #if VIDEO_CUTSCENES cutsceneCamera = new FlxCamera(); cutsceneCamera.bgColor = 0; FlxG.cameras.add(cutsceneCamera, false); - #if VIDEO_CUTSCENES parseSubtitles(); - video = new FlxVideo(); - video.onEndReached.add(function() - { - video.dispose(); - FlxG.removeChild(video); - }); - video.onEndReached.add(close); - FlxG.addChildBelowMouse(video); + add(video = new FlxVideoSprite()); + video.antialiasing = true; + video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex + video.bitmap.onEndReached.add(close); //cover = new FlxSprite(0, FlxG.height * 0.85).makeSolid(FlxG.width + 50, FlxG.height + 50, 0xFF000000); //cover.scrollFactor.set(0, 0); @@ -77,41 +76,44 @@ class VideoCutscene extends Cutscene { subtitle.alignment = CENTER; subtitle.visible = false; - if (localPath.startsWith("[ZIP]")) { - text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video..."); - text.cameras = [cutsceneCamera]; - text.visible = false; - @:privateAccess - text.regenGraphic(); - add(text); + text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video..."); + text.cameras = [cutsceneCamera]; + text.visible = false; + @:privateAccess + text.regenGraphic(); + add(text); - loadingBackdrop = new FlxBackdrop(text.graphic, X); - loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height; - loadingBackdrop.cameras = [cutsceneCamera]; - add(loadingBackdrop); + loadingBackdrop = new FlxBackdrop(text.graphic, X); + loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height; + loadingBackdrop.velocity.x = 70; + loadingBackdrop.cameras = [cutsceneCamera]; + add(loadingBackdrop); - // ZIP PATH: EXPORT - // TODO: this but better and more ram friendly - localPath = './.temp/video-${curVideo++}.mp4'; - Main.execAsync(function() { + loadingBackdrop.alpha = 0; + FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut}); + + Main.execAsync(function() { + if (localPath.startsWith("[ZIP]")) { + // ZIP PATH: EXPORT + // TODO: this but better and more ram friendly + localPath = './.temp/video-${curVideo++}.mp4'; File.saveBytes(localPath, Assets.getBytes(path)); - videoReady = true; - }); - } else { - if (video.load(localPath)) - new FlxTimer().start(0.001, function(tmr:FlxTimer) { - video.play(); - }); - else - close(); - } + } + + if(video.load(localPath)) new FlxTimer().start(0.001, function(_) {mutex.acquire(); onReady(); mutex.release();}); + else { mutex.acquire(); close(); mutex.release(); } + }); + add(bg); add(subtitle); - #end cameras = [cutsceneCamera]; + #else + close(); + #end } + #if VIDEO_CUTSCENES public function parseSubtitles() { var subtitlesPath = '${Path.withoutExtension(path)}.srt'; @@ -141,27 +143,23 @@ class VideoCutscene extends Cutscene { } if (subtitleText.length <= 0) continue; // empty subtitle, skipping var lastSub = subtitles.last(); - if (lastSub != null && lastSub.subtitle == "" && lastSub.time > beginTime) + if (lastSub != null && lastSub.subtitle == "" && lastSub.time >= beginTime) subtitles.pop(); // remove last subtitle auto reset to prevent bugs subtitles.push({ - subtitle: subtitleText.join("."), - time: beginTime * 1000, - color: 0xFFFFFFFF // todo + subtitle: subtitleText.join(""), + time: beginTime * 1000 }); subtitles.push({ subtitle: "", - time: endTime * 1000, - color: 0xFFFFFFFF + time: endTime * 1000 }); } } - - trace(subtitles); } public static function splitTime(str:String):Float { if (str == null || str.trim() == "") return -1; - var multipliers:Array = [1, 60, 3600, 86400]; // no way a cutscene will last longer than days + var multipliers:Array = [1, 60, 3600, 86400, 31536000]; // no way a cutscene will last longer than years var timeSplit:Array> = [for(e in str.split(":")) Std.parseFloat(e.replace(",", "."))]; var time:Float = 0; @@ -173,50 +171,62 @@ class VideoCutscene extends Cutscene { return time; } + public inline function onReady() { + FlxTween.cancelTweensOf(loadingBackdrop); + FlxTween.tween(loadingBackdrop, {alpha: 0}, 0.7, {ease: FlxEase.sineInOut, onComplete: function(_) { + loadingBackdrop.destroy(); + text.destroy(); + }}); + + __pausable = __loaded = true; + video.play(); + } + public override function update(elapsed:Float) { super.update(elapsed); - #if VIDEO_CUTSCENES - if (videoReady) { - videoReady = false; - if (video.load(localPath)) - new FlxTimer().start(0.001, function(tmr:FlxTimer) { - video.play(); - }); - else - { - close(); + if(loadingBackdrop != null) return; - if (loadingBackdrop != null) - loadingBackdrop.visible = false; - - return; - } - - if (loadingBackdrop != null) - loadingBackdrop.visible = false; - } @:privateAccess - var time:Int64 = video.time; + var time:Int64 = video.bitmap.time; + var time:Float = FPHelper.i64ToDouble(time.low, time.high); - while (subtitles.length > 0 && subtitles[0].time < Math.round(FPHelper.i64ToDouble(time.low, time.high))) - setSubtitle(subtitles.shift()); + if(curSubtitle < subtitles.length && subtitles[curSubtitle].time < time) { + setSubtitle(subtitles[curSubtitle]); + curSubtitle++; + } + } - if (loadingBackdrop != null) { - loadingBackdrop.x -= elapsed * FlxG.width * 0.5; - } - if (skippable && video.isPlaying && controls.ACCEPT) { - video.onEndReached.dispatch(); - } - #else - close(); - #end + @:dox(hide) override public function onFocus() { + if(FlxG.autoPause && !paused) video.resume(); + super.onFocus(); + } + + @:dox(hide) override public function onFocusLost() { + if(FlxG.autoPause && !paused) video.pause(); + super.onFocusLost(); + } + + public override function pauseCutscene() { + video.pause(); + super.pauseCutscene(); + } + + public override function onResumeCutscene(event) { + video.resume(); + super.onResumeCutscene(event); + } + + public override function onRestartCutscene(event) { + closeSubState(); + video.bitmap.position = curSubtitle = 0; + setSubtitle({time: -1, subtitle: ""}); + video.resume(); } public function setSubtitle(sub:CutsceneSubtitle) { if (bg.visible = subtitle.visible = (sub.subtitle.length > 0)) { subtitle.text = sub.subtitle; - subtitle.color = sub.color; subtitle.screenCenter(X); bg.scale.set(subtitle.width + 8, subtitle.height + 8); bg.updateHitbox(); @@ -228,10 +238,10 @@ class VideoCutscene extends Cutscene { FlxG.cameras.remove(cutsceneCamera, true); super.destroy(); } + #end } typedef CutsceneSubtitle = { var time:Float; // time in ms var subtitle:String; // subtitle text - var color:FlxColor; } diff --git a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx index a948344d..b445b611 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx @@ -3,8 +3,7 @@ package funkin.game.cutscenes.dialogue; import flixel.addons.text.FlxTypeText; import flixel.sound.FlxSound; import funkin.backend.scripting.Script; -import funkin.backend.scripting.events.PlayAnimContext; -import funkin.backend.scripting.events.PlayAnimEvent; +import funkin.backend.scripting.events.sprite.*; import funkin.backend.scripting.events.dialogue.*; import haxe.xml.Access; @@ -87,8 +86,11 @@ class DialogueBox extends FunkinSprite { } } catch(e) { active = false; - Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR); + var message:String = e.toString(); + Logs.trace('Couldn\'t load dialogue box "$name": $message', ERROR, RED); + dialogueBoxScript.call("loadingError", [message]); } + defaultTextTypeSFX = [FlxG.sound.load(textTypeSFX)]; FlxG.sound.cache(nextSFX); dialogueBoxScript.call("postCreate"); diff --git a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx index d53bcbd9..2c3eec13 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx @@ -2,8 +2,7 @@ package funkin.game.cutscenes.dialogue; import flixel.tweens.FlxTween; import funkin.backend.scripting.Script; -import funkin.backend.scripting.events.PlayAnimContext; -import funkin.backend.scripting.events.PlayAnimEvent; +import funkin.backend.scripting.events.sprite.*; import funkin.backend.scripting.events.dialogue.*; import haxe.xml.Access; @@ -54,9 +53,10 @@ class DialogueCharacter extends FunkinSprite { x = 0; y = 0; } catch(e) { var message:String = e.toString(); - Logs.trace('Failed to load dialogue character $name: ${message}', ERROR); + Logs.trace('Failed to load dialogue character $name: ${message}', ERROR, RED); dialogueCharScript.call("loadingError", [message]); } + visible = false; dialogueCharScript.call("postCreate"); } diff --git a/source/funkin/menus/BetaWarningState.hx b/source/funkin/menus/BetaWarningState.hx index d436138f..30e3c4fd 100644 --- a/source/funkin/menus/BetaWarningState.hx +++ b/source/funkin/menus/BetaWarningState.hx @@ -19,7 +19,7 @@ class BetaWarningState extends MusicBeatState { disclaimer = new FunkinText(16, titleAlphabet.y + titleAlphabet.height + 10, FlxG.width - 32, "", 32); disclaimer.alignment = CENTER; - disclaimer.applyMarkup('This engine is still in a *${Main.releaseCycle}* 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', + 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), "*") ] diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index c1670dfd..afb142ea 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -4,9 +4,10 @@ import flixel.text.FlxText; import flixel.util.FlxColor; import funkin.backend.chart.Chart; import funkin.backend.chart.ChartData.ChartMetaData; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.menu.MenuChangeEvent; +import funkin.backend.scripting.events.menu.freeplay.*; import funkin.game.HealthIcon; -import funkin.savedata.FunkinSave; +import funkin.backend.utils.FunkinSave; using StringTools; @@ -458,7 +459,7 @@ class FreeplaySonglist { if (songsFound.length > 0) { for(s in songsFound) - songs.push(Chart.loadChartMeta(s, "normal", source == MODS)); + songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS)); return false; } return true; diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index cd40bc90..5f580868 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -4,7 +4,8 @@ import flixel.FlxState; import flixel.effects.FlxFlicker; import flixel.tweens.FlxTween; import funkin.backend.FunkinText; -import funkin.backend.scripting.events.*; +import funkin.backend.scripting.events.menu.MenuChangeEvent; +import funkin.backend.scripting.events.NameEvent; import funkin.menus.credits.CreditsMain; import funkin.options.OptionsMenu; import lime.app.Application; @@ -24,7 +25,7 @@ class MainMenuState extends MusicBeatState var camFollow:FlxObject; var versionText:FunkinText; - public var canAccessDebugMenus:Bool = true; + public var canAccessDebugMenus:Bool = Flags.DEFAULT_CAN_ACCESS_DEBUG_MENUS; override function create() { @@ -72,7 +73,7 @@ class MainMenuState extends MusicBeatState FlxG.camera.follow(camFollow, null, 0.06); - versionText = new FunkinText(5, FlxG.height - 2, 0, 'Codename Engine v${Application.current.meta.get('version')}\nCommit ${funkin.backend.system.macros.GitCommitMacro.commitNumber} (${funkin.backend.system.macros.GitCommitMacro.commitHash})\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n'); + versionText = new FunkinText(5, FlxG.height - 2, 0, '${Flags.VERSION_MESSAGE}\n${Flags.COMMIT_MESSAGE}\n[${controls.getKeyName(SWITCHMOD)}] Open Mods menu\n'); versionText.y -= versionText.height; versionText.scrollFactor.set(); add(versionText); @@ -158,7 +159,7 @@ class MainMenuState extends MusicBeatState var event = event("onSelectItem", EventManager.get(NameEvent).recycle(daChoice)); if (event.cancelled) return; - switch (daChoice) + switch (event.name) { case 'story mode': FlxG.switchState(new StoryMenuState()); case 'freeplay': FlxG.switchState(new FreeplayState()); diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index 7101486c..1ab70f33 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -5,15 +5,16 @@ import flixel.text.FlxText; import flixel.tweens.FlxTween; import flixel.util.FlxColor; import funkin.backend.scripting.Script; -import funkin.backend.scripting.events.MenuChangeEvent; +import funkin.backend.scripting.events.menu.MenuChangeEvent; +import funkin.backend.scripting.events.menu.pause.*; import funkin.backend.scripting.events.NameEvent; -import funkin.backend.scripting.events.PauseCreationEvent; import funkin.backend.system.Conductor; import funkin.backend.utils.FunkinParentDisabler; import funkin.editors.charter.Charter; import funkin.menus.StoryMenuState; import funkin.options.OptionsMenu; import funkin.options.keybinds.KeybindsOptions; +import funkin.options.TreeMenu; class PauseSubState extends MusicBeatSubstate { @@ -21,19 +22,22 @@ class PauseSubState extends MusicBeatSubstate var grpMenuShit:FlxTypedGroup; - var menuItems:Array = ['Resume', 'Restart Song', 'Change Controls', 'Change Options', 'Exit to menu', "Exit to charter"]; + var menuItems:Array; var curSelected:Int = 0; var pauseMusic:FlxSound; public var pauseScript:Script; + public var selectCall:NameEvent->Void; // Mainly for extern stuff that aren't scripts - Nex public var game:PlayState = PlayState.instance; // shortcut private var __cancelDefault:Bool = false; - public function new(x:Float = 0, y:Float = 0) { + public function new(?items:Array, ?selectCall:NameEvent->Void) { super(); + menuItems = items != null ? items : Flags.DEFAULT_PAUSE_ITEMS; + this.selectCall = selectCall; } var parentDisabler:FunkinParentDisabler; @@ -135,13 +139,11 @@ class PauseSubState extends MusicBeatSubstate public function selectOption() { var event = EventManager.get(NameEvent).recycle(menuItems[curSelected]); + if (selectCall != null) selectCall(event); pauseScript.call("onSelectOption", [event]); - if (event.cancelled) return; - var daSelected:String = event.name; - - switch (daSelected) + switch (event.name) { case "Resume": close(); @@ -153,9 +155,10 @@ class PauseSubState extends MusicBeatSubstate persistentDraw = false; openSubState(new KeybindsOptions()); case "Change Options": + TreeMenu.lastState = PlayState; FlxG.switchState(new OptionsMenu()); case "Exit to charter": - FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false)); + FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false)); case "Exit to menu": if (PlayState.chartingMode && Charter.undos.unsaved) game.saveWarn(false); @@ -206,4 +209,4 @@ class PauseSubState extends MusicBeatSubstate item.alpha = 0.6; } } -} \ No newline at end of file +} diff --git a/source/funkin/menus/StoryMenuState.hx b/source/funkin/menus/StoryMenuState.hx index 1ed7fa5b..8cc0c431 100644 --- a/source/funkin/menus/StoryMenuState.hx +++ b/source/funkin/menus/StoryMenuState.hx @@ -6,15 +6,20 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; import funkin.backend.FunkinText; -import funkin.backend.scripting.events.*; -import funkin.savedata.FunkinSave; +import funkin.backend.scripting.events.menu.MenuChangeEvent; +import funkin.backend.scripting.events.menu.storymenu.*; +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.utils.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 = []; + // yes it supports parameters - Nex + public var scoreMessage:String = "WEEK SCORE:{0}"; + public var scoreText:FlxText; public var tracklist:FlxText; public var weekTitle:FlxText; @@ -23,18 +28,21 @@ class StoryMenuState extends MusicBeatState { public var curWeek:Int = 0; public var difficultySprites:Map = []; - public var weekBG:FlxSprite; public var leftArrow:FlxSprite; public var rightArrow:FlxSprite; public var blackBar:FlxSprite; + public var weekBG:FlxSprite; + public var defColor:String = "#F9CF51"; + public var interpColor:FlxInterpolateColor; + public var lerpScore:Float = 0; public var intendedScore:Int = 0; public var canSelect:Bool = true; public var weekSprites:FlxTypedGroup; - public var characterSprites:FlxTypedGroup; + public var characterSprites:FlxTypedGroup; //public var charFrames:Map = []; @@ -56,7 +64,7 @@ class StoryMenuState extends MusicBeatState { weekTitle.alpha = 0.7; weekBG = new FlxSprite(0, 56).makeSolid(FlxG.width, 400, 0xFFFFFFFF); - weekBG.color = 0xFFF9CF51; + weekBG.color = weeks.length > 0 ? weeks[0].bgColor : FlxColor.fromString(defColor); weekBG.updateHitbox(); weekSprites = new FlxTypedGroup(); @@ -85,15 +93,12 @@ class StoryMenuState extends MusicBeatState { add(weekSprites); for(e in [blackBar, scoreText, weekTitle, weekBG, tracklist]) { + e.antialiasing = true; e.scrollFactor.set(); add(e); } - characterSprites = new FlxTypedGroup(); - for(i in 0...3) { - characterSprites.add(new MenuCharacterSprite(i)); - } - add(characterSprites); + add(characterSprites = new FlxTypedGroup()); for(i=>week in weeks) { var spr:MenuItem = new MenuItem(0, (i * 120) + 480, 'menus/storymenu/weeks/${week.sprite}'); @@ -114,6 +119,14 @@ class StoryMenuState extends MusicBeatState { } } + interpColor = new FlxInterpolateColor(weekBG.color); + + // default difficulty should be the middle difficulty in the array + // to be consistent with base game and whatnot, you know the drill + curDifficulty = Math.floor(weeks[0].difficulties.length * 0.5); + // debug stuff lol + Logs.trace('Middle Difficulty for Week 1 is ${weeks[0].difficulties[curDifficulty]} (ID: $curDifficulty)'); + changeWeek(0, true); DiscordUtil.call("onMenuLoaded", ["Story Menu"]); @@ -125,7 +138,7 @@ class StoryMenuState extends MusicBeatState { super.update(elapsed); lerpScore = lerp(lerpScore, intendedScore, 0.5); - scoreText.text = 'WEEK SCORE:${Math.round(lerpScore)}'; + scoreText.text = scoreMessage.replace("{0}", Std.string(Math.round(lerpScore))); if (canSelect) { if (leftArrow != null && leftArrow.exists) leftArrow.animation.play(controls.LEFT ? 'press' : 'idle'); @@ -145,6 +158,9 @@ class StoryMenuState extends MusicBeatState { if (e != null && e.exists) e.animation.play('idle'); } + + interpColor.fpsLerpTo(weeks[curWeek].bgColor, 0.0625); + weekBG.color = interpColor.color; } public function goBack() { @@ -163,18 +179,43 @@ class StoryMenuState extends MusicBeatState { if (!force) CoolUtil.playMenuSFX(); for(k=>e in weekSprites.members) { 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.toUpperCase()].join('\n')}'; + tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.name].join('\n')}'; weekTitle.text = weeks[curWeek].name.getDefault(""); - for(i in 0...3) - characterSprites.members[i].changeCharacter(characters[weeks[curWeek].chars[i]]); + 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); + } changeDifficulty(0, true); 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; @@ -238,7 +279,8 @@ class StoryMenuState extends MusicBeatState { sprite: week.getAtt('sprite').getDefault(weekName), chars: [null, null, null], songs: [], - difficulties: ['easy', 'normal', 'hard'] + difficulties: ['easy', 'normal', 'hard'], + bgColor: FlxColor.fromString(week.getAtt("bgColor").getDefault(defColor)) }; var diffNodes = week.nodes.difficulty; @@ -292,19 +334,14 @@ class StoryMenuState extends MusicBeatState { } catch(e) { Logs.trace('Story Menu: Cannot parse character "$charName.xml": ${Std.string(e)}`', ERROR); } - if (char == null) return; - if (characters[charName] != null) return; - var charObj:MenuCharacter = { - spritePath: Paths.image(char.getAtt('sprite').getDefault('menus/storymenu/characters/${charName}')), - scale: Std.parseFloat(char.getAtt('scale')).getDefault(1), - xml: char, - offset: FlxPoint.get( - Std.parseFloat(char.getAtt('x')).getDefault(0), - Std.parseFloat(char.getAtt('y')).getDefault(0) - ) - }; - characters[charName] = charObj; + 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; + } } public function getWeeksFromSource(weeks:Array, source:funkin.backend.assets.AssetsLibraryList.AssetSource) { @@ -325,13 +362,6 @@ class StoryMenuState extends MusicBeatState { return true; } - public override function destroy() { - super.destroy(); - for(e in characters) - if (e != null && e.offset != null) - e.offset.put(); - } - public function selectWeek() { var event = event("onWeekSelect", EventManager.get(WeekSelectEvent).recycle(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty], curWeek, curDifficulty)); if (event.cancelled) return; @@ -339,17 +369,16 @@ class StoryMenuState extends MusicBeatState { canSelect = false; CoolUtil.playMenuSFX(CONFIRM); - for(char in characterSprites) - if (char.animation.exists("confirm")) - char.animation.play("confirm"); + if(characterSprites != null) + characterSprites.forEachAlive(function(spr) spr.playAnim("confirm", true, LOCK)); - PlayState.loadWeek(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty]); + PlayState.loadWeek(event.week, event.difficulty); new FlxTimer().start(1, function(tmr:FlxTimer) { FlxG.switchState(new PlayState()); }); - weekSprites.members[curWeek].startFlashing(); + weekSprites.members[event.weekID].startFlashing(); } } @@ -360,6 +389,7 @@ typedef WeekData = { var chars:Array; var songs:Array; var difficulties:Array; + var bgColor:FlxColor; } typedef WeekSong = { @@ -367,52 +397,6 @@ typedef WeekSong = { var hide:Bool; } -typedef MenuCharacter = { - var spritePath:String; - var xml:Access; - var scale:Float; - var offset:FlxPoint; - // var frames:FlxFramesCollection; -} - -class MenuCharacterSprite extends FlxSprite -{ - public var character:String; - - var pos:Int; - - public function new(pos:Int) { - super(0, 70); - this.pos = pos; - visible = false; - antialiasing = true; - } - - public var oldChar:MenuCharacter = null; - - public function changeCharacter(data:MenuCharacter) { - visible = (data != null); - if (!visible) - return; - - if (oldChar != (oldChar = data)) { - CoolUtil.loadAnimatedGraphic(this, data.spritePath); - for(e in data.xml.nodes.anim) { - if (e.getAtt("name") == "idle") - animation.remove("idle"); - - XMLUtil.addXMLAnimation(this, e); - } - animation.play("idle"); - scale.set(data.scale, data.scale); - updateHitbox(); - offset.x += data.offset.x; - offset.y += data.offset.y; - - x = (FlxG.width * 0.25) * (1 + pos) - 150; - } - } -} class MenuItem extends FlxSprite { public var targetY:Float = 0; diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx index e1245a1e..a4a42385 100644 --- a/source/funkin/menus/TitleState.hx +++ b/source/funkin/menus/TitleState.hx @@ -25,8 +25,6 @@ class TitleState extends MusicBeatState public var textGroup:FlxGroup; public var ngSpr:FlxSprite; - public var wackyImage:FlxSprite; - override public function create():Void { curWacky = FlxG.random.getObject(getIntroTextShit()); @@ -40,9 +38,6 @@ class TitleState extends MusicBeatState DiscordUtil.call("onMenuLoaded", ["Title Screen"]); } - var logoBl:FlxSprite; - var gfDance:FlxSprite; - var danceLeft:Bool = false; var titleText:FlxSprite; var titleScreenSprites:MusicBeatGroup; @@ -56,11 +51,9 @@ class TitleState extends MusicBeatState var bg:FlxSprite = new FlxSprite().makeSolid(FlxG.width, FlxG.height, FlxColor.BLACK); add(bg); - #if TITLESCREEN_XML titleScreenSprites = new MusicBeatGroup(); add(titleScreenSprites); loadXML(); - #end if (titleText == null) { titleText = new FlxSprite(100, FlxG.height * 0.8); @@ -78,16 +71,6 @@ class TitleState extends MusicBeatState blackScreen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); add(blackScreen); - #if !TITLESCREEN_XML - ngSpr = new FlxSprite(0, FlxG.height * 0.52).loadAnimatedGraphic(Paths.image('newgrounds_logo')); - add(ngSpr); - ngSpr.visible = false; - ngSpr.setGraphicSize(Std.int(ngSpr.width * 0.8)); - ngSpr.updateHitbox(); - ngSpr.screenCenter(X); - ngSpr.antialiasing = true; - #end - FlxG.mouse.visible = false; if (initialized) @@ -217,7 +200,6 @@ class TitleState extends MusicBeatState { super.beatHit(curBeat); - #if TITLESCREEN_XML if (curBeat >= titleLength || skippedIntro) { if (!skippedIntro) skipIntro(); return; @@ -225,27 +207,8 @@ class TitleState extends MusicBeatState var introText = titleLines[curBeat]; if (introText != null) introText.show(); - #else - switch (curBeat) - { - case 1: createCoolText(['ninjamuffin99', 'phantomArcade', 'kawaisprite', 'evilsk8er']); - case 3: addMoreText('present'); - case 4: deleteCoolText(); - case 5: createCoolText(['In association', 'with']); - case 7: addMoreText('newgrounds'); ngSpr.visible = true; - case 8: deleteCoolText(); ngSpr.visible = false; - case 9: createCoolText([curWacky[0]]); - case 11: addMoreText(curWacky[1]); - case 12: deleteCoolText(); - case 13: addMoreText('Friday'); - case 14: addMoreText('Night'); - case 15: addMoreText('Funkin'); - case 16: skipIntro(); - } - #end } - #if TITLESCREEN_XML public var xml:Access; public var titleLength:Int = 16; public var titleLines:Map = [ @@ -267,12 +230,14 @@ class TitleState extends MusicBeatState 15 => new IntroText(['Friday', 'Night', "Funkin'"]), ]; + public var titleSprites:Map = []; + public function loadXML() { try { xml = new Access(Xml.parse(Assets.getText(Paths.xml('titlescreen/titlescreen'))).firstElement()); if (xml.hasNode.intro) { titleLines = []; - if (xml.node.intro.has.length) titleLength = CoolUtil.getDefault(Std.parseInt(xml.node.intro.att.length), 16); + if (xml.node.intro.has.length) titleLength = Std.parseInt(xml.node.intro.att.length).getDefault(16); for(node in xml.nodes.sprites) { var parentFolder:String = node.getAtt("folder").getDefault(""); if (parentFolder != "" && !parentFolder.endsWith("/")) parentFolder += "/"; @@ -284,26 +249,28 @@ class TitleState extends MusicBeatState default: titleScreenSprites.add(spr); } + if(node.has.name) titleSprites[node.att.name] = spr; } } for(text in xml.node.intro.nodes.text) { - var beat:Int = CoolUtil.getDefault(text.has.beat ? Std.parseInt(text.att.beat) : null, 0); + var beat:Int = text.has.beat ? Std.parseInt(text.att.beat).getDefault(0) : 0; var texts:Array> = []; - for(e in text.elements) { - switch(e.name) { + for(node in text.elements) { + switch(node.name) { case "line": - if (!e.has.text) continue; - texts.push(e.att.text); + if (!node.has.text) continue; + texts.push(node.att.text); case "introtext": - if (!e.has.line) continue; - texts.push('{introText${e.att.line}}'); + if (!node.has.line) continue; + texts.push('{introText${node.att.line}}'); case "sprite": - if (!e.has.path) continue; - var name:String = e.has.name ? e.att.name : null; - var path:String = e.att.path; - var flipX:Bool = e.has.flipX ? e.att.flipX == "true" : false; - var flipY:Bool = e.has.flipY ? e.att.flipY == "true" : false; - var scale:Float = e.has.scale ? CoolUtil.getDefault(Std.parseFloat(e.att.scale), 1) : 1; + if (!node.has.path) continue; + var name:String = node.has.name ? node.att.name : null; + var path:String = node.att.path; + var flipX:Bool = node.has.flipX ? node.att.flipX == "true" : false; + var flipY:Bool = node.has.flipY ? node.att.flipY == "true" : false; + var scale:Float = node.has.scale ? Std.parseFloat(node.att.scale).getDefault(1) : 1; + texts.push({ name: name, path: path, @@ -320,7 +287,6 @@ class TitleState extends MusicBeatState Logs.trace('Failed to load titlescreen XML: $e', ERROR); } } - #end var skippedIntro:Bool = false; @@ -328,10 +294,6 @@ class TitleState extends MusicBeatState { if (!skippedIntro) { - #if !TITLESCREEN_XML - remove(ngSpr); - #end - FlxG.camera.flash(FlxColor.WHITE, 4); remove(blackScreen); blackScreen.destroy(); @@ -361,7 +323,7 @@ class IntroText { var image:TitleStateImage = e; if (image.path == null) continue; - var scale:Float = CoolUtil.getDefault(image.scale, 1); + var scale:Float = image.scale.getDefault(1); var yPos:Float = 200; if(state.textGroup.members.length > 0) { @@ -370,8 +332,8 @@ class IntroText { } var sprite = new FlxSprite(0, yPos).loadAnimatedGraphic(Paths.image(image.path)); - sprite.flipX = CoolUtil.getDefault(image.flipX, false); - sprite.flipY = CoolUtil.getDefault(image.flipY, false); + sprite.flipX = image.flipX.getDefault(false); + sprite.flipY = image.flipY.getDefault(false); sprite.scale.set(scale, scale); sprite.updateHitbox(); sprite.screenCenter(X); diff --git a/source/funkin/menus/credits/CreditsCodename.hx b/source/funkin/menus/credits/CreditsCodename.hx index 8ed343b6..1ee47d65 100644 --- a/source/funkin/menus/credits/CreditsCodename.hx +++ b/source/funkin/menus/credits/CreditsCodename.hx @@ -10,11 +10,7 @@ using StringTools; class CreditsCodename extends funkin.options.OptionsScreen { public var error:Bool = false; - public var author:String = "FNF-CNE-Devs"; public var totalContributions:Int = 0; - - public var mainDevCol:FlxColor = 0xFF9C35D5; - public var minContrCol:FlxColor = 0xFFB4A7DA; public var contribFormats:Array = []; public override function new() @@ -61,8 +57,8 @@ class CreditsCodename extends funkin.options.OptionsScreen { var text:String = parent.treeParent.pathDesc.text; parent.treeParent.pathDesc.text = ""; parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [ - new FlxTextFormatMarkerPair(new FlxTextFormat(mainDevCol), '*'), - new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(minContrCol, mainDevCol, Options.contributors[curSelected].contributions / totalContributions)), '~') + new FlxTextFormatMarkerPair(new FlxTextFormat(Flags.MAIN_DEVS_COLOR), '*'), + new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(Flags.MIN_CONTRIBUTIONS_COLOR, Flags.MAIN_DEVS_COLOR, Options.contributors[curSelected].contributions / totalContributions)), '~') ]); } @@ -77,7 +73,7 @@ class CreditsCodename extends funkin.options.OptionsScreen { Options.lastUpdated = curTime; error = false; - var idk = GitHub.getContributors(author, "CodenameEngine", function(e) { + var idk = GitHub.getContributors(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) { error = true; var errMsg:String = 'Error while trying to download contributors list:\n${CoolUtil.removeIP(e.message)}'; Logs.traceColored([Logs.logText(errMsg.replace('\n', ' '), RED)], ERROR); @@ -88,9 +84,9 @@ class CreditsCodename extends funkin.options.OptionsScreen { Logs.trace('[CreditsCodename] Contributors list Updated!', VERBOSE); var errorOnMain:Bool = false; - var idk2 = GitHub.getOrganizationMembers(author, function(e) { + var idk2 = GitHub.getOrganizationMembers(Flags.REPO_OWNER, function(e) { errorOnMain = true; - var errMsg:String = 'Error while trying to download $author members list:\n${CoolUtil.removeIP(e.message)}'; + var errMsg:String = 'Error while trying to download ${Flags.REPO_OWNER} members list:\n${CoolUtil.removeIP(e.message)}'; Logs.traceColored([Logs.logText(errMsg.replace('\n', ' '), RED)], ERROR); funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING); }); @@ -118,7 +114,7 @@ class CreditsCodename extends funkin.options.OptionsScreen { var opt:GithubIconOption = new GithubIconOption(c, 'Total Contributions: ~${c.contributions}~ / *${totalContributions}* (~${FlxMath.roundDecimal(c.contributions / totalContributions * 100, 2)}%~) - Select to open GitHub account'); if(Options.mainDevs.contains(c.id)) { opt.desc += " *- Public member of the main Devs!*"; - @:privateAccess opt.__text.color = mainDevCol; + @:privateAccess opt.__text.color = Flags.MAIN_DEVS_COLOR; } add(opt); } diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx index 916066eb..e2df9c6e 100644 --- a/source/funkin/menus/credits/CreditsMain.hx +++ b/source/funkin/menus/credits/CreditsMain.hx @@ -59,7 +59,7 @@ class CreditsMain extends TreeMenu { if (node.name == "github") { if (!node.has.user) { - Logs.trace("[CreditsMain] A github node requires a user attribute.", WARNING); + Logs.trace("A github node requires a user attribute.", WARNING); continue; } @@ -78,7 +78,7 @@ class CreditsMain extends TreeMenu { credsMenus.push(opt); } else { if (!node.has.name) { - Logs.trace("[CreditsMain] A credit node requires a name attribute.", WARNING); + Logs.trace("A credit node requires a name attribute.", WARNING); continue; } var name = node.getAtt("name"); diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index dfc86584..3ae5a127 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -162,7 +162,7 @@ class Alphabet extends FlxSpriteGroup splitWords = _finalText.split(""); } - //public var personTalking:String = 'gf'; + //public var personTalking:String = Flags.DEFAULT_GIRLFRIEND; public function startTypedText():Void { diff --git a/source/funkin/menus/ui/FunkinSoundTray.hx b/source/funkin/menus/ui/FunkinSoundTray.hx new file mode 100644 index 00000000..21280679 --- /dev/null +++ b/source/funkin/menus/ui/FunkinSoundTray.hx @@ -0,0 +1,118 @@ +package funkin.menus.ui; + +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.soundtray.*; +import funkin.backend.scripting.Script; +import flixel.system.ui.FlxSoundTray; +import openfl.text.TextFormat; + +class FunkinSoundTray extends FlxSoundTray +{ + public var script:Script; + + // Neeo wanted them since it would reduce lags for some people if activated - Nex + public var callsUpdate:Bool = true; + public var callsPostUpdate:Bool = true; + + public function new() + { + script = Script.create(Paths.script('data/soundTray')); + script.setParent(this); + script.load(); + + script.call("create"); + super(); + + FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume'); + FlxSoundTray.volumeUpChangeSFX = null; + FlxSoundTray.volumeDownChangeSFX = null; + FlxSoundTray.volumeMaxChangeSFX = null; + text.setTextFormat(new TextFormat(Paths.font("vcr.ttf"))); + + script.call("postCreate"); + } + + public override function reloadText(checkIfNull:Bool = true, reloadDefaultTextFormat:Bool = true, displayTxt:String = "VOLUME", y:Float = 16) + { + var event = EventManager.get(SoundTrayTextEvent).recycle(checkIfNull, reloadDefaultTextFormat, displayTxt, y); + script.call("reloadText", [event]); + if (event.cancelled) return; + + super.reloadText(event.checkIfNull, event.reloadDefaultTextFormat, event.displayTxt, event.y); + script.call("postReloadText", [event]); + } + + public override function reloadDtf() + { + var event = new CancellableEvent(); + script.call("reloadDtf", [event]); + if (event.cancelled) return; + + super.reloadDtf(); + script.call("postReloadDtf"); + } + + public override function regenerateBarsArray() + { + var event = new CancellableEvent(); + script.call("regenerateBarsArray", [event]); + if (event.cancelled) return; + + super.regenerateBarsArray(); + script.call("postRegenerateBarsArray"); + } + + public override function regenerateBars() + { + var event = new CancellableEvent(); + script.call("regenerateBars", [event]); + if (event.cancelled) return; + + super.regenerateBars(); + script.call("postRegenerateBars"); + } + + public override function update(elapsed:Float) + { + if (callsUpdate) script.call("update", [elapsed]); + super.update(elapsed); + if (callsPostUpdate) script.call("postUpdate", [elapsed]); + } + + public override function saveSoundPreferences() + { + var event = new CancellableEvent(); + script.call("saveSoundPreferences", [event]); + if (event.cancelled) return; + + super.saveSoundPreferences(); + script.call("postSaveSoundPreferences"); + } + + public override function show(up:Bool = false) + { + var event = EventManager.get(SoundTrayShowEvent).recycle(up); + script.call("show", [event]); + if (event.cancelled) return; + + super.show(event.up); + script.call("postShow", [event]); + } + + public override function screenCenter() + { + var event = new CancellableEvent(); + script.call("screenCenter", [event]); + if (event.cancelled) return; + + super.screenCenter(); + script.call("postScreenCenter"); + } + + private override function __cleanup() + { + script.call("destroy"); + script.destroy(); + super.__cleanup(); + } +} \ No newline at end of file diff --git a/source/hscript/Config.hx b/source/hscript/Config.hx index 98fc7f7a..4847baca 100644 --- a/source/hscript/Config.hx +++ b/source/hscript/Config.hx @@ -29,6 +29,6 @@ class Config { ]; public static final DISALLOW_ABSTRACT_AND_ENUM = [ - "funkin.backend.scripting.events.PlayAnimContext", // Error: expected member name or ';' after declaration specifiers, Due to define macro from math.h + "funkin.backend.scripting.events.sprite.PlayAnimContext", // Error: expected member name or ';' after declaration specifiers, Due to define macro from math.h ]; } \ No newline at end of file diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx index 98efafe3..64f8c771 100644 --- a/source/lime/system/System.hx +++ b/source/lime/system/System.hx @@ -367,7 +367,10 @@ class System #elseif mac Sys.command("/usr/bin/open", [path]); #elseif linux - Sys.command("/usr/bin/xdg-open", [path]); + // generally `xdg-open` should work in every distro + var cmd = Sys.command("xdg-open", [path, "&"]); + // run old command JUST IN CASE it fails, which it shouldn't + if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]); #elseif (js && html5) Browser.window.open(path, "_blank"); #elseif flash diff --git a/source/openfl/display/GraphicsShader.hx b/source/openfl/display/GraphicsShader.hx new file mode 100644 index 00000000..72c0ed97 --- /dev/null +++ b/source/openfl/display/GraphicsShader.hx @@ -0,0 +1,89 @@ +package openfl.display; + +import openfl.utils.ByteArray; + +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +class GraphicsShader extends Shader +{ + @:glVertexHeader("attribute float openfl_Alpha; +attribute vec4 openfl_ColorMultiplier; +attribute vec4 openfl_ColorOffset; +attribute vec4 openfl_Position; +attribute vec2 openfl_TextureCoord; + +varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform mat4 openfl_Matrix; +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize;") + @:glVertexBody("openfl_Alphav = openfl_Alpha; +openfl_TextureCoordv = openfl_TextureCoord; + +if (openfl_HasColorTransform) { + openfl_ColorMultiplierv = openfl_ColorMultiplier; + openfl_ColorOffsetv = openfl_ColorOffset / 255.0; +} + +gl_Position = openfl_Matrix * openfl_Position;") + @:glVertexSource("#pragma header + +void main(void) { + #pragma body +}") + @:glFragmentHeader("varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize; +uniform sampler2D bitmap;") + @:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv); + +if (color.a == 0.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} else if (openfl_HasColorTransform) { + color = vec4(color.rgb / color.a, color.a); + + mat4 colorMultiplier = mat4(0); + colorMultiplier[0][0] = openfl_ColorMultiplierv.x; + colorMultiplier[1][1] = openfl_ColorMultiplierv.y; + colorMultiplier[2][2] = openfl_ColorMultiplierv.z; + colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w; + + color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); + + if (color.a > 0.0) { + gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); + } else { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } +} else { + gl_FragColor = color * openfl_Alphav; +}") + #if emscripten + @:glFragmentSource("#pragma header + +void main(void) { + #pragma body + + gl_FragColor = gl_FragColor.bgra; +}") + #else + @:glFragmentSource("#pragma header + +void main(void) { + #pragma body +}") + #end + public function new(code:ByteArray = null) + { + super(code); + } +}