From 4815bed2cb8f32d984ec4e325883d68b5f6d64b5 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 16 Apr 2023 13:12:51 -0400 Subject: [PATCH 01/49] abot in progresss --- source/funkin/modding/base/ScriptedFlxAtlasSprite.hx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 source/funkin/modding/base/ScriptedFlxAtlasSprite.hx diff --git a/source/funkin/modding/base/ScriptedFlxAtlasSprite.hx b/source/funkin/modding/base/ScriptedFlxAtlasSprite.hx new file mode 100644 index 000000000..e9ffd450d --- /dev/null +++ b/source/funkin/modding/base/ScriptedFlxAtlasSprite.hx @@ -0,0 +1,8 @@ +package funkin.modding.base; + +/** + * A script that can be tied to an FlxAtlasSprite + * Create a scripted class that extends FlxAtlasSprite to use this. + */ +@:hscriptClass +class ScriptedFlxAtlasSprite extends funkin.graphics.adobeanimate.FlxAtlasSprite implements HScriptedClass {} From cf9915ef6097f897534a5bc843e71674836e09b6 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 8 Sep 2023 01:37:32 -0400 Subject: [PATCH 02/49] puddle positioning --- source/funkin/import.hx | 1 + source/funkin/play/stage/Stage.hx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 5ca6b03db..02055d4ed 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -6,6 +6,7 @@ import funkin.util.Constants; import funkin.Paths; import funkin.Preferences; import flixel.FlxG; // This one in particular causes a compile error if you're using macros. +import flixel.system.debug.watch.Tracker; // These are great. using Lambda; diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index c8cb8ce66..f876a0e54 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -276,6 +276,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass { namedProps.set(name, prop); prop.name = name; + FlxG.debugger.track(prop, name); + FlxG.debugger.track(prop.scale, name + '.scale'); } this.add(prop); } From 32ec724da68ba8e146a629168baa2eb7592aaece Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 11 Sep 2023 11:10:08 -0400 Subject: [PATCH 03/49] rainshader prototype --- .../funkin/graphics/shaders/PuddleShader.hx | 12 ++ .../shaders/RuntimePostEffectShader.hx | 51 ++++++++ .../graphics/shaders/RuntimeRainShader.hx | 111 ++++++++++++++++++ source/funkin/play/stage/Stage.hx | 2 - 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 source/funkin/graphics/shaders/PuddleShader.hx create mode 100644 source/funkin/graphics/shaders/RuntimePostEffectShader.hx create mode 100644 source/funkin/graphics/shaders/RuntimeRainShader.hx diff --git a/source/funkin/graphics/shaders/PuddleShader.hx b/source/funkin/graphics/shaders/PuddleShader.hx new file mode 100644 index 000000000..8e04c6453 --- /dev/null +++ b/source/funkin/graphics/shaders/PuddleShader.hx @@ -0,0 +1,12 @@ +package funkin.shaderslmfao; + +import flixel.addons.display.FlxRuntimeShader; +import openfl.Assets; + +class PuddleShader extends FlxRuntimeShader +{ + public function new() + { + super(Assets.getText(Paths.frag('puddle'))); + } +} diff --git a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx new file mode 100644 index 000000000..b9ec73c76 --- /dev/null +++ b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx @@ -0,0 +1,51 @@ +package funkin.shaderslmfao; + +import flixel.FlxG; +import flixel.addons.display.FlxRuntimeShader; +import flixel.system.FlxAssets.FlxShader; +import haxe.CallStack; +import lime.graphics.opengl.GLProgram; +import lime.utils.Log; + +class RuntimePostEffectShader extends FlxRuntimeShader +{ + @:glVertexHeader(" + varying vec2 fragCoord; // normalized texture coord + varying vec2 screenPos; // y: always between 0 and 1, x: between 0 and (width/height) + uniform vec2 screenResolution; + ", true) + @:glVertexBody(" + fragCoord = vec2( + openfl_TextureCoord.x > 0.0 ? 1.0 : 0.0, + openfl_TextureCoord.y > 0.0 ? 1.0 : 0.0 + ); + screenPos = fragCoord * vec2(screenResolution.x / screenResolution.y, 1.0); + ") + @:glFragmentHeader(" + varying vec2 fragCoord; + varying vec2 screenPos; + + vec2 texCoordSize() { // hack + return openfl_TextureCoordv / fragCoord; + } + ", true) + public function new(fragmentSource:String = null, glVersion:String = null) + { + super(fragmentSource, null, glVersion); + screenResolution.value = [FlxG.width, FlxG.height]; + } + + override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram + { + try + { + final res = super.__createGLProgram(vertexSource, fragmentSource); + return res; + } + catch (error) + { + Log.warn(error); + return null; + } + } +} diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx new file mode 100644 index 000000000..cab0a8964 --- /dev/null +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -0,0 +1,111 @@ +package funkin.shaderslmfao; + +import flixel.system.FlxAssets.FlxShader; +import openfl.display.BitmapData; +import openfl.display.ShaderParameter; +import openfl.display.ShaderParameterType; +import openfl.utils.Assets; + +typedef Light = +{ + var position:Array; + var color:Array; + var radius:Float; +} + +class RuntimeRainShader extends RuntimePostEffectShader +{ + static final MAX_LIGHTS:Int = 8; + + public var lights:Array< + { + position:ShaderParameter, + color:ShaderParameter, + radius:ShaderParameter, + }>; + + // This is a property, whenever the value is set it calls the set_time function. + // This makes the code cleaner elsewhere. + public var time(default, set):Float = 1; + + function set_time(value:Float):Float + { + this.setFloat('uTime', value); + return time = value; + } + + public var puddleMap(default, set):BitmapData; + + public var groundMap(default, set):BitmapData; + + function set_groundMap(value:BitmapData):BitmapData + { + trace("groundmap set"); + this.setBitmapData('uGroundMap', value); + // this.setFloat2('uPuddleTextureSize', value.width, value.height); + return groundMap = value; + } + + function set_puddleMap(value:BitmapData):BitmapData + { + this.setBitmapData('uPuddleMap', value); + return puddleMap = value; + } + + public var lightMap(default, set):BitmapData; + + function set_lightMap(value:BitmapData):BitmapData + { + trace("lightmap set"); + this.setBitmapData('uLightMap', value); + return lightMap = value; + } + + public var numLights(default, set):Int = 0; + + function set_numLights(value:Int):Int + { + this.setInt('numLights', value); + return numLights = value; + } + + public function new() + { + super(Assets.getText(Paths.frag("rain"))); + } + + public function update(elapsed:Float):Void + { + time += elapsed; + } + + override function __processGLData(source:String, storageType:String):Void + { + super.__processGLData(source, storageType); + if (storageType == "uniform") + { + lights = [ + for (i in 0...MAX_LIGHTS) + { + position: addFloatUniform("lights[" + i + "].position", 2), + color: addFloatUniform("lights[" + i + "].color", 3), + radius: addFloatUniform("lights[" + i + "].radius", 1), + } + ]; + } + } + + @:access(openfl.display.ShaderParameter) + function addFloatUniform(name:String, length:Int):ShaderParameter + { + final res = new ShaderParameter(); + res.name = name; + res.type = [null, FLOAT, FLOAT2, FLOAT3, FLOAT4][length]; + res.__arrayLength = 1; + res.__isFloat = true; + res.__isUniform = true; + res.__length = length; + __paramFloat.push(res); + return res; + } +} diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index f876a0e54..c8cb8ce66 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -276,8 +276,6 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass { namedProps.set(name, prop); prop.name = name; - FlxG.debugger.track(prop, name); - FlxG.debugger.track(prop.scale, name + '.scale'); } this.add(prop); } From eb27c679c3de4b0f3326438c2a4b0ff23d7e871b Mon Sep 17 00:00:00 2001 From: shr Date: Thu, 14 Sep 2023 06:28:59 +0900 Subject: [PATCH 04/49] update shader --- .../shaders/RuntimePostEffectShader.hx | 92 +++++++++++++++---- .../graphics/shaders/RuntimeRainShader.hx | 36 ++++++-- 2 files changed, 100 insertions(+), 28 deletions(-) diff --git a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx index b9ec73c76..7488d086f 100644 --- a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx +++ b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx @@ -1,38 +1,92 @@ package funkin.shaderslmfao; +import flixel.FlxCamera; import flixel.FlxG; import flixel.addons.display.FlxRuntimeShader; -import flixel.system.FlxAssets.FlxShader; -import haxe.CallStack; import lime.graphics.opengl.GLProgram; import lime.utils.Log; class RuntimePostEffectShader extends FlxRuntimeShader { - @:glVertexHeader(" - varying vec2 fragCoord; // normalized texture coord - varying vec2 screenPos; // y: always between 0 and 1, x: between 0 and (width/height) - uniform vec2 screenResolution; - ", true) - @:glVertexBody(" - fragCoord = vec2( + @:glVertexHeader(' + // normalized screen coord + // (0, 0) is the top left of the window + // (1, 1) is the bottom right of the window + varying vec2 screenCoord; + ', true) + @:glVertexBody(' + screenCoord = vec2( openfl_TextureCoord.x > 0.0 ? 1.0 : 0.0, openfl_TextureCoord.y > 0.0 ? 1.0 : 0.0 ); - screenPos = fragCoord * vec2(screenResolution.x / screenResolution.y, 1.0); - ") - @:glFragmentHeader(" - varying vec2 fragCoord; - varying vec2 screenPos; + ') + @:glFragmentHeader(' + // normalized screen coord + // (0, 0) is the top left of the window + // (1, 1) is the bottom right of the window + varying vec2 screenCoord; - vec2 texCoordSize() { // hack - return openfl_TextureCoordv / fragCoord; + // equals (FlxG.width, FlxG.height) + uniform vec2 uScreenResolution; + + // equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom) + uniform vec4 uCameraBounds; + + // screen coord -> world coord conversion + // returns world coord in px + vec2 screenToWorld(vec2 screenCoord) { + float left = uCameraBounds.x; + float top = uCameraBounds.y; + float right = uCameraBounds.z; + float bottom = uCameraBounds.w; + vec2 scale = vec2(right - left, bottom - top); + vec2 offset = vec2(left, top); + return screenCoord * scale + offset; } - ", true) + + // world coord -> screen coord conversion + // returns normalized screen coord + vec2 worldToScreen(vec2 worldCoord) { + float left = uCameraBounds.x; + float top = uCameraBounds.y; + float right = uCameraBounds.z; + float bottom = uCameraBounds.w; + vec2 scale = vec2(right - left, bottom - top); + vec2 offset = vec2(left, top); + return (worldCoord - offset) / scale; + } + + // internally used to get the maximum `openfl_TextureCoordv` + vec2 bitmapCoordScale() { + return openfl_TextureCoordv / screenCoord; + } + + // internally used to compute bitmap coord + vec2 screenToBitmap(vec2 screenCoord) { + return screenCoord * bitmapCoordScale(); + } + + // samples the frame buffer using a screen coord + vec4 sampleBitmapScreen(vec2 screenCoord) { + return texture2D(bitmap, screenToBitmap(screenCoord)); + } + + // samples the frame buffer using a world coord + vec4 sampleBitmapWorld(vec2 worldCoord) { + return sampleBitmapScreen(worldToScreen(worldCoord)); + } + ', true) public function new(fragmentSource:String = null, glVersion:String = null) { super(fragmentSource, null, glVersion); - screenResolution.value = [FlxG.width, FlxG.height]; + uScreenResolution.value = [FlxG.width, FlxG.height]; + } + + // basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good + public function updateViewInfo(screenWidth:Float, screenHeight:Float, camera:FlxCamera):Void + { + uScreenResolution.value = [screenWidth, screenHeight]; + uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom]; } override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram @@ -44,7 +98,7 @@ class RuntimePostEffectShader extends FlxRuntimeShader } catch (error) { - Log.warn(error); + Log.warn(error); // prevent the app from dying immediately return null; } } diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index cab0a8964..1150e4002 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -24,8 +24,6 @@ class RuntimeRainShader extends RuntimePostEffectShader radius:ShaderParameter, }>; - // This is a property, whenever the value is set it calls the set_time function. - // This makes the code cleaner elsewhere. public var time(default, set):Float = 1; function set_time(value:Float):Float @@ -34,13 +32,33 @@ class RuntimeRainShader extends RuntimePostEffectShader return time = value; } + // The scale of the rain depends on the world coordinate system, so higher resolution makes + // the raindrops smaller. This parameter can be used to adjust the total scale of the scene. + // The size of the raindrops is proportional to the value of this parameter. + public var scale(default, set):Float = 1; + + function set_scale(value:Float):Float + { + this.setFloat('uScale', value); + return scale = value; + } + + // The intensity of the rain. Zero means no rain and one means the maximum amount of rain. + public var intensity(default, set):Float = 1; + + function set_intensity(value:Float):Float + { + this.setFloat('uIntensity', value); + return intensity = value; + } + public var puddleMap(default, set):BitmapData; public var groundMap(default, set):BitmapData; function set_groundMap(value:BitmapData):BitmapData { - trace("groundmap set"); + trace('groundmap set'); this.setBitmapData('uGroundMap', value); // this.setFloat2('uPuddleTextureSize', value.width, value.height); return groundMap = value; @@ -56,7 +74,7 @@ class RuntimeRainShader extends RuntimePostEffectShader function set_lightMap(value:BitmapData):BitmapData { - trace("lightmap set"); + trace('lightmap set'); this.setBitmapData('uLightMap', value); return lightMap = value; } @@ -71,7 +89,7 @@ class RuntimeRainShader extends RuntimePostEffectShader public function new() { - super(Assets.getText(Paths.frag("rain"))); + super(Assets.getText(Paths.frag('rain'))); } public function update(elapsed:Float):Void @@ -82,14 +100,14 @@ class RuntimeRainShader extends RuntimePostEffectShader override function __processGLData(source:String, storageType:String):Void { super.__processGLData(source, storageType); - if (storageType == "uniform") + if (storageType == 'uniform') { lights = [ for (i in 0...MAX_LIGHTS) { - position: addFloatUniform("lights[" + i + "].position", 2), - color: addFloatUniform("lights[" + i + "].color", 3), - radius: addFloatUniform("lights[" + i + "].radius", 1), + position: addFloatUniform('lights[$i].position', 2), + color: addFloatUniform('lights[$i].color', 3), + radius: addFloatUniform('lights[$i].radius', 1), } ]; } From 95b03debb3a1dc84cbb97fbf811e4810d36385ba Mon Sep 17 00:00:00 2001 From: shr Date: Fri, 15 Sep 2023 22:09:59 +0900 Subject: [PATCH 05/49] still buggy --- .../graphics/shaders/RuntimeRainShader.hx | 16 ++++-- source/funkin/play/PlayState.hx | 54 +++++++++++++++++++ source/funkin/play/stage/Stage.hx | 12 +++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 1150e4002..20f5abc45 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -44,7 +44,7 @@ class RuntimeRainShader extends RuntimePostEffectShader } // The intensity of the rain. Zero means no rain and one means the maximum amount of rain. - public var intensity(default, set):Float = 1; + public var intensity(default, set):Float = 0.5; function set_intensity(value:Float):Float { @@ -52,18 +52,17 @@ class RuntimeRainShader extends RuntimePostEffectShader return intensity = value; } - public var puddleMap(default, set):BitmapData; - public var groundMap(default, set):BitmapData; function set_groundMap(value:BitmapData):BitmapData { - trace('groundmap set'); this.setBitmapData('uGroundMap', value); // this.setFloat2('uPuddleTextureSize', value.width, value.height); return groundMap = value; } + public var puddleMap(default, set):BitmapData; + function set_puddleMap(value:BitmapData):BitmapData { this.setBitmapData('uPuddleMap', value); @@ -74,11 +73,18 @@ class RuntimeRainShader extends RuntimePostEffectShader function set_lightMap(value:BitmapData):BitmapData { - trace('lightmap set'); this.setBitmapData('uLightMap', value); return lightMap = value; } + public var mask(default, set):BitmapData; + + function set_mask(value:BitmapData):BitmapData + { + this.setBitmapData('uMask', value); + return mask = value; + } + public var numLights(default, set):Int = 0; function set_numLights(value:Int):Int diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e0932e756..231e9d8b5 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -25,10 +25,14 @@ import flixel.math.FlxRect; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; +import flixel.system.frontEnds.CameraFrontEnd; +import flixel.util.FlxColor; import flixel.ui.FlxBar; import flixel.util.FlxColor; import funkin.api.newgrounds.NGio; import flixel.util.FlxTimer; +import openfl.display.BitmapData; +import openfl.geom.Rectangle; import funkin.audio.VoicesGroup; import funkin.save.Save; import funkin.Highscore.Tallies; @@ -972,6 +976,21 @@ class PlayState extends MusicBeatSubState processNotes(elapsed); } + @:access(flixel.FlxCamera) + @:access(flixel.system.frontEnds.CameraFrontEnd) + override function draw():Void + { + super.draw(); + + bufferCameraFrontEnd.lock(); + super.draw(); + camMask.render(); + bufferCameraFrontEnd.unlock(); + + maskTexture.fillRect(new Rectangle(0, 0, FlxG.width, FlxG.height), 0); + maskTexture.draw(camMask.canvas); // TODO: this assumes tile render mode?? + } + public override function dispatchEvent(event:ScriptEvent):Void { // ORDER: Module, Stage, Character, Song, Conversation, Note @@ -1245,6 +1264,9 @@ class PlayState extends MusicBeatSubState performCleanup(); super.destroy(); + + FlxG.signals.postUpdate.remove(syncBufferCameras); + bufferCameraFrontEnd.remove(camMask); } /** @@ -1279,6 +1301,12 @@ class PlayState extends MusicBeatSubState camCutscene = new FlxCamera(); camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. + // Init cameras and stuff for buffers. + camMask = new FlxCamera(); + maskTexture = new BitmapData(FlxG.width, FlxG.height, true, FlxColor.TRANSPARENT); + bufferCameraFrontEnd.reset(camMask); + FlxG.signals.postUpdate.add(syncBufferCameras); + FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); FlxG.cameras.add(camCutscene, false); @@ -1342,6 +1370,24 @@ class PlayState extends MusicBeatSubState add(menuBG); } + /** + * Syncs cameras for buffers; basically just copies how the main camera is doing + */ + function syncBufferCameras():Void + { + final tr = @:privateAccess FlxG.log._standardTraceFunction; + // tr("zoom: " + camGame.zoom); + for (cam in bufferCameraFrontEnd.list) + { + cam.x = camGame.x; + cam.y = camGame.y; + cam.scroll.x = camGame.scroll.x; + cam.scroll.y = camGame.scroll.y; + cam.zoom = camGame.zoom; + cam.update(FlxG.elapsed); // TODO: is this needed? + } + } + /** * Loads stage data from cache, assembles the props, * and adds it to the state. @@ -1366,6 +1412,14 @@ class PlayState extends MusicBeatSubState #if debug FlxG.console.registerObject('stage', currentStage); #end + + // Add mask sprites to the mask camera. + for (sprite in currentStage.maskSprites) + { + sprite.cameras.push(camMask); + } + // Set buffer textures. + currentStage.maskTexture = maskTexture; } else { diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index c8cb8ce66..d86d9404c 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -1,10 +1,12 @@ package funkin.play.stage; +import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxShader; import flixel.util.FlxSort; +import openfl.display.BitmapData; import funkin.modding.IScriptedClass; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventType; @@ -32,6 +34,16 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass public var camZoom:Float = 1.0; + /** + * The list of sprites that should be rendered for mask texture. + */ + public var maskSprites:Array = []; + + /** + * The texture that has the mask information. Used for shader effects. + */ + public var maskTexture:BitmapData; + var namedProps:Map = new Map(); var characters:Map = new Map(); var boppers:Array = new Array(); From b88fbeb206ceeac351a77c1c42ba1e2efea4e075 Mon Sep 17 00:00:00 2001 From: shr Date: Fri, 15 Sep 2023 23:49:27 +0900 Subject: [PATCH 06/49] update `draw()` --- source/funkin/play/PlayState.hx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 231e9d8b5..22ea78580 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -980,15 +980,22 @@ class PlayState extends MusicBeatSubState @:access(flixel.system.frontEnds.CameraFrontEnd) override function draw():Void { - super.draw(); - + // Clears the draw stacks buffer cameras. bufferCameraFrontEnd.lock(); + // Collects draw stacks to render stuff, for ALL cameras including + // the main ones and buffer ones. But at this point each camera's + // canvas.graphics is still empty. super.draw(); - camMask.render(); + // Actually render (using canvas.graphics) stuff ONLY for the buffer cameras. + // For the main cameras, it will be done by FlxGame LATER. + bufferCameraFrontEnd.render(); + // Possibly applies some FXs to the buffer cameras. bufferCameraFrontEnd.unlock(); - + // Update the buffer texture using `flashSprite`. + // This is IMMEDIATELY done while the main cameras are not rendered yet, + // so any shaders in the main part that refers the texture can see the updated texture! maskTexture.fillRect(new Rectangle(0, 0, FlxG.width, FlxG.height), 0); - maskTexture.draw(camMask.canvas); // TODO: this assumes tile render mode?? + maskTexture.draw(camMask.flashSprite, new openfl.geom.Matrix(1, 0, 0, 1, camMask.flashSprite.x, camMask.flashSprite.y)); } public override function dispatchEvent(event:ScriptEvent):Void @@ -1384,7 +1391,7 @@ class PlayState extends MusicBeatSubState cam.scroll.x = camGame.scroll.x; cam.scroll.y = camGame.scroll.y; cam.zoom = camGame.zoom; - cam.update(FlxG.elapsed); // TODO: is this needed? + @:privateAccess cam.updateFlashSpritePosition(); } } @@ -1416,7 +1423,7 @@ class PlayState extends MusicBeatSubState // Add mask sprites to the mask camera. for (sprite in currentStage.maskSprites) { - sprite.cameras.push(camMask); + sprite.cameras = [camMask]; } // Set buffer textures. currentStage.maskTexture = maskTexture; From 56cecf98a064a2176b2e28d6cb46be5dfa97d3ea Mon Sep 17 00:00:00 2001 From: shr Date: Fri, 15 Sep 2023 23:53:39 +0900 Subject: [PATCH 07/49] fix typo --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 22ea78580..2a8be1c5e 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -993,7 +993,7 @@ class PlayState extends MusicBeatSubState bufferCameraFrontEnd.unlock(); // Update the buffer texture using `flashSprite`. // This is IMMEDIATELY done while the main cameras are not rendered yet, - // so any shaders in the main part that refers the texture can see the updated texture! + // so any shaders in the main part that refer the texture can see the updated texture! maskTexture.fillRect(new Rectangle(0, 0, FlxG.width, FlxG.height), 0); maskTexture.draw(camMask.flashSprite, new openfl.geom.Matrix(1, 0, 0, 1, camMask.flashSprite.x, camMask.flashSprite.y)); } From 883ef13e6539a179de5c562e7316a306248f2579 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 00:32:45 +0900 Subject: [PATCH 08/49] finally did it --- source/funkin/SolidColorSprite.hx | 64 +++++++++++++++++++++++++++++++ source/funkin/play/PlayState.hx | 50 +++++++++++++++++------- 2 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 source/funkin/SolidColorSprite.hx diff --git a/source/funkin/SolidColorSprite.hx b/source/funkin/SolidColorSprite.hx new file mode 100644 index 000000000..b385ea1ae --- /dev/null +++ b/source/funkin/SolidColorSprite.hx @@ -0,0 +1,64 @@ +package funkin; + +import flixel.FlxSprite; + +/** + * Provides a clone of a sprite that is filled with a single color while keeping its alpha. + */ +class SolidColorSprite extends FlxSprite +{ + /** + * The FlxSprite that this sprite referes to. + */ + public var reference:FlxSprite; + + /** + * The red color strength, from 0.0 to 1.0. + */ + public var red:Float; + + /** + * The green color strength, from 0.0 to 1.0. + */ + public var green:Float; + + /** + * The blue color strength, from 0.0 to 1.0. + */ + public var blue:Float; + + function new(reference:FlxSprite, red:Float = 1.0, green:Float = 1.0, blue:Float = 1.0) + { + super(); + this.reference = reference; + this.red = red; + this.green = green; + this.blue = blue; + } + + override function draw():Void + { + super.draw(); + + final rMult = reference.colorTransform.redMultiplier; + final gMult = reference.colorTransform.greenMultiplier; + final bMult = reference.colorTransform.blueMultiplier; + final aMult = reference.colorTransform.alphaMultiplier; + final rOff = Std.int(reference.colorTransform.redOffset); + final gOff = Std.int(reference.colorTransform.greenOffset); + final bOff = Std.int(reference.colorTransform.blueOffset); + final aOff = Std.int(reference.colorTransform.alphaOffset); + final tmpCameras = reference._cameras; + final tmpShader = reference.shader; + + reference._cameras = _cameras; + + reference.shader = shader; + reference.setColorTransform(0, 0, 0, 1, Std.int(red * 255 + 0.5), Std.int(green * 255 + 0.5), Std.int(blue * 255 + 0.5), 0); + reference.draw(); + + reference._cameras = tmpCameras; + reference.shader = tmpShader; + reference.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); + } +} diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 2a8be1c5e..0dab2c52e 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -31,6 +31,7 @@ import flixel.ui.FlxBar; import flixel.util.FlxColor; import funkin.api.newgrounds.NGio; import flixel.util.FlxTimer; +import openfl.Lib; import openfl.display.BitmapData; import openfl.geom.Rectangle; import funkin.audio.VoicesGroup; @@ -589,6 +590,9 @@ class PlayState extends MusicBeatSubState } initStrumlines(); + // Initialize sprites for the buffer texture. + initMaskSprites(); + // Initialize the judgements and combo meter. comboPopUps = new PopUpStuff(); comboPopUps.cameras = [camHUD]; @@ -1272,6 +1276,8 @@ class PlayState extends MusicBeatSubState super.destroy(); + // It's manually obtained, don't forget to release it! + maskTextureBase.dispose(); FlxG.signals.postUpdate.remove(syncBufferCameras); bufferCameraFrontEnd.remove(camMask); } @@ -1308,11 +1314,7 @@ class PlayState extends MusicBeatSubState camCutscene = new FlxCamera(); camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. - // Init cameras and stuff for buffers. - camMask = new FlxCamera(); - maskTexture = new BitmapData(FlxG.width, FlxG.height, true, FlxColor.TRANSPARENT); - bufferCameraFrontEnd.reset(camMask); - FlxG.signals.postUpdate.add(syncBufferCameras); + initBufferCameras(); FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); @@ -1327,6 +1329,24 @@ class PlayState extends MusicBeatSubState add(cameraFollowPoint); } + function initBufferCameras():Void + { + // Init cameras and stuff for buffers. + camMask = new FlxCamera(); + // note: removing this line will cause NullReferenceError inside OpenGLRenderer lol + camMask.flashSprite.cacheAsBitmap = true; + // Prevent potential memory leak. + if (maskTextureBase != null) maskTextureBase.dispose(); + // We need to directly create texture using Context3D, otherwise cannot render Sprite + // using OpenGLRenderer, which disables any shader applied to it. + maskTextureBase = Lib.current.stage.context3D.createTexture(FlxG.width, FlxG.height, Context3DTextureFormat.BGRA, true); + maskTexture = BitmapData.fromTexture(maskTextureBase); + // This must be done BEFORE `FlxG.cameras.reset`. + bufferCameraFrontEnd.reset(camMask); + // Sync buffer cameras after every update. + FlxG.signals.postUpdate.add(syncBufferCameras); + } + /** * Initializes the health bar on the HUD. */ @@ -1419,14 +1439,6 @@ class PlayState extends MusicBeatSubState #if debug FlxG.console.registerObject('stage', currentStage); #end - - // Add mask sprites to the mask camera. - for (sprite in currentStage.maskSprites) - { - sprite.cameras = [camMask]; - } - // Set buffer textures. - currentStage.maskTexture = maskTexture; } else { @@ -1585,6 +1597,18 @@ class PlayState extends MusicBeatSubState this.refresh(); } + function initMaskSprites():Void + { + // Add mask sprites to the mask camera. + for (sprite in currentStage.maskSprites) + { + this.add(sprite); + sprite.cameras = [camMask]; + } + // Set buffer textures to the current stage. + currentStage.maskTexture = maskTexture; + } + /** * Initializes the Discord Rich Presence. */ From 6d0a8cc535fd254d823486e02159296fbcc6bb46 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 07:39:20 +0900 Subject: [PATCH 09/49] testing poyopoyo --- .../graphics/framebuffer/FrameBuffer.hx | 99 ++++++++++++++++ .../framebuffer/FrameBufferManager.hx | 109 ++++++++++++++++++ .../funkin/graphics/framebuffer/SpriteCopy.hx | 51 ++++++++ 3 files changed, 259 insertions(+) create mode 100644 source/funkin/graphics/framebuffer/FrameBuffer.hx create mode 100644 source/funkin/graphics/framebuffer/FrameBufferManager.hx create mode 100644 source/funkin/graphics/framebuffer/SpriteCopy.hx diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx new file mode 100644 index 000000000..480bbf111 --- /dev/null +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -0,0 +1,99 @@ +package funkin.graphics.framebuffer; + +import openfl.geom.Rectangle; +import openfl.geom.Matrix; +import openfl.display.BitmapData; +import flixel.util.FlxColor; +import flixel.FlxCamera; +import openfl.Lib; +import openfl.display3D.textures.TextureBase; + +class FrameBuffer +{ + /** + * The bitmap data of the frame buffer. + */ + public var bitmap(default, null):BitmapData = null; + + var texture:TextureBase; + final camera:FlxCamera; + final spriteCopies:Array = []; + + public function new() + { + camera = new FlxCamera(); + camera.bgColor = FlxColor.TRANSPARENT; + camera.flashSprite.cacheAsBitmap = true; + } + + /** + * Creates a frame buffer with the given size. + * @param width the width + * @param height the height + */ + public function create(width:Int, height:Int):Void + { + dispose(); + final c3d = Lib.current.stage.context3D; + texture = c3d.createTexture(width, height, BGRA, true); + bitmap = BitmapData.fromTexture(texture); + } + + /** + * Makes the internal camera follows the target camera. + * @param target the target camera + */ + public function follow(target:FlxCamera):Void + { + camera.scroll.copyFrom(target.scroll); + camera.setScale(target.scaleX, target.scaleY); + } + + /** + * Locks the frame buffer and clears the buffer. + */ + @:access(flixel.FlxCamera) + public function lock():Void + { + camera.clearDrawStack(); + camera.canvas.graphics.clear(); + camera.fill(camera.bgColor.to24Bit(), camera.useBgAlphaBlending, camera.bgColor.alphaFloat); + } + + /** + * Renders all sprite copies. + */ + public function render():Void + { + for (spriteCopy in spriteCopies) + { + spriteCopy.render(camera); + } + } + + /** + * Unlocks the frame buffer and makes the bitmap ready to use. + */ + @:access(flixel.FlxCamera) + public function unlock():Void + { + bitmap.fillRect(new Rectangle(0, 0, bitmap.width, bitmap.height), 0); + bitmap.draw(camera.flashSprite, new Matrix(1, 0, 0, 1, camera.flashSprite.x, camera.flashSprite.y)); + } + + public function dispose():Void + { + if (texture != null) + { + texture.dispose(); + texture = null; + bitmap.dispose(); + bitmap = null; + } + } + + public function addSpriteCopy(spriteCopy:SpriteCopy):Void + { + spriteCopies.push(spriteCopy); + } +} diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx new file mode 100644 index 000000000..2326b6c43 --- /dev/null +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -0,0 +1,109 @@ +package funkin.graphics.framebuffer; + +import openfl.display.BitmapData; +import flixel.FlxSprite; +import flixel.FlxCamera; + +class FrameBufferManager +{ + final camera:FlxCamera; + final frameBufferMap:Map = []; + + /** + * Creates a frame buffer manager that targets `camera`. + * @param camera the target camera. + */ + public function new(camera:FlxCamera) + { + this.camera = camera; + } + + /** + * Creates a new frame buffer with a name. + * @param name the name + */ + public function createFrameBuffer(name:String):Void + { + if (frameBufferMap.exists(name)) + { + FlxG.log.warn('frame buffer "$name" already exists'); + } + else + { + final fb = new FrameBuffer(); + fb.create(camera.width, camera.height); + frameBufferMap[name] = fb; + } + } + + /** + * Adds a copy of the sprite to the frame buffer. + * @param name the name of the frame buffer + * @param sprite the sprite + * @param color if this is not `-1`, the sprite will have the color while keeping its shape + */ + public function addSpriteTo(name:String, sprite:FlxSprite, color:Int = -1):Void + { + if (!frameBufferMap.exists(name)) + { + createFrameBuffer(name); + } + frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color)); + } + + /** + * Call this before everything is drawn. + */ + public function lock():Void + { + for (_ => fb in frameBufferMap) + { + fb.lock(); + } + } + + /** + * Renders all the copies of the sprites. Make sure this is called between + * `lock` and `unlock`. + */ + public function render():Void + { + for (_ => fb in frameBufferMap) + { + fb.render(); + } + } + + /** + * After calling this you can use bitmap data of all frame buffers. + */ + public function unlock():Void + { + for (_ => fb in frameBufferMap) + { + fb.unlock(); + } + } + + /** + * Returns the bitmap data of the frame buffer + * @param name the name of the frame buffer + * @return the ready-to-use bitmap data + */ + public function getFrameBuffer(name:String):BitmapData + { + return frameBufferMap[name].bitmap; + } + + /** + * Disposes all frame buffers. + */ + public function dispose():Void + { + for (_ => fb in frameBufferMap) + { + fb.dispose(); + } + frameBufferMap.clear(); + } +} diff --git a/source/funkin/graphics/framebuffer/SpriteCopy.hx b/source/funkin/graphics/framebuffer/SpriteCopy.hx new file mode 100644 index 000000000..aaa6bb730 --- /dev/null +++ b/source/funkin/graphics/framebuffer/SpriteCopy.hx @@ -0,0 +1,51 @@ +package funkin.graphics.framebuffer; + +import flixel.FlxCamera; +import flixel.FlxSprite; + +class SpriteCopy +{ + final sprite:FlxSprite; + var color:Int; + + public function new(sprite:FlxSprite, color:Int = -1) + { + this.sprite = sprite; + this.color = color; + } + + /** + * Renders the copy to the camera. + * @param camera the camera + */ + @:access(flixel.FlxSprite) + public function render(camera:FlxCamera):Void + { + final rMult = sprite.colorTransform.redMultiplier; + final gMult = sprite.colorTransform.greenMultiplier; + final bMult = sprite.colorTransform.blueMultiplier; + final aMult = sprite.colorTransform.alphaMultiplier; + final rOff = Std.int(sprite.colorTransform.redOffset); + final gOff = Std.int(sprite.colorTransform.greenOffset); + final bOff = Std.int(sprite.colorTransform.blueOffset); + final aOff = Std.int(sprite.colorTransform.alphaOffset); + final tmpCameras = sprite._cameras; + + sprite._cameras = [camera]; + + if (color != -1) + { + final red = color >> 16 & 0xFF; + final green = color >> 8 & 0xFF; + final blue = color & 0xFF; + sprite.setColorTransform(0, 0, 0, 1, red, green, blue, 0); + } + sprite.draw(); + + sprite._cameras = tmpCameras; + if (color != -1) + { + sprite.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); + } + } +} From 26818e6d99f5d72c002f78428f43e77fe9d96f6d Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 20:45:10 +0900 Subject: [PATCH 10/49] made it a library --- source/funkin/SolidColorSprite.hx | 64 ------------- .../graphics/framebuffer/FrameBuffer.hx | 18 +++- .../framebuffer/FrameBufferManager.hx | 44 ++++----- .../funkin/graphics/framebuffer/SpriteCopy.hx | 51 +++++----- source/funkin/play/PlayState.hx | 95 ++----------------- source/funkin/play/stage/Stage.hx | 49 +++++++++- 6 files changed, 117 insertions(+), 204 deletions(-) delete mode 100644 source/funkin/SolidColorSprite.hx diff --git a/source/funkin/SolidColorSprite.hx b/source/funkin/SolidColorSprite.hx deleted file mode 100644 index b385ea1ae..000000000 --- a/source/funkin/SolidColorSprite.hx +++ /dev/null @@ -1,64 +0,0 @@ -package funkin; - -import flixel.FlxSprite; - -/** - * Provides a clone of a sprite that is filled with a single color while keeping its alpha. - */ -class SolidColorSprite extends FlxSprite -{ - /** - * The FlxSprite that this sprite referes to. - */ - public var reference:FlxSprite; - - /** - * The red color strength, from 0.0 to 1.0. - */ - public var red:Float; - - /** - * The green color strength, from 0.0 to 1.0. - */ - public var green:Float; - - /** - * The blue color strength, from 0.0 to 1.0. - */ - public var blue:Float; - - function new(reference:FlxSprite, red:Float = 1.0, green:Float = 1.0, blue:Float = 1.0) - { - super(); - this.reference = reference; - this.red = red; - this.green = green; - this.blue = blue; - } - - override function draw():Void - { - super.draw(); - - final rMult = reference.colorTransform.redMultiplier; - final gMult = reference.colorTransform.greenMultiplier; - final bMult = reference.colorTransform.blueMultiplier; - final aMult = reference.colorTransform.alphaMultiplier; - final rOff = Std.int(reference.colorTransform.redOffset); - final gOff = Std.int(reference.colorTransform.greenOffset); - final bOff = Std.int(reference.colorTransform.blueOffset); - final aOff = Std.int(reference.colorTransform.alphaOffset); - final tmpCameras = reference._cameras; - final tmpShader = reference.shader; - - reference._cameras = _cameras; - - reference.shader = shader; - reference.setColorTransform(0, 0, 0, 1, Std.int(red * 255 + 0.5), Std.int(green * 255 + 0.5), Std.int(blue * 255 + 0.5), 0); - reference.draw(); - - reference._cameras = tmpCameras; - reference.shader = tmpShader; - reference.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); - } -} diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx index 480bbf111..73567d0ba 100644 --- a/source/funkin/graphics/framebuffer/FrameBuffer.hx +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -22,21 +22,25 @@ class FrameBuffer public function new() { camera = new FlxCamera(); + camera.antialiasing = false; camera.bgColor = FlxColor.TRANSPARENT; camera.flashSprite.cacheAsBitmap = true; + @:privateAccess camera.flashSprite.stage = Lib.current.stage; } /** * Creates a frame buffer with the given size. * @param width the width * @param height the height + * @param bgColor the background color */ - public function create(width:Int, height:Int):Void + public function create(width:Int, height:Int, bgColor:FlxColor):Void { dispose(); final c3d = Lib.current.stage.context3D; texture = c3d.createTexture(width, height, BGRA, true); bitmap = BitmapData.fromTexture(texture); + camera.bgColor = bgColor; } /** @@ -45,7 +49,12 @@ class FrameBuffer */ public function follow(target:FlxCamera):Void { - camera.scroll.copyFrom(target.scroll); + camera.x = target.x; + camera.y = target.y; + camera.width = target.width; + camera.height = target.height; + camera.scroll.x = target.scroll.x; + camera.scroll.y = target.scroll.y; camera.setScale(target.scaleX, target.scaleY); } @@ -58,17 +67,22 @@ class FrameBuffer camera.clearDrawStack(); camera.canvas.graphics.clear(); camera.fill(camera.bgColor.to24Bit(), camera.useBgAlphaBlending, camera.bgColor.alphaFloat); + #if FLX_DEBUG + camera.debugLayer.graphics.clear(); + #end } /** * Renders all sprite copies. */ + @:access(flixel.FlxCamera) public function render():Void { for (spriteCopy in spriteCopies) { spriteCopy.render(camera); } + camera.render(); } /** diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index 2326b6c43..f842ef638 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -1,5 +1,6 @@ package funkin.graphics.framebuffer; +import flixel.util.FlxColor; import openfl.display.BitmapData; import flixel.FlxSprite; import flixel.FlxCamera; @@ -21,33 +22,33 @@ class FrameBufferManager /** * Creates a new frame buffer with a name. * @param name the name + * @param bgColor the background color + * @return the bitmap data of the frame buffer. the bitmap data instance + * will not be changed through frame buffer updates. */ - public function createFrameBuffer(name:String):Void + public function createFrameBuffer(name:String, bgColor:FlxColor):BitmapData { if (frameBufferMap.exists(name)) { FlxG.log.warn('frame buffer "$name" already exists'); + frameBufferMap[name].dispose(); + frameBufferMap.remove(name); } - else - { - final fb = new FrameBuffer(); - fb.create(camera.width, camera.height); - frameBufferMap[name] = fb; - } + final fb = new FrameBuffer(); + fb.create(camera.width, camera.height, bgColor); + frameBufferMap[name] = fb; + return fb.bitmap; } /** * Adds a copy of the sprite to the frame buffer. * @param name the name of the frame buffer * @param sprite the sprite - * @param color if this is not `-1`, the sprite will have the color while keeping its shape + * @param color if this is not `null`, the sprite will be filled with the color. + * if this is `null`, the sprite will keep its original color. */ - public function addSpriteTo(name:String, sprite:FlxSprite, color:Int = -1):Void + public function addSpriteCopyTo(name:String, sprite:FlxSprite, color:Null = null):Void { - if (!frameBufferMap.exists(name)) - { - createFrameBuffer(name); - } frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color)); } @@ -58,27 +59,20 @@ class FrameBufferManager { for (_ => fb in frameBufferMap) { + fb.follow(camera); fb.lock(); } } /** - * Renders all the copies of the sprites. Make sure this is called between - * `lock` and `unlock`. + * Unlocks the frame buffers. This updates the bitmap data of each frame buffer. */ - public function render():Void + public function unlock():Void { for (_ => fb in frameBufferMap) { fb.render(); } - } - - /** - * After calling this you can use bitmap data of all frame buffers. - */ - public function unlock():Void - { for (_ => fb in frameBufferMap) { fb.unlock(); @@ -88,7 +82,7 @@ class FrameBufferManager /** * Returns the bitmap data of the frame buffer * @param name the name of the frame buffer - * @return the ready-to-use bitmap data + * @return the bitmap data */ public function getFrameBuffer(name:String):BitmapData { @@ -96,7 +90,7 @@ class FrameBufferManager } /** - * Disposes all frame buffers. + * Disposes all frame buffers. The instance can be reused. */ public function dispose():Void { diff --git a/source/funkin/graphics/framebuffer/SpriteCopy.hx b/source/funkin/graphics/framebuffer/SpriteCopy.hx index aaa6bb730..b1fc82497 100644 --- a/source/funkin/graphics/framebuffer/SpriteCopy.hx +++ b/source/funkin/graphics/framebuffer/SpriteCopy.hx @@ -1,14 +1,15 @@ package funkin.graphics.framebuffer; +import flixel.util.FlxColor; import flixel.FlxCamera; import flixel.FlxSprite; class SpriteCopy { final sprite:FlxSprite; - var color:Int; + var color:Null; - public function new(sprite:FlxSprite, color:Int = -1) + public function new(sprite:FlxSprite, color:Null) { this.sprite = sprite; this.color = color; @@ -21,30 +22,34 @@ class SpriteCopy @:access(flixel.FlxSprite) public function render(camera:FlxCamera):Void { - final rMult = sprite.colorTransform.redMultiplier; - final gMult = sprite.colorTransform.greenMultiplier; - final bMult = sprite.colorTransform.blueMultiplier; - final aMult = sprite.colorTransform.alphaMultiplier; - final rOff = Std.int(sprite.colorTransform.redOffset); - final gOff = Std.int(sprite.colorTransform.greenOffset); - final bOff = Std.int(sprite.colorTransform.blueOffset); - final aOff = Std.int(sprite.colorTransform.alphaOffset); - final tmpCameras = sprite._cameras; - - sprite._cameras = [camera]; - - if (color != -1) + if (color == null) { - final red = color >> 16 & 0xFF; - final green = color >> 8 & 0xFF; - final blue = color & 0xFF; - sprite.setColorTransform(0, 0, 0, 1, red, green, blue, 0); + final tmpCameras = sprite._cameras; + sprite._cameras = [camera]; + sprite.draw(); + sprite._cameras = tmpCameras; } - sprite.draw(); - - sprite._cameras = tmpCameras; - if (color != -1) + else { + final rMult = sprite.colorTransform.redMultiplier; + final gMult = sprite.colorTransform.greenMultiplier; + final bMult = sprite.colorTransform.blueMultiplier; + final aMult = sprite.colorTransform.alphaMultiplier; + final rOff = Std.int(sprite.colorTransform.redOffset); + final gOff = Std.int(sprite.colorTransform.greenOffset); + final bOff = Std.int(sprite.colorTransform.blueOffset); + final aOff = Std.int(sprite.colorTransform.alphaOffset); + final tmpCameras = sprite._cameras; + final tmpShader = sprite.shader; + + sprite._cameras = [camera]; + sprite.shader = null; + + sprite.setColorTransform(0, 0, 0, 1, color.red, color.green, color.blue, 0); + sprite.draw(); + + sprite._cameras = tmpCameras; + sprite.shader = tmpShader; sprite.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff); } } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 0dab2c52e..75a311a45 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -15,7 +15,10 @@ import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; import flixel.FlxSubState; -import flixel.input.keyboard.FlxKey; +import flixel.addons.display.FlxPieDial; +import flixel.addons.transition.FlxTransitionableState; +import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.Transition; import flixel.math.FlxMath; import funkin.play.components.ComboMilestone; import flixel.math.FlxPoint; @@ -25,8 +28,6 @@ import flixel.math.FlxRect; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; -import flixel.system.frontEnds.CameraFrontEnd; -import flixel.util.FlxColor; import flixel.ui.FlxBar; import flixel.util.FlxColor; import funkin.api.newgrounds.NGio; @@ -37,21 +38,24 @@ import openfl.geom.Rectangle; import funkin.audio.VoicesGroup; import funkin.save.Save; import funkin.Highscore.Tallies; +import funkin.NoteSplash; +import funkin.audio.VoicesGroup; +import funkin.data.notestyle.NoteStyleRegistry; import funkin.input.PreciseInputManager; import funkin.modding.events.ScriptEvent; import funkin.ui.mainmenu.MainMenuState; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.BaseCharacter; import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.ConversationDataParser; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.data.event.SongEventData.SongEventParser; import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteDirection; +import funkin.play.notes.NoteSprite; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; +import funkin.play.notes.notestyle.NoteStyle; import funkin.play.scoring.Scoring; import funkin.play.song.Song; import funkin.data.song.SongRegistry; @@ -66,7 +70,6 @@ import funkin.ui.options.PreferencesMenu; import funkin.ui.debug.stage.StageOffsetSubState; import funkin.ui.story.StoryMenuState; import funkin.util.SerializerUtil; -import funkin.util.SortUtil; import lime.ui.Haptic; #if discord_rpc import Discord.DiscordClient; @@ -590,9 +593,6 @@ class PlayState extends MusicBeatSubState } initStrumlines(); - // Initialize sprites for the buffer texture. - initMaskSprites(); - // Initialize the judgements and combo meter. comboPopUps = new PopUpStuff(); comboPopUps.cameras = [camHUD]; @@ -980,28 +980,6 @@ class PlayState extends MusicBeatSubState processNotes(elapsed); } - @:access(flixel.FlxCamera) - @:access(flixel.system.frontEnds.CameraFrontEnd) - override function draw():Void - { - // Clears the draw stacks buffer cameras. - bufferCameraFrontEnd.lock(); - // Collects draw stacks to render stuff, for ALL cameras including - // the main ones and buffer ones. But at this point each camera's - // canvas.graphics is still empty. - super.draw(); - // Actually render (using canvas.graphics) stuff ONLY for the buffer cameras. - // For the main cameras, it will be done by FlxGame LATER. - bufferCameraFrontEnd.render(); - // Possibly applies some FXs to the buffer cameras. - bufferCameraFrontEnd.unlock(); - // Update the buffer texture using `flashSprite`. - // This is IMMEDIATELY done while the main cameras are not rendered yet, - // so any shaders in the main part that refer the texture can see the updated texture! - maskTexture.fillRect(new Rectangle(0, 0, FlxG.width, FlxG.height), 0); - maskTexture.draw(camMask.flashSprite, new openfl.geom.Matrix(1, 0, 0, 1, camMask.flashSprite.x, camMask.flashSprite.y)); - } - public override function dispatchEvent(event:ScriptEvent):Void { // ORDER: Module, Stage, Character, Song, Conversation, Note @@ -1275,11 +1253,6 @@ class PlayState extends MusicBeatSubState performCleanup(); super.destroy(); - - // It's manually obtained, don't forget to release it! - maskTextureBase.dispose(); - FlxG.signals.postUpdate.remove(syncBufferCameras); - bufferCameraFrontEnd.remove(camMask); } /** @@ -1314,8 +1287,6 @@ class PlayState extends MusicBeatSubState camCutscene = new FlxCamera(); camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. - initBufferCameras(); - FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); FlxG.cameras.add(camCutscene, false); @@ -1329,24 +1300,6 @@ class PlayState extends MusicBeatSubState add(cameraFollowPoint); } - function initBufferCameras():Void - { - // Init cameras and stuff for buffers. - camMask = new FlxCamera(); - // note: removing this line will cause NullReferenceError inside OpenGLRenderer lol - camMask.flashSprite.cacheAsBitmap = true; - // Prevent potential memory leak. - if (maskTextureBase != null) maskTextureBase.dispose(); - // We need to directly create texture using Context3D, otherwise cannot render Sprite - // using OpenGLRenderer, which disables any shader applied to it. - maskTextureBase = Lib.current.stage.context3D.createTexture(FlxG.width, FlxG.height, Context3DTextureFormat.BGRA, true); - maskTexture = BitmapData.fromTexture(maskTextureBase); - // This must be done BEFORE `FlxG.cameras.reset`. - bufferCameraFrontEnd.reset(camMask); - // Sync buffer cameras after every update. - FlxG.signals.postUpdate.add(syncBufferCameras); - } - /** * Initializes the health bar on the HUD. */ @@ -1397,24 +1350,6 @@ class PlayState extends MusicBeatSubState add(menuBG); } - /** - * Syncs cameras for buffers; basically just copies how the main camera is doing - */ - function syncBufferCameras():Void - { - final tr = @:privateAccess FlxG.log._standardTraceFunction; - // tr("zoom: " + camGame.zoom); - for (cam in bufferCameraFrontEnd.list) - { - cam.x = camGame.x; - cam.y = camGame.y; - cam.scroll.x = camGame.scroll.x; - cam.scroll.y = camGame.scroll.y; - cam.zoom = camGame.zoom; - @:privateAccess cam.updateFlashSpritePosition(); - } - } - /** * Loads stage data from cache, assembles the props, * and adds it to the state. @@ -1597,18 +1532,6 @@ class PlayState extends MusicBeatSubState this.refresh(); } - function initMaskSprites():Void - { - // Add mask sprites to the mask camera. - for (sprite in currentStage.maskSprites) - { - this.add(sprite); - sprite.cameras = [camMask]; - } - // Set buffer textures to the current stage. - currentStage.maskTexture = maskTexture; - } - /** * Initializes the Discord Rich Presence. */ diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index d86d9404c..2f9efa42f 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -1,5 +1,8 @@ package funkin.play.stage; +import funkin.graphics.framebuffer.FrameBufferManager; +import flixel.util.FlxColor; +import funkin.graphics.framebuffer.SpriteCopy; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; @@ -20,6 +23,26 @@ import funkin.util.assets.FlxAnimationUtil; typedef StagePropGroup = FlxTypedSpriteGroup; +typedef FrameBufferSprite = +{ + /** + * The name of the target frame buffer. + */ + var name:String; + + /** + * The sprite to be rendered. + */ + var sprite:FlxSprite; + + /** + * The RGB color of the sprite. The alpha component will be ignored. + * If this is `null`, the sprite keeps its original color. + */ + @:optional + var color:Null; +} + /** * A Stage is a group of objects rendered in the PlayState. * @@ -34,10 +57,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass public var camZoom:Float = 1.0; - /** - * The list of sprites that should be rendered for mask texture. - */ - public var maskSprites:Array = []; + var frameBufferMan:FrameBufferManager; + + public final frameBufferSprites:Array = []; /** * The texture that has the mask information. Used for shader effects. @@ -76,6 +98,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass */ public function onCreate(event:ScriptEvent):Void { + if (frameBufferMan != null) frameBufferMan.dispose(); + frameBufferMan = new FrameBufferManager(FlxG.camera); + onFrameBufferCreate(); + buildStage(); this.refresh(); @@ -691,6 +717,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass { debugIconGroup = null; } + + frameBufferMan.dispose(); } /** @@ -743,6 +771,19 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass return Sprite; } + override function draw():Void + { + frameBufferMan.lock(); + super.draw(); + frameBufferMan.unlock(); + } + + /** + * Called when the frame buffer manager is ready. + * Create frame buffers inside this method. + */ + public function onFrameBufferCreate():Void {} + public function onScriptEvent(event:ScriptEvent) {} public function onPause(event:PauseScriptEvent) {} From ace89a44a5cc4f4b52187cdb8ff502264bbe7be2 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 20:58:15 +0900 Subject: [PATCH 11/49] cleaning up code --- source/funkin/play/stage/Stage.hx | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 2f9efa42f..ada8c898d 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -23,26 +23,6 @@ import funkin.util.assets.FlxAnimationUtil; typedef StagePropGroup = FlxTypedSpriteGroup; -typedef FrameBufferSprite = -{ - /** - * The name of the target frame buffer. - */ - var name:String; - - /** - * The sprite to be rendered. - */ - var sprite:FlxSprite; - - /** - * The RGB color of the sprite. The alpha component will be ignored. - * If this is `null`, the sprite keeps its original color. - */ - @:optional - var color:Null; -} - /** * A Stage is a group of objects rendered in the PlayState. * @@ -59,8 +39,6 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass var frameBufferMan:FrameBufferManager; - public final frameBufferSprites:Array = []; - /** * The texture that has the mask information. Used for shader effects. */ From 7c46bcb7078bdc012e24e3f2634d8726d38040d2 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 17 Sep 2023 21:11:11 +0900 Subject: [PATCH 12/49] add docs --- source/funkin/graphics/framebuffer/FrameBuffer.hx | 15 ++++++++++++--- .../graphics/framebuffer/FrameBufferManager.hx | 3 +++ source/funkin/graphics/framebuffer/SpriteCopy.hx | 5 ++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx index 73567d0ba..0209a4aef 100644 --- a/source/funkin/graphics/framebuffer/FrameBuffer.hx +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -8,6 +8,9 @@ import flixel.FlxCamera; import openfl.Lib; import openfl.display3D.textures.TextureBase; +/** + * A single frame buffer. Used by `FrameBufferManager`. + */ class FrameBuffer { /** @@ -37,8 +40,7 @@ class FrameBuffer public function create(width:Int, height:Int, bgColor:FlxColor):Void { dispose(); - final c3d = Lib.current.stage.context3D; - texture = c3d.createTexture(width, height, BGRA, true); + texture = Lib.current.stage.context3D.createTexture(width, height, BGRA, true); bitmap = BitmapData.fromTexture(texture); camera.bgColor = bgColor; } @@ -88,13 +90,15 @@ class FrameBuffer /** * Unlocks the frame buffer and makes the bitmap ready to use. */ - @:access(flixel.FlxCamera) public function unlock():Void { bitmap.fillRect(new Rectangle(0, 0, bitmap.width, bitmap.height), 0); bitmap.draw(camera.flashSprite, new Matrix(1, 0, 0, 1, camera.flashSprite.x, camera.flashSprite.y)); } + /** + * Diposes stuff. Call `create` again if you want to reuse the instance. + */ public function dispose():Void { if (texture != null) @@ -104,8 +108,13 @@ class FrameBuffer bitmap.dispose(); bitmap = null; } + spriteCopies.clear(); } + /** + * Adds a sprite copy to the frame buffer. + * @param spriteCopy the sprite copy + */ public function addSpriteCopy(spriteCopy:SpriteCopy):Void { spriteCopies.push(spriteCopy); diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index f842ef638..6cfadd468 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -5,6 +5,9 @@ import openfl.display.BitmapData; import flixel.FlxSprite; import flixel.FlxCamera; +/** + * Manages frame buffers and gives access to each frame buffer. + */ class FrameBufferManager { final camera:FlxCamera; diff --git a/source/funkin/graphics/framebuffer/SpriteCopy.hx b/source/funkin/graphics/framebuffer/SpriteCopy.hx index b1fc82497..b95462ea3 100644 --- a/source/funkin/graphics/framebuffer/SpriteCopy.hx +++ b/source/funkin/graphics/framebuffer/SpriteCopy.hx @@ -1,9 +1,12 @@ package funkin.graphics.framebuffer; -import flixel.util.FlxColor; import flixel.FlxCamera; import flixel.FlxSprite; +import flixel.util.FlxColor; +/** + * A copy of a `FlxSprite` with a specified color. Used to render the sprite to a frame buffer. + */ class SpriteCopy { final sprite:FlxSprite; From 9fc3a46b48c2b4b5d93283f78260702ee61795e9 Mon Sep 17 00:00:00 2001 From: shr Date: Tue, 19 Sep 2023 22:31:50 +0900 Subject: [PATCH 13/49] shader update --- .../graphics/shaders/RuntimeRainShader.hx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 20f5abc45..7889e2a33 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -52,6 +52,15 @@ class RuntimeRainShader extends RuntimePostEffectShader return intensity = value; } + // the y coord of the puddle, used to mirror things + public var puddleY(default, set):Float = 0; + + function set_puddleY(value:Float):Float + { + this.setFloat('uPuddleY', value); + return puddleY = value; + } + public var groundMap(default, set):BitmapData; function set_groundMap(value:BitmapData):BitmapData @@ -61,14 +70,6 @@ class RuntimeRainShader extends RuntimePostEffectShader return groundMap = value; } - public var puddleMap(default, set):BitmapData; - - function set_puddleMap(value:BitmapData):BitmapData - { - this.setBitmapData('uPuddleMap', value); - return puddleMap = value; - } - public var lightMap(default, set):BitmapData; function set_lightMap(value:BitmapData):BitmapData From ab7ba485cbf81bf1b24628ebae3056e6c3588713 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 24 Sep 2023 03:59:24 +0900 Subject: [PATCH 14/49] added GrabbableCamera --- .../graphics/framebuffer/BitmapDataTools.hx | 43 +++++ .../graphics/framebuffer/FixedBitmapData.hx | 42 +++++ .../graphics/framebuffer/FrameBuffer.hx | 13 +- .../framebuffer/FrameBufferManager.hx | 7 +- .../graphics/framebuffer/GrabbableCamera.hx | 161 ++++++++++++++++++ .../graphics/shaders/RuntimeRainShader.hx | 17 +- source/funkin/play/PlayState.hx | 2 +- source/funkin/play/stage/Stage.hx | 48 +++++- source/funkin/ui/SwagCamera.hx | 3 +- 9 files changed, 306 insertions(+), 30 deletions(-) create mode 100644 source/funkin/graphics/framebuffer/BitmapDataTools.hx create mode 100644 source/funkin/graphics/framebuffer/FixedBitmapData.hx create mode 100644 source/funkin/graphics/framebuffer/GrabbableCamera.hx diff --git a/source/funkin/graphics/framebuffer/BitmapDataTools.hx b/source/funkin/graphics/framebuffer/BitmapDataTools.hx new file mode 100644 index 000000000..9743d7e0b --- /dev/null +++ b/source/funkin/graphics/framebuffer/BitmapDataTools.hx @@ -0,0 +1,43 @@ +package funkin.graphics.framebuffer; + +import flixel.FlxG; +import openfl.display.Bitmap; +import openfl.display.BitmapData; +import openfl.display.Sprite; +import openfl.filters.BitmapFilter; + +/** + * Provides cool stuff for `BitmapData`s that have a hardware texture internally. + */ +class BitmapDataTools +{ + /** + * Applies a bitmap filter to a bitmap immediately. The bitmap filter may refer + * the bitmap itself as a shader input. + * @param bitmap the bitmap data + * @param filter the bitmap filter + */ + public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):Void + { + if (bitmap.readable) + { + FlxG.log.error('do not use `BitmapDataTools` for non-GPU bitmaps!'); + } + // man, allow me to use anon structuers for local vars! + static var cache:{sprite:Sprite, bitmap:Bitmap} = null; + if (cache == null) + { + final sprite = new Sprite(); + final bitmap = new Bitmap(); + sprite.addChild(bitmap); + cache = + { + sprite: sprite, + bitmap: bitmap + } + } + cache.bitmap.bitmapData = bitmap; + cache.sprite.filters = [filter]; + bitmap.draw(cache.sprite); + } +} diff --git a/source/funkin/graphics/framebuffer/FixedBitmapData.hx b/source/funkin/graphics/framebuffer/FixedBitmapData.hx new file mode 100644 index 000000000..00b39ce1c --- /dev/null +++ b/source/funkin/graphics/framebuffer/FixedBitmapData.hx @@ -0,0 +1,42 @@ +package funkin.graphics.framebuffer; + +import openfl.display.BitmapData; +import openfl.display.DisplayObject; +import openfl.display.DisplayObjectContainer; +import openfl.display.IBitmapDrawable; +import openfl.display.OpenGLRenderer; +import openfl.display3D.textures.TextureBase; + +/** + * `BitmapData` is kinda broken so I fixed it. + */ +@:access(openfl.display3D.textures.TextureBase) +@:access(openfl.display.OpenGLRenderer) +class FixedBitmapData extends BitmapData +{ + override function __drawGL(source:IBitmapDrawable, renderer:OpenGLRenderer):Void + { + if (Std.isOfType(source, DisplayObject)) + { + final object:DisplayObjectContainer = cast source; + renderer.__stage = object.stage; + } + super.__drawGL(source, renderer); + } + + /** + * Never use `BitmapData.fromTexture`, always use this. + * @param texture the texture + * @return the bitmap data + */ + public static function fromTexture(texture:TextureBase):FixedBitmapData + { + if (texture == null) return null; + final bitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0); + bitmapData.readable = false; + bitmapData.__texture = texture; + bitmapData.__textureContext = texture.__textureContext; + bitmapData.image = null; + return bitmapData; + } +} diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx index 0209a4aef..786f980ea 100644 --- a/source/funkin/graphics/framebuffer/FrameBuffer.hx +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -1,12 +1,12 @@ package funkin.graphics.framebuffer; -import openfl.geom.Rectangle; -import openfl.geom.Matrix; -import openfl.display.BitmapData; -import flixel.util.FlxColor; import flixel.FlxCamera; +import flixel.util.FlxColor; import openfl.Lib; +import openfl.display.BitmapData; import openfl.display3D.textures.TextureBase; +import openfl.geom.Matrix; +import openfl.geom.Rectangle; /** * A single frame buffer. Used by `FrameBufferManager`. @@ -27,7 +27,6 @@ class FrameBuffer camera = new FlxCamera(); camera.antialiasing = false; camera.bgColor = FlxColor.TRANSPARENT; - camera.flashSprite.cacheAsBitmap = true; @:privateAccess camera.flashSprite.stage = Lib.current.stage; } @@ -41,7 +40,7 @@ class FrameBuffer { dispose(); texture = Lib.current.stage.context3D.createTexture(width, height, BGRA, true); - bitmap = BitmapData.fromTexture(texture); + bitmap = FixedBitmapData.fromTexture(texture); camera.bgColor = bgColor; } @@ -108,7 +107,7 @@ class FrameBuffer bitmap.dispose(); bitmap = null; } - spriteCopies.clear(); + spriteCopies.resize(0); } /** diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index 6cfadd468..5ed4afb3a 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -1,9 +1,10 @@ package funkin.graphics.framebuffer; +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.FlxSprite; import flixel.util.FlxColor; import openfl.display.BitmapData; -import flixel.FlxSprite; -import flixel.FlxCamera; /** * Manages frame buffers and gives access to each frame buffer. @@ -56,7 +57,7 @@ class FrameBufferManager } /** - * Call this before everything is drawn. + * Call this before drawing anything. */ public function lock():Void { diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/framebuffer/GrabbableCamera.hx new file mode 100644 index 000000000..34fb8c9ca --- /dev/null +++ b/source/funkin/graphics/framebuffer/GrabbableCamera.hx @@ -0,0 +1,161 @@ +package funkin.graphics.framebuffer; + +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.graphics.FlxGraphic; +import flixel.graphics.frames.FlxFrame; +import flixel.math.FlxMatrix; +import flixel.math.FlxRect; +import flixel.system.FlxAssets.FlxShader; +import openfl.Lib; +import openfl.display.BitmapData; +import openfl.display3D.textures.TextureBase; + +/** + * A camera, but grabbable. + */ +@:access(openfl.display.DisplayObject) +@:access(openfl.display.BitmapData) +@:access(openfl.display3D.Context3D) +@:access(openfl.display3D.textures.TextureBase) +@:access(flixel.graphics.FlxGraphic) +@:access(flixel.graphics.frames.FlxFrame) +class GrabbableCamera extends FlxCamera +{ + final grabbed:Array = []; + final texturePool:Array = []; + final defaultShader:FlxShader = new FlxShader(); + + final bgTexture:TextureBase; + final bgBitmap:BitmapData; + final bgFrame:FlxFrame; + + public function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0) + { + super(x, y, width, height, zoom); + bgTexture = pickTexture(width, height); + bgBitmap = FixedBitmapData.fromTexture(bgTexture); + bgFrame = new FlxFrame(new FlxGraphic('', null)); + bgFrame.parent.bitmap = bgBitmap; + bgFrame.frame = new FlxRect(); + } + + /** + * Grabs the camera screen and returns it as a `BitmapData`. The returned bitmap + * will not be referred by the camera, so changing it will not affect the scene. + * @param applyFilters if this is `true`, the camera's filters will be applied to the grabbed bitmap + * @return the grabbed bitmap data + */ + public function grabScreen(applyFilters:Bool):BitmapData + { + final texture = pickTexture(width, height); + final bitmap = FixedBitmapData.fromTexture(texture); + squashTo(bitmap, applyFilters); + return bitmap; + } + + function squashTo(bitmap:BitmapData, applyFilters:Bool):Void + { + static final matrix = new FlxMatrix(); + + // resize the background bitmap if needed + if (bgTexture.__width != width || bgTexture.__height != height) + { + resizeTexture(bgTexture, width, height); + bgBitmap.__resize(width, height); + bgFrame.parent.bitmap = bgBitmap; + } + + // grab the bitmap + render(); + bitmap.fillRect(bitmap.rect, 0); + matrix.setTo(1, 0, 0, 1, flashSprite.x, flashSprite.y); + if (applyFilters) + { + bitmap.draw(flashSprite, matrix); + } + else + { + final tmp = flashSprite.filters; + flashSprite.filters = null; + bitmap.draw(flashSprite, matrix); + flashSprite.filters = tmp; + } + + // also copy to the background bitmap + bgBitmap.fillRect(bgBitmap.rect, 0); + bgBitmap.draw(bitmap); + + // clear graphics data + super.clearDrawStack(); + canvas.graphics.clear(); + + // render the background bitmap + bgFrame.frame.set(0, 0, width, height); + matrix.setTo(viewWidth / width, 0, 0, viewHeight / height, viewMarginLeft, viewMarginTop); + drawPixels(bgFrame, matrix); + } + + function resizeTexture(texture:TextureBase, width:Int, height:Int):Void + { + texture.__width = width; + texture.__height = height; + final context = texture.__context; + final gl = context.gl; + context.__bindGLTexture2D(texture.__textureID); + gl.texImage2D(gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, gl.UNSIGNED_BYTE, null); + context.__bindGLTexture2D(null); + } + + override function destroy():Void + { + super.destroy(); + disposeTextures(); + } + + override function clearDrawStack():Void + { + super.clearDrawStack(); + // also clear grabbed bitmaps + for (bitmap in grabbed) + { + texturePool.push(@:privateAccess bitmap.__texture); + bitmap.dispose(); + } + grabbed.clear(); + } + + function pickTexture(width:Int, height:Int):TextureBase + { + // zero-sized textures will be problematic + width = width < 1 ? 1 : width; + height = height < 1 ? 1 : height; + if (texturePool.length > 0) + { + final res = texturePool.pop(); + if (res.__width != width || res.__height != height) + { + resizeTexture(res, width, height); + } + return res; + } + return Lib.current.stage.context3D.createTexture(width, height, BGRA, true); + } + + function disposeTextures():Void + { + trace('disposing textures'); + for (bitmap in grabbed) + { + bitmap.dispose(); + } + grabbed.clear(); + for (texture in texturePool) + { + texture.dispose(); + } + texturePool.resize(0); + bgTexture.dispose(); + bgBitmap.dispose(); + } +} diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 7889e2a33..77608b98e 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -61,21 +61,12 @@ class RuntimeRainShader extends RuntimePostEffectShader return puddleY = value; } - public var groundMap(default, set):BitmapData; + public var blurredScreen(default, set):BitmapData; - function set_groundMap(value:BitmapData):BitmapData + function set_blurredScreen(value:BitmapData):BitmapData { - this.setBitmapData('uGroundMap', value); - // this.setFloat2('uPuddleTextureSize', value.width, value.height); - return groundMap = value; - } - - public var lightMap(default, set):BitmapData; - - function set_lightMap(value:BitmapData):BitmapData - { - this.setBitmapData('uLightMap', value); - return lightMap = value; + this.setBitmapData('uBlurredScreen', value); + return blurredScreen = value; } public var mask(default, set):BitmapData; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 75a311a45..9fa6929d9 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -411,7 +411,7 @@ class PlayState extends MusicBeatSubState /** * The camera which contains, and controls visibility of, the stage and characters. */ - public var camGame:FlxCamera; + public var camGame:SwagCamera; /** * The camera which contains, and controls visibility of, a video cutscene. diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index ada8c898d..def67bdbc 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -3,6 +3,7 @@ package funkin.play.stage; import funkin.graphics.framebuffer.FrameBufferManager; import flixel.util.FlxColor; import funkin.graphics.framebuffer.SpriteCopy; +import funkin.graphics.framebuffer.GrabbableCamera; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; @@ -78,7 +79,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass { if (frameBufferMan != null) frameBufferMan.dispose(); frameBufferMan = new FrameBufferManager(FlxG.camera); - onFrameBufferCreate(); + setupFrameBuffers(); buildStage(); this.refresh(); @@ -696,7 +697,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass debugIconGroup = null; } - frameBufferMan.dispose(); + if (frameBufferMan != null) + { + frameBufferMan.dispose(); + } } /** @@ -751,16 +755,50 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass override function draw():Void { - frameBufferMan.lock(); + if (frameBufferMan != null) + { + frameBufferMan.lock(); + } super.draw(); - frameBufferMan.unlock(); + if (frameBufferMan != null) + { + frameBufferMan.unlock(); + } + frameBuffersUpdated(); } /** * Called when the frame buffer manager is ready. * Create frame buffers inside this method. */ - public function onFrameBufferCreate():Void {} + function setupFrameBuffers():Void {} + + /** + * Called when all the frame buffers are updated. If you need any + * frame buffers before `grabScreen()`, make sure you + * grab the screen inside this method since it immediately uses the + * frame buffers. + */ + function frameBuffersUpdated():Void {} + + /** + * Grabs the current screen and returns it as a bitmap data. You can sefely modify it. + * @param applyFilters if this is `true`, the filters set to the camera will be applied to the resulting bitmap + * @return the grabbed screen + */ + function grabScreen(applyFilters:Bool):BitmapData + { + if (Std.isOfType(FlxG.camera, GrabbableCamera)) + { + final cam:GrabbableCamera = cast FlxG.camera; + return cam.grabScreen(applyFilters); + } + else + { + FlxG.log.error('cannot grab the screen: the main camera is not grabbable'); + return null; + } + } public function onScriptEvent(event:ScriptEvent) {} diff --git a/source/funkin/ui/SwagCamera.hx b/source/funkin/ui/SwagCamera.hx index 70791d38f..bb6a24c3d 100644 --- a/source/funkin/ui/SwagCamera.hx +++ b/source/funkin/ui/SwagCamera.hx @@ -1,11 +1,12 @@ package funkin.ui; +import funkin.graphics.framebuffer.GrabbableCamera; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.math.FlxPoint; import funkin.util.MathUtil; -class SwagCamera extends FlxCamera +class SwagCamera extends GrabbableCamera { /** * properly follow framerate From f7a519b13fb6fc7610d00189f128905c03bc4fa5 Mon Sep 17 00:00:00 2001 From: shr Date: Sun, 24 Sep 2023 04:09:19 +0900 Subject: [PATCH 15/49] cleaning code --- source/funkin/graphics/framebuffer/GrabbableCamera.hx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/framebuffer/GrabbableCamera.hx index 34fb8c9ca..e7ebb8e52 100644 --- a/source/funkin/graphics/framebuffer/GrabbableCamera.hx +++ b/source/funkin/graphics/framebuffer/GrabbableCamera.hx @@ -1,12 +1,10 @@ package funkin.graphics.framebuffer; import flixel.FlxCamera; -import flixel.FlxG; import flixel.graphics.FlxGraphic; import flixel.graphics.frames.FlxFrame; import flixel.math.FlxMatrix; import flixel.math.FlxRect; -import flixel.system.FlxAssets.FlxShader; import openfl.Lib; import openfl.display.BitmapData; import openfl.display3D.textures.TextureBase; @@ -24,7 +22,6 @@ class GrabbableCamera extends FlxCamera { final grabbed:Array = []; final texturePool:Array = []; - final defaultShader:FlxShader = new FlxShader(); final bgTexture:TextureBase; final bgBitmap:BitmapData; @@ -119,8 +116,8 @@ class GrabbableCamera extends FlxCamera // also clear grabbed bitmaps for (bitmap in grabbed) { - texturePool.push(@:privateAccess bitmap.__texture); - bitmap.dispose(); + texturePool.push(bitmap.__texture); + bitmap.dispose(); // this doesn't release the texture } grabbed.clear(); } From bc20b8172362092226630bcb697d066abac447bf Mon Sep 17 00:00:00 2001 From: shr Date: Tue, 26 Sep 2023 03:28:55 +0900 Subject: [PATCH 16/49] squiiish --- source/funkin/graphics/shaders/RuntimeRainShader.hx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 77608b98e..5675f26e6 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -61,6 +61,15 @@ class RuntimeRainShader extends RuntimePostEffectShader return puddleY = value; } + // the y scale of the puddle, the less this value the more the puddle effects squished + public var puddleScaleY(default, set):Float = 0; + + function set_puddleScaleY(value:Float):Float + { + this.setFloat('uPuddleScaleY', value); + return puddleScaleY = value; + } + public var blurredScreen(default, set):BitmapData; function set_blurredScreen(value:BitmapData):BitmapData From dc8dfeb3367de62b139a827f62557171272c9354 Mon Sep 17 00:00:00 2001 From: shr Date: Mon, 2 Oct 2023 21:05:13 +0900 Subject: [PATCH 17/49] add lightmaps --- .../funkin/graphics/framebuffer/FrameBuffer.hx | 11 +++++++++++ .../graphics/framebuffer/FrameBufferManager.hx | 12 +++++++++++- .../funkin/graphics/framebuffer/SpriteCopy.hx | 2 +- .../graphics/shaders/RuntimeRainShader.hx | 8 ++++++++ source/funkin/play/stage/Stage.hx | 17 +++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/source/funkin/graphics/framebuffer/FrameBuffer.hx b/source/funkin/graphics/framebuffer/FrameBuffer.hx index 786f980ea..e99f72b77 100644 --- a/source/funkin/graphics/framebuffer/FrameBuffer.hx +++ b/source/funkin/graphics/framebuffer/FrameBuffer.hx @@ -1,5 +1,6 @@ package funkin.graphics.framebuffer; +import flixel.FlxSprite; import flixel.FlxCamera; import flixel.util.FlxColor; import openfl.Lib; @@ -118,4 +119,14 @@ class FrameBuffer { spriteCopies.push(spriteCopy); } + + /** + * Adds the sprite to the frame buffer. The sprite will only be seen from + * the frame buffer. + * @param sprite the sprite + */ + public function moveSprite(sprite:FlxSprite):Void + { + sprite.cameras = [camera]; + } } diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index 5ed4afb3a..6a0aaac53 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -51,11 +51,21 @@ class FrameBufferManager * @param color if this is not `null`, the sprite will be filled with the color. * if this is `null`, the sprite will keep its original color. */ - public function addSpriteCopyTo(name:String, sprite:FlxSprite, color:Null = null):Void + public function copySpriteTo(name:String, sprite:FlxSprite, color:Null = null):Void { frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color)); } + /** + * Adds the sprite to the frame buffer. The sprite will only be seen from the frame buffer. + * @param name the name of the frame buffer + * @param sprite the sprite + */ + public function moveSpriteTo(name:String, sprite:FlxSprite):Void + { + frameBufferMap[name].moveSprite(sprite); + } + /** * Call this before drawing anything. */ diff --git a/source/funkin/graphics/framebuffer/SpriteCopy.hx b/source/funkin/graphics/framebuffer/SpriteCopy.hx index b95462ea3..ea7de69dc 100644 --- a/source/funkin/graphics/framebuffer/SpriteCopy.hx +++ b/source/funkin/graphics/framebuffer/SpriteCopy.hx @@ -10,7 +10,7 @@ import flixel.util.FlxColor; class SpriteCopy { final sprite:FlxSprite; - var color:Null; + final color:Null; public function new(sprite:FlxSprite, color:Null) { diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 5675f26e6..dc35a7bb7 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -86,6 +86,14 @@ class RuntimeRainShader extends RuntimePostEffectShader return mask = value; } + public var lightMap(default, set):BitmapData; + + function set_lightMap(value:BitmapData):BitmapData + { + this.setBitmapData('uLightMap', value); + return lightMap = value; + } + public var numLights(default, set):Int = 0; function set_numLights(value:Int):Int diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index def67bdbc..298484a97 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -1,5 +1,6 @@ package funkin.play.stage; +import openfl.display.BlendMode; import funkin.graphics.framebuffer.FrameBufferManager; import flixel.util.FlxColor; import funkin.graphics.framebuffer.SpriteCopy; @@ -800,6 +801,22 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass } } + /** + * FIXME: Polymod hack + */ + function stringEndsWith(a:String, b:String):Bool + { + return a.endsWith(b); + } + + /** + * FIXME: Polymod hack + */ + function setBlendAdd(sprite:FlxSprite, blend:BlendMode):Void + { + sprite.blend = blend; + } + public function onScriptEvent(event:ScriptEvent) {} public function onPause(event:PauseScriptEvent) {} From 90036ee75d146671c5a6d6702f935602a5f755cf Mon Sep 17 00:00:00 2001 From: shr Date: Tue, 3 Oct 2023 03:17:30 +0900 Subject: [PATCH 18/49] removing da dumb needless functions --- source/funkin/play/stage/Stage.hx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 298484a97..639ed02da 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -801,22 +801,6 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass } } - /** - * FIXME: Polymod hack - */ - function stringEndsWith(a:String, b:String):Bool - { - return a.endsWith(b); - } - - /** - * FIXME: Polymod hack - */ - function setBlendAdd(sprite:FlxSprite, blend:BlendMode):Void - { - sprite.blend = blend; - } - public function onScriptEvent(event:ScriptEvent) {} public function onPause(event:PauseScriptEvent) {} From 7fcf4c4175c07c412e48c45ac6ee40c0c13cb76e Mon Sep 17 00:00:00 2001 From: shr Date: Tue, 12 Dec 2023 02:23:33 +0900 Subject: [PATCH 19/49] added custom blend feature --- .../graphics/framebuffer/BitmapDataTools.hx | 43 ------ .../graphics/framebuffer/BitmapDataUtil.hx | 123 ++++++++++++++++ .../graphics/framebuffer/GrabbableCamera.hx | 132 +++++++++++++++--- .../shaders/RuntimeCustomBlendShader.hx | 29 ++++ 4 files changed, 261 insertions(+), 66 deletions(-) delete mode 100644 source/funkin/graphics/framebuffer/BitmapDataTools.hx create mode 100644 source/funkin/graphics/framebuffer/BitmapDataUtil.hx create mode 100644 source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx diff --git a/source/funkin/graphics/framebuffer/BitmapDataTools.hx b/source/funkin/graphics/framebuffer/BitmapDataTools.hx deleted file mode 100644 index 9743d7e0b..000000000 --- a/source/funkin/graphics/framebuffer/BitmapDataTools.hx +++ /dev/null @@ -1,43 +0,0 @@ -package funkin.graphics.framebuffer; - -import flixel.FlxG; -import openfl.display.Bitmap; -import openfl.display.BitmapData; -import openfl.display.Sprite; -import openfl.filters.BitmapFilter; - -/** - * Provides cool stuff for `BitmapData`s that have a hardware texture internally. - */ -class BitmapDataTools -{ - /** - * Applies a bitmap filter to a bitmap immediately. The bitmap filter may refer - * the bitmap itself as a shader input. - * @param bitmap the bitmap data - * @param filter the bitmap filter - */ - public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):Void - { - if (bitmap.readable) - { - FlxG.log.error('do not use `BitmapDataTools` for non-GPU bitmaps!'); - } - // man, allow me to use anon structuers for local vars! - static var cache:{sprite:Sprite, bitmap:Bitmap} = null; - if (cache == null) - { - final sprite = new Sprite(); - final bitmap = new Bitmap(); - sprite.addChild(bitmap); - cache = - { - sprite: sprite, - bitmap: bitmap - } - } - cache.bitmap.bitmapData = bitmap; - cache.sprite.filters = [filter]; - bitmap.draw(cache.sprite); - } -} diff --git a/source/funkin/graphics/framebuffer/BitmapDataUtil.hx b/source/funkin/graphics/framebuffer/BitmapDataUtil.hx new file mode 100644 index 000000000..7b49705e0 --- /dev/null +++ b/source/funkin/graphics/framebuffer/BitmapDataUtil.hx @@ -0,0 +1,123 @@ +package funkin.graphics.framebuffer; + +import flixel.FlxG; +import openfl.Lib; +import openfl.display.Bitmap; +import openfl.display.BitmapData; +import openfl.display.Sprite; +import openfl.display3D.Context3DTextureFormat; +import openfl.display3D.textures.TextureBase; +import openfl.filters.BitmapFilter; + +/** + * Provides cool stuff for `BitmapData`s that have a hardware texture internally. + */ +@:access(openfl.display.BitmapData) +@:access(openfl.display3D.textures.TextureBase) +@:access(openfl.display3D.Context3D) +class BitmapDataUtil +{ + static function getCache():{sprite:Sprite, bitmap:Bitmap} + { + static var cache:{sprite:Sprite, bitmap:Bitmap} = null; + if (cache == null) + { + final sprite = new Sprite(); + final bitmap = new Bitmap(); + sprite.addChild(bitmap); + cache = + { + sprite: sprite, + bitmap: bitmap + } + } + return cache; + } + + /** + * Applies a bitmap filter to a bitmap immediately. The bitmap filter may refer + * the bitmap itself as a shader input. + * @param bitmap the bitmap data + * @param filter the bitmap filter + */ + public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):Void + { + hardwareCheck(bitmap); + final cache = getCache(); + cache.bitmap.bitmapData = bitmap; + cache.sprite.filters = [filter]; + bitmap.draw(cache.sprite); + } + + /** + * Creates a bitmap with a hardware texture. + * @param width the width + * @param height the height + * @param format the format if the internal texture + * @return the bitmap + */ + public static function create(width:Int, height:Int, format:Context3DTextureFormat = BGRA):FixedBitmapData + { + final texture = Lib.current.stage.context3D.createTexture(width, height, format, true); + return FixedBitmapData.fromTexture(texture); + } + + /** + * Resizes the bitmap. + * @param bitmap the bitmap data + * @param width the width + * @param height the height + */ + public static function resize(bitmap:BitmapData, width:Int, height:Int):Void + { + hardwareCheck(bitmap); + if (bitmap.width == width && bitmap.height == height) return; + bitmap.width = width; + bitmap.height = height; + resizeTexture(bitmap.__texture, width, height); + } + + /** + * Resizes the texture. + * @param texture the texture + * @param width the width + * @param height the height + */ + public static function resizeTexture(texture:TextureBase, width:Int, height:Int):Void + { + if (texture.__width == width && texture.__height == height) return; + texture.__width = width; + texture.__height = height; + final context = texture.__context; + final gl = context.gl; + context.__bindGLTexture2D(texture.__textureID); + gl.texImage2D(gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, gl.UNSIGNED_BYTE, null); + context.__bindGLTexture2D(null); + } + + /** + * Copies the content of `src` to `dst`. The destination bitmap `dst` will be resized + * so that it has the same size as `src`. + * @param dst the destination bitmap + * @param src the source bitmap + */ + public static function copy(dst:BitmapData, src:BitmapData):Void + { + hardwareCheck(dst); + hardwareCheck(src); + final cache = getCache(); + cache.bitmap.bitmapData = src; + cache.sprite.filters = null; + resize(dst, src.width, src.height); + dst.fillRect(dst.rect, 0); + dst.draw(cache.sprite); + } + + static function hardwareCheck(bitmap:BitmapData):Void + { + if (bitmap.readable) + { + FlxG.log.error('do not use `BitmapDataUtil` for non-GPU bitmaps!'); + } + } +} diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/framebuffer/GrabbableCamera.hx index e7ebb8e52..266420e5f 100644 --- a/source/funkin/graphics/framebuffer/GrabbableCamera.hx +++ b/source/funkin/graphics/framebuffer/GrabbableCamera.hx @@ -1,16 +1,22 @@ package funkin.graphics.framebuffer; +import flash.geom.ColorTransform; import flixel.FlxCamera; import flixel.graphics.FlxGraphic; import flixel.graphics.frames.FlxFrame; import flixel.math.FlxMatrix; import flixel.math.FlxRect; +import flixel.system.FlxAssets.FlxShader; +import funkin.shaderslmfao.RuntimeCustomBlendShader; import openfl.Lib; import openfl.display.BitmapData; +import openfl.display.BlendMode; import openfl.display3D.textures.TextureBase; +import openfl.filters.BitmapFilter; +import openfl.filters.ShaderFilter; /** - * A camera, but grabbable. + * A camera, but grabbable. Also supports several additional blend modes. */ @:access(openfl.display.DisplayObject) @:access(openfl.display.BitmapData) @@ -27,6 +33,12 @@ class GrabbableCamera extends FlxCamera final bgBitmap:BitmapData; final bgFrame:FlxFrame; + final customBlendShader:RuntimeCustomBlendShader; + final customBlendFilter:ShaderFilter; + + var filtersApplied:Bool = false; + var bgItemCount:Int = 0; + public function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0) { super(x, y, width, height, zoom); @@ -35,41 +47,75 @@ class GrabbableCamera extends FlxCamera bgFrame = new FlxFrame(new FlxGraphic('', null)); bgFrame.parent.bitmap = bgBitmap; bgFrame.frame = new FlxRect(); + customBlendShader = new RuntimeCustomBlendShader(); + customBlendFilter = new ShaderFilter(customBlendShader); } /** * Grabs the camera screen and returns it as a `BitmapData`. The returned bitmap - * will not be referred by the camera, so changing it will not affect the scene. - * @param applyFilters if this is `true`, the camera's filters will be applied to the grabbed bitmap + * will not be referred by the camera so, changing it will not affect the scene. + * The returned bitmap **will be reused in the next frame**, so the content is available + * only in the current frame. + * @param applyFilters if this is `true`, the camera's filters will be applied to the grabbed bitmap, + * and the camera's filters will be disabled until the beginning of the next frame + * @param isolate if this is `true`, sprites to be rendered will only be rendered to the grabbed bitmap, + * and the grabbed bitmap will not include any previously rendered sprites * @return the grabbed bitmap data */ - public function grabScreen(applyFilters:Bool):BitmapData + public function grabScreen(applyFilters:Bool, isolate:Bool = false):BitmapData { final texture = pickTexture(width, height); final bitmap = FixedBitmapData.fromTexture(texture); - squashTo(bitmap, applyFilters); + squashTo(bitmap, applyFilters, isolate); + grabbed.push(bitmap); return bitmap; } - function squashTo(bitmap:BitmapData, applyFilters:Bool):Void + /** + * Applies the filter immediately to the camera. This will be done independently from + * the camera's filters. This method can only be called after the first `grabScreen` + * in the frame. + * @param filter the filter + */ + public function applyFilter(filter:BitmapFilter):Void { + if (grabbed.length == 0) + { + FlxG.log.error('grab screen before you can apply a filter!'); + return; + } + BitmapDataUtil.applyFilter(bgBitmap, filter); + } + + function squashTo(bitmap:BitmapData, applyFilters:Bool, isolate:Bool):Void + { + if (applyFilters && isolate) + { + FlxG.log.error('cannot apply filters while isolating!'); + } + if (filtersApplied && applyFilters) + { + FlxG.log.warn('filters already applied!'); + } static final matrix = new FlxMatrix(); // resize the background bitmap if needed if (bgTexture.__width != width || bgTexture.__height != height) { - resizeTexture(bgTexture, width, height); + BitmapDataUtil.resizeTexture(bgTexture, width, height); bgBitmap.__resize(width, height); bgFrame.parent.bitmap = bgBitmap; } // grab the bitmap - render(); + renderSkipping(isolate ? bgItemCount : 0); bitmap.fillRect(bitmap.rect, 0); matrix.setTo(1, 0, 0, 1, flashSprite.x, flashSprite.y); if (applyFilters) { bitmap.draw(flashSprite, matrix); + flashSprite.filters = null; + filtersApplied = true; } else { @@ -79,9 +125,12 @@ class GrabbableCamera extends FlxCamera flashSprite.filters = tmp; } - // also copy to the background bitmap - bgBitmap.fillRect(bgBitmap.rect, 0); - bgBitmap.draw(bitmap); + if (!isolate) + { + // also copy to the background bitmap + bgBitmap.fillRect(bgBitmap.rect, 0); + bgBitmap.draw(bitmap); + } // clear graphics data super.clearDrawStack(); @@ -91,17 +140,54 @@ class GrabbableCamera extends FlxCamera bgFrame.frame.set(0, 0, width, height); matrix.setTo(viewWidth / width, 0, 0, viewHeight / height, viewMarginLeft, viewMarginTop); drawPixels(bgFrame, matrix); + + // count background draw items for future isolation + bgItemCount = 0; + { + var item = _headOfDrawStack; + while (item != null) + { + item = item.next; + bgItemCount++; + } + } } - function resizeTexture(texture:TextureBase, width:Int, height:Int):Void + function renderSkipping(count:Int):Void { - texture.__width = width; - texture.__height = height; - final context = texture.__context; - final gl = context.gl; - context.__bindGLTexture2D(texture.__textureID); - gl.texImage2D(gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, gl.UNSIGNED_BYTE, null); - context.__bindGLTexture2D(null); + var item = _headOfDrawStack; + while (item != null) + { + if (--count < 0) item.render(this); + item = item.next; + } + } + + override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, + ?shader:FlxShader):Void + { + if ( switch blend + { + case DARKEN | HARDLIGHT | LIGHTEN | OVERLAY: true; + case _: false; + }) + { + // squash the screen + grabScreen(false); + // render without blend + super.drawPixels(frame, pixels, matrix, transform, null, smoothing, shader); + // get the isolated bitmap + final isolated = grabScreen(false, true); + // apply fullscreen blend + customBlendShader.blend = blend; + customBlendShader.source = isolated; + customBlendShader.updateViewInfo(FlxG.width, FlxG.height, this); + applyFilter(customBlendFilter); + } + else + { + super.drawPixels(frame, pixels, matrix, transform, blend, smoothing, shader); + } } override function destroy():Void @@ -120,6 +206,9 @@ class GrabbableCamera extends FlxCamera bitmap.dispose(); // this doesn't release the texture } grabbed.clear(); + // clear filters applied flag + filtersApplied = false; + bgItemCount = 0; } function pickTexture(width:Int, height:Int):TextureBase @@ -130,10 +219,7 @@ class GrabbableCamera extends FlxCamera if (texturePool.length > 0) { final res = texturePool.pop(); - if (res.__width != width || res.__height != height) - { - resizeTexture(res, width, height); - } + BitmapDataUtil.resizeTexture(res, width, height); return res; } return Lib.current.stage.context3D.createTexture(width, height, BGRA, true); diff --git a/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx b/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx new file mode 100644 index 000000000..627df46ff --- /dev/null +++ b/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx @@ -0,0 +1,29 @@ +package funkin.shaderslmfao; + +import openfl.display.BitmapData; +import openfl.display.BlendMode; +import openfl.utils.Assets; + +class RuntimeCustomBlendShader extends RuntimePostEffectShader +{ + public var source(default, set):BitmapData; + + function set_source(value:BitmapData):BitmapData + { + this.setBitmapData("source", value); + return source = value; + } + + public var blend(default, set):BlendMode; + + function set_blend(value:BlendMode):BlendMode + { + this.setInt("blendMode", cast value); + return blend = value; + } + + public function new() + { + super(Assets.getText("assets/shaders/customBlend.frag")); + } +} From 360ba694967374c423d1ca4024660bb0cd03c395 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 18 Dec 2023 20:49:01 -0500 Subject: [PATCH 20/49] shaderslmfao fix --- assets | 2 +- .../graphics/framebuffer/GrabbableCamera.hx | 2 +- .../funkin/graphics/shaders/PuddleShader.hx | 2 +- .../shaders/RuntimeCustomBlendShader.hx | 34 +++++++++---------- .../shaders/RuntimePostEffectShader.hx | 2 +- .../graphics/shaders/RuntimeRainShader.hx | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/assets b/assets index c354795f7..200fa60df 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c354795f7f560fa096b855c6e6bca745f77fa414 +Subproject commit 200fa60dfb116bda6c438b0bb3e9ad541b2180ea diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/framebuffer/GrabbableCamera.hx index 266420e5f..0fa372123 100644 --- a/source/funkin/graphics/framebuffer/GrabbableCamera.hx +++ b/source/funkin/graphics/framebuffer/GrabbableCamera.hx @@ -7,7 +7,7 @@ import flixel.graphics.frames.FlxFrame; import flixel.math.FlxMatrix; import flixel.math.FlxRect; import flixel.system.FlxAssets.FlxShader; -import funkin.shaderslmfao.RuntimeCustomBlendShader; +import funkin.graphics.shaders.RuntimeCustomBlendShader; import openfl.Lib; import openfl.display.BitmapData; import openfl.display.BlendMode; diff --git a/source/funkin/graphics/shaders/PuddleShader.hx b/source/funkin/graphics/shaders/PuddleShader.hx index 8e04c6453..352568737 100644 --- a/source/funkin/graphics/shaders/PuddleShader.hx +++ b/source/funkin/graphics/shaders/PuddleShader.hx @@ -1,4 +1,4 @@ -package funkin.shaderslmfao; +package funkin.graphics.shaders; import flixel.addons.display.FlxRuntimeShader; import openfl.Assets; diff --git a/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx b/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx index 627df46ff..a07124d23 100644 --- a/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx +++ b/source/funkin/graphics/shaders/RuntimeCustomBlendShader.hx @@ -1,4 +1,4 @@ -package funkin.shaderslmfao; +package funkin.graphics.shaders; import openfl.display.BitmapData; import openfl.display.BlendMode; @@ -6,24 +6,24 @@ import openfl.utils.Assets; class RuntimeCustomBlendShader extends RuntimePostEffectShader { - public var source(default, set):BitmapData; + public var source(default, set):BitmapData; - function set_source(value:BitmapData):BitmapData - { - this.setBitmapData("source", value); - return source = value; - } + function set_source(value:BitmapData):BitmapData + { + this.setBitmapData("source", value); + return source = value; + } - public var blend(default, set):BlendMode; + public var blend(default, set):BlendMode; - function set_blend(value:BlendMode):BlendMode - { - this.setInt("blendMode", cast value); - return blend = value; - } + function set_blend(value:BlendMode):BlendMode + { + this.setInt("blendMode", cast value); + return blend = value; + } - public function new() - { - super(Assets.getText("assets/shaders/customBlend.frag")); - } + public function new() + { + super(Assets.getText("assets/shaders/customBlend.frag")); + } } diff --git a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx index 7488d086f..9f49da075 100644 --- a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx +++ b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx @@ -1,4 +1,4 @@ -package funkin.shaderslmfao; +package funkin.graphics.shaders; import flixel.FlxCamera; import flixel.FlxG; diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index dc35a7bb7..05bc68f72 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -1,4 +1,4 @@ -package funkin.shaderslmfao; +package funkin.graphics.shaders; import flixel.system.FlxAssets.FlxShader; import openfl.display.BitmapData; From 48f916240f5b7f2e0909a4db9d2d236ebbba1a3a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 18 Dec 2023 21:33:29 -0500 Subject: [PATCH 21/49] playstate import fixes and asset update --- assets | 2 +- source/funkin/play/PlayState.hx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 200fa60df..2cba31629 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 200fa60dfb116bda6c438b0bb3e9ad541b2180ea +Subproject commit 2cba3162990db807be054889b23cf214a1d8dd2d diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 9fa6929d9..5f53e9388 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -38,7 +38,7 @@ import openfl.geom.Rectangle; import funkin.audio.VoicesGroup; import funkin.save.Save; import funkin.Highscore.Tallies; -import funkin.NoteSplash; +import funkin.play.notes.NoteSplash; import funkin.audio.VoicesGroup; import funkin.data.notestyle.NoteStyleRegistry; import funkin.input.PreciseInputManager; @@ -71,6 +71,8 @@ import funkin.ui.debug.stage.StageOffsetSubState; import funkin.ui.story.StoryMenuState; import funkin.util.SerializerUtil; import lime.ui.Haptic; +import funkin.play.cutscene.dialogue.Conversation; +import funkin.play.cutscene.dialogue.ConversationDataParser; #if discord_rpc import Discord.DiscordClient; #end From 167976c8ba50befeee6d6625a8b2674bdf852649 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 5 Feb 2024 13:35:30 -0500 Subject: [PATCH 22/49] Work in progress --- assets | 2 +- source/funkin/play/PlayState.hx | 17 ++++++++--- source/funkin/play/song/Song.hx | 34 +++++++++++++++------- source/funkin/ui/freeplay/FreeplayState.hx | 23 +++++++++++++-- source/funkin/util/tools/ArrayTools.hx | 18 ++++++++++++ 5 files changed, 76 insertions(+), 18 deletions(-) diff --git a/assets b/assets index b2f8b6a78..5479e17b1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b2f8b6a780316959d0a79adc6dbf61f9e4ca675f +Subproject commit 5479e17b1085f72e05f1c1f9e0a668ef832d3341 diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index cc9debf13..763c05640 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -87,6 +87,12 @@ typedef PlayStateParams = * @default `bf`, or the first character in the song's character list. */ ?targetCharacter:String, + /** + * The instrumental to play with. + * Significant if the `targetSong` supports alternate instrumentals. + * @default `null` + */ + ?targetInstrumental:String, /** * Whether the song should start in Practice Mode. * @default `false` @@ -448,7 +454,7 @@ class PlayState extends MusicBeatSubState function get_currentChart():SongDifficulty { if (currentSong == null || currentDifficulty == null) return null; - return currentSong.getDifficulty(currentDifficulty); + return currentSong.getDifficulty(currentDifficulty, currentPlayerId); } /** @@ -2650,13 +2656,16 @@ class PlayState extends MusicBeatSubState { // Stop the music. FlxG.sound.music.pause(); - vocals.stop(); + if (vocals != null) vocals.stop(); } else { FlxG.sound.music.pause(); - vocals.pause(); - remove(vocals); + if (vocals != null) + { + vocals.pause(); + remove(vocals); + } } // Remove reference to stage and remove sprites from it to save memory. diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 0434607f3..52a1ba6f8 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -184,7 +184,10 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + public inline function getDifficulty(?diffId:String, ?variation:String):Null { if (diffId == null) diffId = listDifficulties()[0]; - return difficulties.get(diffId); + if (variation == null) variation = Constants.DEFAULT_VARIATION; + var variationSuffix = (variation != null && variation != '' && variation != Constants.DEFAULT_VARIATION) ? '-$variation' : ''; + + return difficulties.get('$diffId$variationSuffix'); } /** @@ -272,12 +279,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = difficulties.keys().array().filter(function(diffId:String):Bool { - if (variationId == null) return true; + // The difficulties array contains entries like 'normal', 'nightmare-erect', and 'normal-pico', + // so we have to map it to the actual difficulty names. + // We also filter out difficulties that don't match the variation or that don't exist. + + var diffFiltered:Array = difficulties.keys().array().map(function(diffId:String):Null { var difficulty:Null = difficulties.get(diffId); - if (difficulty == null) return false; - return difficulty.variation == variationId; - }); + if (difficulty == null) return null; + if (variationId != null && difficulty.variation != variationId) return null; + return difficulty.difficulty; + }).nonNull().unique(); diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST)); @@ -286,8 +297,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = difficulties.get(diffId); + if (variationId == '') variationId = Constants.DEFAULT_VARIATION; + var variationSuffix:String = (variationId == Constants.DEFAULT_VARIATION) ? '' : '-$variationId'; + var difficulty:Null = difficulties.get('$diffId$variationSuffix'); return variationId == null ? (difficulty != null) : (difficulty != null && difficulty.variation == variationId); } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index de6484fd3..f981357cf 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -54,6 +54,14 @@ import funkin.util.MathUtil; import lime.app.Future; import lime.utils.Assets; +/** + * Parameters used to initialize the FreeplayState. + */ +typedef FreeplayStateParams = +{ + ?character:String; +}; + class FreeplayState extends MusicBeatSubState { var songs:Array> = []; @@ -64,6 +72,9 @@ class FreeplayState extends MusicBeatSubState var curSelected:Int = 0; var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY; + // Params + var currentCharacter:String; + var fp:FreeplayScore; var txtCompletion:AtlasText; var lerpCompletion:Float = 0; @@ -99,12 +110,13 @@ class FreeplayState extends MusicBeatSubState var stickerSubState:StickerSubState; - // static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; static var rememberedSongId:Null = null; - public function new(?stickers:StickerSubState = null) + public function new(?params:FreeplayParams, ?stickers:StickerSubState) { + currentCharacter = params?.character ?? Constants.DEFAULT_CHARACTER; + if (stickers != null) { stickerSubState = stickers; @@ -844,6 +856,13 @@ class FreeplayState extends MusicBeatSubState changeDiff(1); } + // TODO: DEBUG REMOVE THIS + if (FlxG.keys.justPressed.P) + { + currentCharacter = (currentCharacter == "bf") ? "pico" : "bf"; + changeSelection(0); + } + if (controls.BACK && !typing.hasFocus) { FlxTween.globalManager.clear(); diff --git a/source/funkin/util/tools/ArrayTools.hx b/source/funkin/util/tools/ArrayTools.hx index 0209cfc19..caf8e8aab 100644 --- a/source/funkin/util/tools/ArrayTools.hx +++ b/source/funkin/util/tools/ArrayTools.hx @@ -23,6 +23,24 @@ class ArrayTools return result; } + /** + * Returns a copy of the array with all `null` elements removed. + * @param array The array to remove `null` elements from. + * @return A copy of the array with all `null` elements removed. + */ + public static function nonNull(array:Array>):Array + { + var result:Array = []; + for (element in array) + { + if (element != null) + { + result.push(element); + } + } + return result; + } + /** * Return the first element of the array that satisfies the predicate, or null if none do. * @param input The array to search From e5fb1de4ba74412e7f3e6673a7223e8dbe373d37 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 5 Feb 2024 19:46:11 -0500 Subject: [PATCH 23/49] Fix a boatload of deprecation warnings and upgrade a few libraries. --- Project.xml | 3 +- hmm.json | 18 ++++--- .../transition/FlxTransitionableSubState.hx | 4 +- source/funkin/InitState.hx | 20 ++++---- source/funkin/play/GameOverSubState.hx | 4 +- source/funkin/play/GitarooPause.hx | 4 +- source/funkin/play/PauseSubState.hx | 4 +- source/funkin/play/PlayState.hx | 48 ++++++++++--------- source/funkin/play/ResultState.hx | 4 +- .../cutscene/dialogue/ConversationData.hx | 3 +- .../play/cutscene/dialogue/DialogueBoxData.hx | 3 +- .../play/cutscene/dialogue/SpeakerData.hx | 4 +- source/funkin/ui/MusicBeatState.hx | 2 +- source/funkin/ui/MusicBeatSubState.hx | 2 +- source/funkin/ui/debug/DebugMenuSubState.hx | 4 +- .../ui/debug/anim/DebugBoundingState.hx | 2 +- .../ui/debug/charting/ChartEditorState.hx | 2 +- .../charting/commands/CopyItemsCommand.hx | 2 +- .../handlers/ChartEditorAudioHandler.hx | 2 +- source/funkin/ui/haxeui/HaxeUISubState.hx | 2 +- source/funkin/ui/mainmenu/MainMenuState.hx | 9 ++-- source/funkin/ui/options/OptionsState.hx | 2 +- source/funkin/ui/story/StoryMenuState.hx | 4 +- source/funkin/ui/title/AttractState.hx | 2 +- source/funkin/ui/title/OutdatedSubState.hx | 2 +- source/funkin/ui/title/TitleState.hx | 9 ++-- source/funkin/ui/transition/LoadingState.hx | 18 +++---- .../funkin/ui/transition/StickerSubState.hx | 40 ++++++---------- .../util/plugins/EvacuateDebugPlugin.hx | 2 +- source/funkin/util/tools/Int64Tools.hx | 4 +- 30 files changed, 115 insertions(+), 114 deletions(-) diff --git a/Project.xml b/Project.xml index f5d506688..d09ee2dbb 100644 --- a/Project.xml +++ b/Project.xml @@ -108,7 +108,6 @@ - @@ -124,8 +123,8 @@ - diff --git a/hmm.json b/hmm.json index 4b2885a87..ebd760ac1 100644 --- a/hmm.json +++ b/hmm.json @@ -88,8 +88,10 @@ }, { "name": "hxcpp-debug-server", - "type": "haxelib", - "version": "1.2.4" + "type": "git", + "dir": "hxcpp-debug-server", + "ref": "147294123f983e35f50a966741474438069a7a8f", + "url": "https://github.com/FunkinCrew/hxcpp-debugger" }, { "name": "hxp", @@ -152,15 +154,17 @@ "ref": "cb11a95d0159271eb3587428cf4b9602e46dc469", "url": "https://github.com/larsiusprime/polymod" }, + { + "name": "thx.core", + "type": "git", + "dir": null, + "ref": "22605ff44f01971d599641790d6bae4869f7d9f4", + "url": "https://github.com/FunkinCrew/thx.core" + }, { "name": "thx.semver", "type": "haxelib", "version": "0.2.2" - }, - { - "name": "tink_json", - "type": "haxelib", - "version": "0.11.0" } ] } diff --git a/source/flixel/addons/transition/FlxTransitionableSubState.hx b/source/flixel/addons/transition/FlxTransitionableSubState.hx index 7bb536bb2..ab416adbc 100644 --- a/source/flixel/addons/transition/FlxTransitionableSubState.hx +++ b/source/flixel/addons/transition/FlxTransitionableSubState.hx @@ -16,7 +16,7 @@ import flixel.addons.transition.FlxTransitionableState; * var in:TransitionData = new TransitionData(...); // add your data where "..." is * var out:TransitionData = new TransitionData(...); * - * FlxG.switchState(new FooState(in,out)); + * FlxG.switchState(() -> new FooState(in,out)); * ``` * * Method 2: @@ -25,7 +25,7 @@ import flixel.addons.transition.FlxTransitionableState; * FlxTransitionableSubState.defaultTransIn = new TransitionData(...); * FlxTransitionableSubState.defaultTransOut = new TransitionData(...); * - * FlxG.switchState(new FooState()); + * FlxG.switchState(() -> new FooState()); * ``` */ class FlxTransitionableSubState extends FlxSubState diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index c9198c3d4..2e6974cf1 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -240,17 +240,17 @@ class InitState extends FlxState #elseif LEVEL // -DLEVEL=week1 -DDIFFICULTY=hard startLevel(defineLevel(), defineDifficulty()); #elseif FREEPLAY // -DFREEPLAY - FlxG.switchState(new FreeplayState()); + FlxG.switchState(() -> new funkin.ui.freeplay.FreeplayState()); #elseif ANIMATE // -DANIMATE - FlxG.switchState(new funkin.ui.debug.anim.FlxAnimateTest()); + FlxG.switchState(() -> new funkin.ui.debug.anim.FlxAnimateTest()); #elseif CHARTING // -DCHARTING - FlxG.switchState(new funkin.ui.debug.charting.ChartEditorState()); + FlxG.switchState(() -> new funkin.ui.debug.charting.ChartEditorState()); #elseif STAGEBUILD // -DSTAGEBUILD - FlxG.switchState(new funkin.ui.debug.stage.StageBuilderState()); + FlxG.switchState(() -> new funkin.ui.debug.stage.StageBuilderState()); #elseif ANIMDEBUG // -DANIMDEBUG - FlxG.switchState(new funkin.ui.debug.anim.DebugBoundingState()); + FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState()); #elseif LATENCY // -DLATENCY - FlxG.switchState(new funkin.LatencyState()); + FlxG.switchState(() -> new funkin.LatencyState()); #else startGameNormally(); #end @@ -266,7 +266,7 @@ class InitState extends FlxState if (params.chart.shouldLoadChart) { - FlxG.switchState(new ChartEditorState( + FlxG.switchState(() -> new ChartEditorState( { fnfcTargetPath: params.chart.chartPath, })); @@ -274,7 +274,7 @@ class InitState extends FlxState else { FlxG.sound.cache(Paths.music('freakyMenu/freakyMenu')); - FlxG.switchState(new TitleState()); + FlxG.switchState(() -> new TitleState()); } } @@ -297,7 +297,7 @@ class InitState extends FlxState // TODO: Do this in the loading state. songData.cacheCharts(true); - LoadingState.loadAndSwitchState(new funkin.play.PlayState( + LoadingState.loadAndSwitchState(() -> new funkin.play.PlayState( { targetSong: songData, targetDifficulty: difficultyId, @@ -327,7 +327,7 @@ class InitState extends FlxState var targetSong:funkin.play.song.Song = SongRegistry.instance.fetchEntry(targetSongId); - LoadingState.loadAndSwitchState(new funkin.play.PlayState( + LoadingState.loadAndSwitchState(() -> new funkin.play.PlayState( { targetSong: targetSong, targetDifficulty: difficultyId, diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 36f72237e..6027ea1ca 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -207,11 +207,11 @@ class GameOverSubState extends MusicBeatSubState } else if (PlayStatePlaylist.isStoryMode) { - FlxG.switchState(new StoryMenuState()); + FlxG.switchState(() -> new StoryMenuState()); } else { - FlxG.switchState(new FreeplayState()); + FlxG.switchState(() -> new FreeplayState()); } } diff --git a/source/funkin/play/GitarooPause.hx b/source/funkin/play/GitarooPause.hx index dbfbf5961..edeb4229c 100644 --- a/source/funkin/play/GitarooPause.hx +++ b/source/funkin/play/GitarooPause.hx @@ -66,11 +66,11 @@ class GitarooPause extends MusicBeatState { FlxTransitionableState.skipNextTransIn = false; FlxTransitionableState.skipNextTransOut = false; - FlxG.switchState(new PlayState(previousParams)); + FlxG.switchState(() -> new PlayState(previousParams)); } else { - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } } diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index c9039ce40..f293919f3 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -234,11 +234,11 @@ class PauseSubState extends MusicBeatSubState if (PlayStatePlaylist.isStoryMode) { PlayStatePlaylist.reset(); - openSubState(new funkin.ui.transition.StickerSubState(null, STORY)); + openSubState(new funkin.ui.transition.StickerSubState(null, () -> new funkin.ui.story.StoryMenuState())); } else { - openSubState(new funkin.ui.transition.StickerSubState(null, FREEPLAY)); + openSubState(new funkin.ui.transition.StickerSubState(null, () -> new funkin.ui.freeplay.FreeplayState())); } case 'Exit to Chart Editor': diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 763c05640..c72ac1ed9 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -690,9 +690,9 @@ class PlayState extends MusicBeatSubState { message = 'The was a critical error selecting a difficulty for this song. Click OK to return to the main menu.'; } - else if (currentSong.getDifficulty(currentDifficulty) == null) + else if (currentChart == null) { - message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty. Click OK to return to the main menu.'; + message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty playing as "$currentPlayerId". Click OK to return to the main menu.'; } // Display a popup. This blocks the application until the user clicks OK. @@ -705,7 +705,7 @@ class PlayState extends MusicBeatSubState } else { - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } return false; } @@ -834,7 +834,7 @@ class PlayState extends MusicBeatSubState // It's a reference to Gitaroo Man, which doesn't let you pause the game. if (!isSubState && event.gitaroo) { - FlxG.switchState(new GitarooPause( + FlxG.switchState(() -> new GitarooPause( { targetSong: currentSong, targetDifficulty: currentDifficulty, @@ -2235,7 +2235,7 @@ class PlayState extends MusicBeatSubState #end // Eject button - if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); if (FlxG.keys.justPressed.F5) debug_refreshModules(); @@ -2253,7 +2253,7 @@ class PlayState extends MusicBeatSubState { disableKeys = true; persistentUpdate = false; - FlxG.switchState(new ChartEditorState( + FlxG.switchState(() -> new ChartEditorState( { targetSongId: currentSong.id, })); @@ -2595,14 +2595,16 @@ class PlayState extends MusicBeatSubState // TODO: Do this in the loading state. targetSong.cacheCharts(true); - var nextPlayState:PlayState = new PlayState( - { - targetSong: targetSong, - targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetCharacter: currentPlayerId, - }); - nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); - LoadingState.loadAndSwitchState(nextPlayState); + LoadingState.loadAndSwitchState(() -> { + var nextPlayState:PlayState = new PlayState( + { + targetSong: targetSong, + targetDifficulty: PlayStatePlaylist.campaignDifficulty, + targetCharacter: currentPlayerId, + }); + nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); + return nextPlayState; + }); }); } else @@ -2611,14 +2613,16 @@ class PlayState extends MusicBeatSubState // Load and cache the song's charts. // TODO: Do this in the loading state. targetSong.cacheCharts(true); - var nextPlayState:PlayState = new PlayState( - { - targetSong: targetSong, - targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetCharacter: currentPlayerId, - }); - nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); - LoadingState.loadAndSwitchState(nextPlayState); + LoadingState.loadAndSwitchState(() -> { + var nextPlayState:PlayState = new PlayState( + { + targetSong: targetSong, + targetDifficulty: PlayStatePlaylist.campaignDifficulty, + targetCharacter: currentPlayerId, + }); + nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); + return nextPlayState; + }); } } } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 507fa1236..caa576bcf 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -352,11 +352,11 @@ class ResultState extends MusicBeatSubState { if (params.storyMode) { - FlxG.switchState(new StoryMenuState()); + FlxG.switchState(() -> new StoryMenuState()); } else { - FlxG.switchState(new FreeplayState()); + FlxG.switchState(() -> new FreeplayState()); } } diff --git a/source/funkin/play/cutscene/dialogue/ConversationData.hx b/source/funkin/play/cutscene/dialogue/ConversationData.hx index 8c4aa9684..d36b75452 100644 --- a/source/funkin/play/cutscene/dialogue/ConversationData.hx +++ b/source/funkin/play/cutscene/dialogue/ConversationData.hx @@ -27,6 +27,7 @@ class ConversationData public static function fromString(i:String):ConversationData { + // TODO: Replace this with json2object. if (i == null || i == '') return null; var data: { @@ -35,7 +36,7 @@ class ConversationData ?outro:Dynamic, // TODO: tink.Json doesn't like when these are typed ?music:Dynamic, // TODO: tink.Json doesn't like when these are typed dialogue:Array // TODO: tink.Json doesn't like when these are typed - } = tink.Json.parse(i); + } = SerializerUtil.fromJSON(i); return fromJson(data); } diff --git a/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx b/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx index 801a01dd7..322a637e7 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBoxData.hx @@ -38,6 +38,7 @@ class DialogueBoxData public static function fromString(i:String):DialogueBoxData { + // TODO: Replace this with json2object. if (i == null || i == '') return null; var data: { @@ -51,7 +52,7 @@ class DialogueBoxData text:Dynamic, scale:Float, animations:Array - } = tink.Json.parse(i); + } = SerializerUtil.fromJSON(i); return fromJson(data); } diff --git a/source/funkin/play/cutscene/dialogue/SpeakerData.hx b/source/funkin/play/cutscene/dialogue/SpeakerData.hx index 88883ead8..7fe6a3b72 100644 --- a/source/funkin/play/cutscene/dialogue/SpeakerData.hx +++ b/source/funkin/play/cutscene/dialogue/SpeakerData.hx @@ -1,6 +1,7 @@ package funkin.play.cutscene.dialogue; import funkin.data.animation.AnimationData; +import funkin.util.SerializerUtil; /** * Data about a conversation. @@ -37,6 +38,7 @@ class SpeakerData public static function fromString(i:String):SpeakerData { + // TODO: Replace this with json2object. if (i == null || i == '') return null; var data: { @@ -48,7 +50,7 @@ class SpeakerData ?flipX:Bool, ?isPixel:Bool, ?scale:Float - } = tink.Json.parse(i); + } = SerializerUtil.fromJSON(i); return fromJson(data); } diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 33333565f..884fc5061 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -74,7 +74,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler function handleFunctionControls():Void { // Emergency exit button. - if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); // This can now be used in EVERY STATE YAY! if (FlxG.keys.justPressed.F5) debug_refreshModules(); diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index 0fa55c234..17c6e7fad 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -59,7 +59,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1); // Emergency exit button. - if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); // This can now be used in EVERY STATE YAY! if (FlxG.keys.justPressed.F5) debug_refreshModules(); diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 404bf6f67..861e99f6b 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -87,12 +87,12 @@ class DebugMenuSubState extends MusicBeatSubState { FlxTransitionableState.skipNextTransIn = true; - FlxG.switchState(new ChartEditorState()); + FlxG.switchState(() -> new ChartEditorState()); } function openAnimationEditor() { - FlxG.switchState(new funkin.ui.debug.anim.DebugBoundingState()); + FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState()); trace('Animation Editor'); } diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 4e06913b4..5561a9dcd 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -364,7 +364,7 @@ class DebugBoundingState extends FlxState if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible; - if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); MouseUtil.mouseCamDrag(); MouseUtil.mouseWheelZoom(); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 467e36f74..686edb135 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5037,7 +5037,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState stopWelcomeMusic(); // TODO: PR Flixel to make onComplete nullable. if (audioInstTrack != null) audioInstTrack.onComplete = null; - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); resetWindowTitle(); diff --git a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx index 4361f867f..311451dcf 100644 --- a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx @@ -57,7 +57,7 @@ class CopyItemsCommand implements ChartEditorCommand state.txtCopyNotif.y = FlxG.mouse.y - 16; FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5, { - type: FlxTween.ONESHOT, + type: FlxTweenType.ONESHOT, ease: FlxEase.quadOut, onComplete: function(_) { state.txtCopyNotif.visible = false; diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx index e1fcd1cb0..d5e888270 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx @@ -1,7 +1,7 @@ package funkin.ui.debug.charting.handlers; import flixel.system.FlxAssets.FlxSoundAsset; -import flixel.system.FlxSound; +import flixel.sound.FlxSound; import funkin.audio.VoicesGroup; import funkin.audio.visualize.PolygonVisGroup; import funkin.audio.FunkinSound; diff --git a/source/funkin/ui/haxeui/HaxeUISubState.hx b/source/funkin/ui/haxeui/HaxeUISubState.hx index 82c15be4c..ac53f4b51 100644 --- a/source/funkin/ui/haxeui/HaxeUISubState.hx +++ b/source/funkin/ui/haxeui/HaxeUISubState.hx @@ -45,7 +45,7 @@ class HaxeUISubState extends MusicBeatSubState super.update(elapsed); // Force quit. - if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); // Refresh the component. if (FlxG.keys.justPressed.F5) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index f41eac07d..3da041ada 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -8,6 +8,7 @@ import flixel.FlxState; import flixel.addons.transition.FlxTransitionableState; import flixel.effects.FlxFlicker; import flixel.graphics.frames.FlxAtlasFrames; +import flixel.util.typeLimit.NextState; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.input.touch.FlxTouch; import flixel.text.FlxText; @@ -94,7 +95,7 @@ class MainMenuState extends MusicBeatState }); menuItems.enabled = true; // can move on intro - createMenuItem('storymode', 'mainmenu/storymode', function() startExitState(new StoryMenuState())); + createMenuItem('storymode', 'mainmenu/storymode', function() startExitState(() -> new StoryMenuState())); createMenuItem('freeplay', 'mainmenu/freeplay', function() { persistentDraw = true; persistentUpdate = false; @@ -110,7 +111,7 @@ class MainMenuState extends MusicBeatState #end createMenuItem('options', 'mainmenu/options', function() { - startExitState(new funkin.ui.options.OptionsState()); + startExitState(() -> new funkin.ui.options.OptionsState()); }); // Reset position of menu items. @@ -255,7 +256,7 @@ class MainMenuState extends MusicBeatState openSubState(prompt); } - function startExitState(state:FlxState) + function startExitState(state:NextState) { menuItems.enabled = false; // disable for exit var duration = 0.4; @@ -313,7 +314,7 @@ class MainMenuState extends MusicBeatState if (controls.BACK && menuItems.enabled && !menuItems.busy) { FlxG.sound.play(Paths.sound('cancelMenu')); - FlxG.switchState(new TitleState()); + FlxG.switchState(() -> new TitleState()); } } } diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 53d972af1..7b233f03d 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -103,7 +103,7 @@ class OptionsState extends MusicBeatState { currentPage.enabled = false; // TODO: Animate this transition? - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 112817f42..2f0111841 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -397,7 +397,7 @@ class StoryMenuState extends MusicBeatState { FlxG.sound.play(Paths.sound('cancelMenu')); exitingMenu = true; - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } } @@ -565,7 +565,7 @@ class StoryMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = false; FlxTransitionableState.skipNextTransOut = false; - LoadingState.loadAndSwitchState(new PlayState( + LoadingState.loadAndSwitchState(() -> new PlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, diff --git a/source/funkin/ui/title/AttractState.hx b/source/funkin/ui/title/AttractState.hx index 38cff7cc8..ade24bffa 100644 --- a/source/funkin/ui/title/AttractState.hx +++ b/source/funkin/ui/title/AttractState.hx @@ -105,6 +105,6 @@ class AttractState extends MusicBeatState vid.destroy(); vid = null; - FlxG.switchState(new TitleState()); + FlxG.switchState(() -> new TitleState()); } } diff --git a/source/funkin/ui/title/OutdatedSubState.hx b/source/funkin/ui/title/OutdatedSubState.hx index 012823541..bc938f24a 100644 --- a/source/funkin/ui/title/OutdatedSubState.hx +++ b/source/funkin/ui/title/OutdatedSubState.hx @@ -39,7 +39,7 @@ class OutdatedSubState extends MusicBeatState if (controls.BACK) { leftState = true; - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } super.update(elapsed); } diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index a5dcd6def..5424e2255 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -9,6 +9,7 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxDirectionFlags; import flixel.util.FlxTimer; +import flixel.util.typeLimit.NextState; import funkin.audio.visualize.SpectogramSprite; import funkin.graphics.shaders.ColorSwap; import funkin.graphics.shaders.LeftMaskShader; @@ -213,7 +214,7 @@ class TitleState extends MusicBeatState */ function moveToAttract():Void { - FlxG.switchState(new AttractState()); + FlxG.switchState(() -> new AttractState()); } function playMenuMusic():Void @@ -294,7 +295,7 @@ class TitleState extends MusicBeatState { if (touch.justPressed) { - FlxG.switchState(new FreeplayState()); + FlxG.switchState(() -> new FreeplayState()); pressedEnter = true; } } @@ -313,7 +314,7 @@ class TitleState extends MusicBeatState // If you spam Enter, we should skip the transition. if (pressedEnter && transitioning && skippedIntro) { - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } if (pressedEnter && !transitioning && skippedIntro) @@ -328,7 +329,7 @@ class TitleState extends MusicBeatState FlxG.sound.play(Paths.sound('confirmMenu'), 0.7); transitioning = true; - var targetState:FlxState = new MainMenuState(); + var targetState:NextState = () -> new MainMenuState(); new FlxTimer().start(2, function(tmr:FlxTimer) { // These assets are very unlikely to be used for the rest of gameplay, so it unloads them from cache/memory diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index da9aeb28b..63dcb8f68 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -1,7 +1,6 @@ package funkin.ui.transition; import flixel.FlxSprite; -import flixel.FlxState; import flixel.math.FlxMath; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; @@ -21,12 +20,13 @@ import lime.utils.AssetManifest; import lime.utils.Assets as LimeAssets; import openfl.filters.ShaderFilter; import openfl.utils.Assets; +import flixel.util.typeLimit.NextState; class LoadingState extends MusicBeatState { inline static var MIN_TIME = 1.0; - var target:FlxState; + var target:NextState; var stopMusic = false; var callbacks:MultiCallback; var danceLeft = false; @@ -34,7 +34,7 @@ class LoadingState extends MusicBeatState var loadBar:FlxSprite; var funkay:FlxSprite; - function new(target:FlxState, stopMusic:Bool) + function new(target:NextState, stopMusic:Bool) { super(); this.target = target; @@ -172,12 +172,12 @@ class LoadingState extends MusicBeatState return Paths.inst(PlayState.instance.currentSong.id); } - inline static public function loadAndSwitchState(nextState:FlxState, shouldStopMusic = false):Void + inline static public function loadAndSwitchState(nextState:NextState, shouldStopMusic = false):Void { FlxG.switchState(getNextState(nextState, shouldStopMusic)); } - static function getNextState(nextState:FlxState, shouldStopMusic = false):FlxState + static function getNextState(nextState:NextState, shouldStopMusic = false):NextState { Paths.setCurrentLevel(PlayStatePlaylist.campaignId); @@ -186,7 +186,7 @@ class LoadingState extends MusicBeatState // && (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath())) // && isLibraryLoaded('shared'); // - if (true) return new LoadingState(nextState, shouldStopMusic); + if (true) return () -> new LoadingState(nextState, shouldStopMusic); #end if (shouldStopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); @@ -332,7 +332,7 @@ class MultiCallback public function getUnfired():ArrayVoid> return unfired.array(); - public static function coolSwitchState(state:FlxState, transitionTex:String = "shaderTransitionStuff/coolDots", time:Float = 2) + public static function coolSwitchState(state:NextState, transitionTex:String = "shaderTransitionStuff/coolDots", time:Float = 2) { var screenShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("shaderTransitionStuff/coolDots")); var screenWipeShit:ScreenWipeShader = new ScreenWipeShader(); @@ -343,9 +343,9 @@ class MultiCallback ease: FlxEase.quadInOut, onComplete: function(twn) { screenShit.destroy(); - FlxG.switchState(new MainMenuState()); + FlxG.switchState(state); } }); - FlxG.camera.setFilters([new ShaderFilter(screenWipeShit)]); + FlxG.camera.filters = [new ShaderFilter(screenWipeShit)]; } } diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 1c012e00c..43ced1d7c 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -19,6 +19,7 @@ import funkin.ui.freeplay.FreeplayState; import openfl.geom.Matrix; import openfl.display.Sprite; import openfl.display.Bitmap; +import flixel.FlxState; using Lambda; using StringTools; @@ -30,7 +31,12 @@ class StickerSubState extends MusicBeatSubState // yes... a damn OpenFL sprite!!! public var dipshit:Sprite; - var nextState:NEXTSTATE = FREEPLAY; + /** + * The state to switch to after the stickers are done. + * This is a FUNCTION so we can pass it directly to `FlxG.switchState()`, + * and we can add constructor parameters in the caller. + */ + var targetState:Void->FlxState; // what "folders" to potentially load from (as of writing only "keys" exist) var soundSelections:Array = []; @@ -38,10 +44,15 @@ class StickerSubState extends MusicBeatSubState var soundSelection:String = ""; var sounds:Array = []; - public function new(?oldStickers:Array, ?nextState:NEXTSTATE = FREEPLAY):Void + public function new(?oldStickers:Array, ?targetState:Void->FlxState):Void { super(); + if (targetState != null) + { + this.targetState = () -> new MainMenuState(); + } + // todo still // make sure that ONLY plays mp3/ogg files // if there's no mp3/ogg file, then it regenerates/reloads the random folder @@ -84,10 +95,6 @@ class StickerSubState extends MusicBeatSubState trace(sounds); - // trace(assetsInList); - - this.nextState = nextState; - grpStickers = new FlxTypedGroup(); add(grpStickers); @@ -241,20 +248,8 @@ class StickerSubState extends MusicBeatSubState dipshit.addChild(bitmap); FlxG.addChildBelowMouse(dipshit); - switch (nextState) - { - case FREEPLAY: - FlxG.switchState(new FreeplayState(this)); - case STORY: - FlxG.switchState(new StoryMenuState(this)); - case MAIN_MENU: - FlxG.switchState(new MainMenuState()); - default: - FlxG.switchState(new MainMenuState()); - } + FlxG.switchState(targetState); } - - // sticky.angle *= FlxG.random.float(0, 0.05); }); }); } @@ -368,10 +363,3 @@ typedef StickerShit = stickers:Map>, stickerPacks:Map> } - -enum abstract NEXTSTATE(String) -{ - var MAIN_MENU = 'mainmenu'; - var FREEPLAY = 'freeplay'; - var STORY = 'story'; -} diff --git a/source/funkin/util/plugins/EvacuateDebugPlugin.hx b/source/funkin/util/plugins/EvacuateDebugPlugin.hx index 1803c25ba..eb292b852 100644 --- a/source/funkin/util/plugins/EvacuateDebugPlugin.hx +++ b/source/funkin/util/plugins/EvacuateDebugPlugin.hx @@ -24,7 +24,7 @@ class EvacuateDebugPlugin extends FlxBasic if (FlxG.keys.justPressed.F4) { - FlxG.switchState(new funkin.ui.mainmenu.MainMenuState()); + FlxG.switchState(() -> new funkin.ui.mainmenu.MainMenuState()); } } diff --git a/source/funkin/util/tools/Int64Tools.hx b/source/funkin/util/tools/Int64Tools.hx index d53fa315d..b6ac84ade 100644 --- a/source/funkin/util/tools/Int64Tools.hx +++ b/source/funkin/util/tools/Int64Tools.hx @@ -18,9 +18,9 @@ class Int64Tools public static function toFloat(i:Int64):Float { - var f:Float = Int64.getLow(i); + var f:Float = i.low; if (f < 0) f += MAX_32_PRECISION; - return (Int64.getHigh(i) * MAX_32_PRECISION + f); + return (i.high * MAX_32_PRECISION + f); } public static function isToIntSafe(i:Int64):Bool From 82b63c02e10c22005ba1256620e7f71bc3f9a142 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 5 Feb 2024 21:35:58 -0500 Subject: [PATCH 24/49] Reworks to make Pico mode actually work. --- assets | 2 +- source/funkin/data/song/SongData.hx | 44 +++++----- source/funkin/play/PauseSubState.hx | 4 +- source/funkin/play/PlayState.hx | 24 +++--- source/funkin/play/components/HealthIcon.hx | 3 +- source/funkin/play/song/Song.hx | 81 +++++++++++++++---- .../ui/debug/charting/ChartEditorState.hx | 5 +- .../ChartEditorImportExportHandler.hx | 38 ++++----- source/funkin/ui/freeplay/FreeplayState.hx | 60 ++++++++------ source/funkin/ui/story/Level.hx | 3 +- .../funkin/ui/transition/StickerSubState.hx | 11 +-- 11 files changed, 167 insertions(+), 108 deletions(-) diff --git a/assets b/assets index 5479e17b1..49970e24e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5479e17b1085f72e05f1c1f9e0a668ef832d3341 +Subproject commit 49970e24e919de25f4dcef5bd47116f1877ee360 diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 52b9c19d6..0a430f196 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -915,6 +915,28 @@ class SongNoteDataRaw implements ICloneable return SongNoteData.buildDirectionName(this.data, strumlineSize); } + /** + * The strumline index of the note, if applicable. + * Strips the direction from the data. + * + * 0 = player, 1 = opponent, etc. + */ + public function getStrumlineIndex(strumlineSize:Int = 4):Int + { + return Math.floor(this.data / strumlineSize); + } + + /** + * Returns true if the note is one that Boyfriend should try to hit (i.e. it's on his side). + * TODO: The name of this function is a little misleading; what about mines? + * @param strumlineSize Defaults to 4. + * @return True if it's Boyfriend's note. + */ + public function getMustHitNote(strumlineSize:Int = 4):Bool + { + return getStrumlineIndex(strumlineSize) == 0; + } + @:jignored var _stepTime:Null = null; @@ -1003,28 +1025,6 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw } } - /** - * The strumline index of the note, if applicable. - * Strips the direction from the data. - * - * 0 = player, 1 = opponent, etc. - */ - public inline function getStrumlineIndex(strumlineSize:Int = 4):Int - { - return Math.floor(this.data / strumlineSize); - } - - /** - * Returns true if the note is one that Boyfriend should try to hit (i.e. it's on his side). - * TODO: The name of this function is a little misleading; what about mines? - * @param strumlineSize Defaults to 4. - * @return True if it's Boyfriend's note. - */ - public inline function getMustHitNote(strumlineSize:Int = 4):Bool - { - return getStrumlineIndex(strumlineSize) == 0; - } - @:jignored public var isHoldNote(get, never):Bool; diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index f293919f3..d38e3ac87 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -234,11 +234,11 @@ class PauseSubState extends MusicBeatSubState if (PlayStatePlaylist.isStoryMode) { PlayStatePlaylist.reset(); - openSubState(new funkin.ui.transition.StickerSubState(null, () -> new funkin.ui.story.StoryMenuState())); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState())); } else { - openSubState(new funkin.ui.transition.StickerSubState(null, () -> new funkin.ui.freeplay.FreeplayState())); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.freeplay.FreeplayState(null, sticker))); } case 'Exit to Chart Editor': diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index c72ac1ed9..ad2ea5a45 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -83,10 +83,10 @@ typedef PlayStateParams = */ ?targetDifficulty:String, /** - * The character to play as. - * @default `bf`, or the first character in the song's character list. + * The variation to play on. + * @default `Constants.DEFAULT_VARIATION` . */ - ?targetCharacter:String, + ?targetVariation:String, /** * The instrumental to play with. * Significant if the `targetSong` supports alternate instrumentals. @@ -153,9 +153,9 @@ class PlayState extends MusicBeatSubState public var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY; /** - * The player character being used for this level, as a character ID. + * The currently selected variation. */ - public var currentPlayerId:String = 'bf'; + public var currentVariation:String = Constants.DEFAULT_VARIATION; /** * The currently active Stage. This is the object containing all the props. @@ -454,7 +454,7 @@ class PlayState extends MusicBeatSubState function get_currentChart():SongDifficulty { if (currentSong == null || currentDifficulty == null) return null; - return currentSong.getDifficulty(currentDifficulty, currentPlayerId); + return currentSong.getDifficulty(currentDifficulty, currentVariation); } /** @@ -512,7 +512,7 @@ class PlayState extends MusicBeatSubState // Apply parameters. currentSong = params.targetSong; if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty; - if (params.targetCharacter != null) currentPlayerId = params.targetCharacter; + if (params.targetVariation != null) currentVariation = params.targetVariation; isPracticeMode = params.practiceMode ?? false; isMinimalMode = params.minimalMode ?? false; startTimestamp = params.startTimestamp ?? 0.0; @@ -692,7 +692,7 @@ class PlayState extends MusicBeatSubState } else if (currentChart == null) { - message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty playing as "$currentPlayerId". Click OK to return to the main menu.'; + message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty with variation "$currentVariation". Click OK to return to the main menu.'; } // Display a popup. This blocks the application until the user clicks OK. @@ -838,7 +838,7 @@ class PlayState extends MusicBeatSubState { targetSong: currentSong, targetDifficulty: currentDifficulty, - targetCharacter: currentPlayerId, + targetVariation: currentVariation, })); } else @@ -1395,7 +1395,7 @@ class PlayState extends MusicBeatSubState trace('Song difficulty could not be loaded.'); } - var currentCharacterData:SongCharacterData = currentChart.characters; // Switch the character we are playing as by manipulating currentPlayerId. + var currentCharacterData:SongCharacterData = currentChart.characters; // Switch the variation we are playing on by manipulating targetVariation. // // GIRLFRIEND @@ -2600,7 +2600,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetCharacter: currentPlayerId, + targetVariation: currentVariation, }); nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); return nextPlayState; @@ -2618,7 +2618,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetCharacter: currentPlayerId, + targetVariation: currentVariation, }); nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); return nextPlayState; diff --git a/source/funkin/play/components/HealthIcon.hx b/source/funkin/play/components/HealthIcon.hx index 0d90df5a0..420a4fdc4 100644 --- a/source/funkin/play/components/HealthIcon.hx +++ b/source/funkin/play/components/HealthIcon.hx @@ -148,11 +148,12 @@ class HealthIcon extends FlxSprite { if (characterId == 'bf-old') { - characterId = PlayState.instance.currentPlayerId; + PlayState.instance.currentStage.getBoyfriend().initHealthIcon(false); } else { characterId = 'bf-old'; + loadCharacter(characterId); } } diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 52a1ba6f8..5100e1888 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -184,9 +184,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = difficulties.get(diffId); + var variationSuffix = (variation != Constants.DEFAULT_VARIATION) ? '-$variation' : ''; + var difficulty:Null = difficulties.get('$diffId$variationSuffix'); if (difficulty == null) { trace('Fabricated new difficulty for $diffId.'); difficulty = new SongDifficulty(this, diffId, variation); var metadata = _metadata.get(variation); - var variationSuffix = (variation != null && variation != '' && variation != Constants.DEFAULT_VARIATION) ? '-$variation' : ''; difficulties.set('$diffId$variationSuffix', difficulty); if (metadata != null) @@ -258,26 +256,52 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + public function getDifficulty(?diffId:String, ?variation:String, ?variations:Array):Null { - if (diffId == null) diffId = listDifficulties()[0]; - + if (diffId == null) diffId = listDifficulties(variation)[0]; if (variation == null) variation = Constants.DEFAULT_VARIATION; - var variationSuffix = (variation != null && variation != '' && variation != Constants.DEFAULT_VARIATION) ? '-$variation' : ''; + if (variations == null) variations = [variation]; - return difficulties.get('$diffId$variationSuffix'); + for (currentVariation in variations) + { + var variationSuffix = (currentVariation != Constants.DEFAULT_VARIATION) ? '-$currentVariation' : ''; + + if (difficulties.exists('$diffId$variationSuffix')) + { + return difficulties.get('$diffId$variationSuffix'); + } + } + + return null; + } + + public function getFirstValidVariation(?diffId:String, ?possibleVariations:Array):Null + { + if (variations == null) possibleVariations = variations; + if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0]; + + for (variation in variations) + { + if (difficulties.exists('$diffId-$variation')) return variation; + } + + return null; } /** * List all the difficulties in this song. - * @param variationId Optionally filter by variation. + * @param variationId Optionally filter by a single variation. + * @param variationIds Optionally filter by multiple variations. * @return The list of difficulties. */ - public function listDifficulties(?variationId:String):Array + public function listDifficulties(?variationId:String, ?variationIds:Array):Array { - if (variationId == '') variationId = null; + if (variationIds == null) variationIds = []; + if (variationId != null) variationIds.push(variationId); // The difficulties array contains entries like 'normal', 'nightmare-erect', and 'normal-pico', // so we have to map it to the actual difficulty names. @@ -286,7 +310,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = difficulties.keys().array().map(function(diffId:String):Null { var difficulty:Null = difficulties.get(diffId); if (difficulty == null) return null; - if (variationId != null && difficulty.variation != variationId) return null; + if (variationIds.length > 0 && !variationIds.contains(difficulty.variation)) return null; return difficulty.difficulty; }).nonNull().unique(); @@ -504,7 +528,8 @@ class SongDifficulty var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : ''; // Automatically resolve voices by removing suffixes. - // For example, if `Voices-bf-car.ogg` does not exist, check for `Voices-bf.ogg`. + // For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`. + // Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`. var playerId:String = characters.player; var voicePlayer:String = Paths.voices(this.song.id, '-$playerId$suffix'); @@ -516,6 +541,19 @@ class SongDifficulty // Try again. voicePlayer = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); } + if (voicePlayer == null) + { + // Try again without $suffix. + playerId = characters.player; + voicePlayer = Paths.voices(this.song.id, '-${playerId}'); + while (voicePlayer != null && !Assets.exists(voicePlayer)) + { + // Remove the last suffix. + playerId = playerId.split('-').slice(0, -1).join('-'); + // Try again. + voicePlayer = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); + } + } var opponentId:String = characters.opponent; var voiceOpponent:String = Paths.voices(this.song.id, '-${opponentId}$suffix'); @@ -526,6 +564,19 @@ class SongDifficulty // Try again. voiceOpponent = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); } + if (voiceOpponent == null) + { + // Try again without $suffix. + opponentId = characters.opponent; + voiceOpponent = Paths.voices(this.song.id, '-${opponentId}'); + while (voiceOpponent != null && !Assets.exists(voiceOpponent)) + { + // Remove the last suffix. + opponentId = opponentId.split('-').slice(0, -1).join('-'); + // Try again. + voiceOpponent = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); + } + } var result:Array = []; if (voicePlayer != null) result.push(voicePlayer); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 686edb135..af7eed129 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5037,7 +5037,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState stopWelcomeMusic(); // TODO: PR Flixel to make onComplete nullable. if (audioInstTrack != null) audioInstTrack.onComplete = null; - FlxG.switchState(() -> new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); resetWindowTitle(); @@ -5303,8 +5303,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { targetSong: targetSong, targetDifficulty: selectedDifficulty, - // TODO: Add this. - // targetCharacter: targetCharacter, + targetVariation: selectedVariation, practiceMode: playtestPracticeMode, minimalMode: minimal, startTimestamp: startTimestamp, diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx index 9c86269e8..2d8e6a71e 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx @@ -70,28 +70,28 @@ class ChartEditorImportExportHandler { state.loadInstFromAsset(Paths.inst(songId, '-$variation'), variation); } - } - for (difficultyId in song.listDifficulties()) - { - var diff:Null = song.getDifficulty(difficultyId); - if (diff == null) continue; + for (difficultyId in song.listDifficulties(variation)) + { + var diff:Null = song.getDifficulty(difficultyId, variation); + if (diff == null) continue; - var instId:String = diff.variation == Constants.DEFAULT_VARIATION ? '' : diff.variation; - var voiceList:Array = diff.buildVoiceList(); // SongDifficulty accounts for variation already. + var instId:String = diff.variation == Constants.DEFAULT_VARIATION ? '' : diff.variation; + var voiceList:Array = diff.buildVoiceList(); // SongDifficulty accounts for variation already. - if (voiceList.length == 2) - { - state.loadVocalsFromAsset(voiceList[0], diff.characters.player, instId); - state.loadVocalsFromAsset(voiceList[1], diff.characters.opponent, instId); - } - else if (voiceList.length == 1) - { - state.loadVocalsFromAsset(voiceList[0], diff.characters.player, instId); - } - else - { - trace('[WARN] Strange quantity of voice paths for difficulty ${difficultyId}: ${voiceList.length}'); + if (voiceList.length == 2) + { + state.loadVocalsFromAsset(voiceList[0], diff.characters.player, instId); + state.loadVocalsFromAsset(voiceList[1], diff.characters.opponent, instId); + } + else if (voiceList.length == 1) + { + state.loadVocalsFromAsset(voiceList[0], diff.characters.player, instId); + } + else + { + trace('[WARN] Strange quantity of voice paths for difficulty ${difficultyId}: ${voiceList.length}'); + } } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index f981357cf..e2f01fb13 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -59,11 +59,14 @@ import lime.utils.Assets; */ typedef FreeplayStateParams = { - ?character:String; + ?character:String, }; class FreeplayState extends MusicBeatSubState { + // Params, you can't change these without transitioning to a new FreeplayState. + final currentCharacter:String; + var songs:Array> = []; var diffIdsCurrent:Array = []; @@ -72,9 +75,6 @@ class FreeplayState extends MusicBeatSubState var curSelected:Int = 0; var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY; - // Params - var currentCharacter:String; - var fp:FreeplayScore; var txtCompletion:AtlasText; var lerpCompletion:Float = 0; @@ -102,6 +102,8 @@ class FreeplayState extends MusicBeatSubState var ostName:FlxText; var difficultyStars:DifficultyStars; + var displayedVariations:Array; + var dj:DJBoyfriend; var letterSort:LetterSort; @@ -113,7 +115,7 @@ class FreeplayState extends MusicBeatSubState static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; static var rememberedSongId:Null = null; - public function new(?params:FreeplayParams, ?stickers:StickerSubState) + public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { currentCharacter = params?.character ?? Constants.DEFAULT_CHARACTER; @@ -159,6 +161,10 @@ class FreeplayState extends MusicBeatSubState // Add a null entry that represents the RANDOM option songs.push(null); + // TODO: This makes custom variations disappear from Freeplay. Figure out a better solution later. + // Default character (BF) shows default and Erect variations. Pico shows only Pico variations. + displayedVariations = (currentCharacter == "bf") ? [Constants.DEFAULT_VARIATION, "erect"] : [currentCharacter]; + // programmatically adds the songs via LevelRegistry and SongRegistry for (levelId in LevelRegistry.instance.listBaseGameLevelIds()) { @@ -166,9 +172,12 @@ class FreeplayState extends MusicBeatSubState { var song:Song = SongRegistry.instance.fetchEntry(songId); - songs.push(new FreeplaySongData(levelId, songId, song)); + // Only display songs which actually have available charts for the current character. + var availableDifficultiesForSong = song.listDifficulties(displayedVariations); + if (availableDifficultiesForSong.length == 0) continue; - for (difficulty in song.listDifficulties()) + songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); + for (difficulty in availableDifficultiesForSong) { diffIdsTotal.pushUnique(difficulty); } @@ -287,6 +296,8 @@ class FreeplayState extends MusicBeatSubState x: -dj.width * 1.6, speed: 0.5 }); + // TODO: Replace this. + if (currentCharacter == "pico") dj.visible = false; add(dj); var bgDad:FlxSprite = new FlxSprite(pinkBack.width * 0.75, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); @@ -859,8 +870,11 @@ class FreeplayState extends MusicBeatSubState // TODO: DEBUG REMOVE THIS if (FlxG.keys.justPressed.P) { - currentCharacter = (currentCharacter == "bf") ? "pico" : "bf"; - changeSelection(0); + var newParams:FreeplayStateParams = + { + character: currentCharacter == "bf" ? "pico" : "bf", + }; + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.freeplay.FreeplayState(newParams, sticker))); } if (controls.BACK && !typing.hasFocus) @@ -914,7 +928,7 @@ class FreeplayState extends MusicBeatSubState } else { - FlxG.switchState(new MainMenuState()); + FlxG.switchState(() -> new MainMenuState()); } }); } @@ -1080,13 +1094,7 @@ class FreeplayState extends MusicBeatSubState return; } var targetDifficulty:String = currentDifficulty; - - // TODO: Implement Pico into the interface properly. - var targetCharacter:String = 'bf'; - if (FlxG.keys.pressed.P) - { - targetCharacter = 'pico'; - } + var targetVariation:String = targetSong.getFirstValidVariation(targetDifficulty); PlayStatePlaylist.campaignId = cap.songData.levelId; @@ -1100,11 +1108,11 @@ class FreeplayState extends MusicBeatSubState new FlxTimer().start(1, function(tmr:FlxTimer) { Paths.setCurrentLevel(cap.songData.levelId); - LoadingState.loadAndSwitchState(new PlayState( + LoadingState.loadAndSwitchState(() -> new PlayState( { targetSong: targetSong, targetDifficulty: targetDifficulty, - targetCharacter: targetCharacter, + targetVariation: targetVariation, }), true); }); } @@ -1269,31 +1277,33 @@ class FreeplaySongData public var songRating(default, null):Int = 0; public var currentDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY; + public var displayedVariations(default, null):Array = [Constants.DEFAULT_VARIATION]; function set_currentDifficulty(value:String):String { if (currentDifficulty == value) return value; currentDifficulty = value; - updateValues(); + updateValues(displayedVariations); return value; } - public function new(levelId:String, songId:String, song:Song) + public function new(levelId:String, songId:String, song:Song, ?displayedVariations:Array) { this.levelId = levelId; this.songId = songId; this.song = song; + if (displayedVariations != null) this.displayedVariations = displayedVariations; - updateValues(); + updateValues(displayedVariations); } - function updateValues():Void + function updateValues(displayedVariations:Array):Void { - this.songDifficulties = song.listDifficulties(); + this.songDifficulties = song.listDifficulties(displayedVariations); if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; - var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty); + var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, displayedVariations); if (songDifficulty == null) return; this.songName = songDifficulty.songName; this.songCharacter = songDifficulty.characters.opponent; diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index 1b9252fde..f0c39c13f 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -150,7 +150,8 @@ class Level implements IRegistryEntry if (firstSong != null) { - for (difficulty in firstSong.listDifficulties()) + // Don't display alternate characters in Story Mode. + for (difficulty in firstSong.listDifficulties([Constants.DEFAULT_VARIATION, "erect"])) { difficulties.push(difficulty); } diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 43ced1d7c..fa36cfd50 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -36,7 +36,7 @@ class StickerSubState extends MusicBeatSubState * This is a FUNCTION so we can pass it directly to `FlxG.switchState()`, * and we can add constructor parameters in the caller. */ - var targetState:Void->FlxState; + var targetState:StickerSubState->FlxState; // what "folders" to potentially load from (as of writing only "keys" exist) var soundSelections:Array = []; @@ -44,14 +44,11 @@ class StickerSubState extends MusicBeatSubState var soundSelection:String = ""; var sounds:Array = []; - public function new(?oldStickers:Array, ?targetState:Void->FlxState):Void + public function new(?oldStickers:Array, ?targetState:StickerSubState->FlxState):Void { super(); - if (targetState != null) - { - this.targetState = () -> new MainMenuState(); - } + this.targetState = (targetState == null) ? ((sticker) -> new MainMenuState()) : targetState; // todo still // make sure that ONLY plays mp3/ogg files @@ -248,7 +245,7 @@ class StickerSubState extends MusicBeatSubState dipshit.addChild(bitmap); FlxG.addChildBelowMouse(dipshit); - FlxG.switchState(targetState); + FlxG.switchState(() -> targetState(this)); } }); }); From fa2aebdc5289617757d022ddd5e35ad4db5cc05d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 7 Feb 2024 19:37:19 -0500 Subject: [PATCH 25/49] Use the correct assets submodule commit. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 49970e24e..ac2566d53 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 49970e24e919de25f4dcef5bd47116f1877ee360 +Subproject commit ac2566d531ea5ea8eb0123786e315e6094a49a1d From 2dd5f476b58732a3bd9e1ee641a46aa029e91af9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 8 Feb 2024 02:21:12 -0500 Subject: [PATCH 26/49] Fix a bug where alt instrumentals will load but not cache. --- source/funkin/play/song/Song.hx | 21 ++++++++++++++------- source/funkin/ui/story/Level.hx | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 5100e1888..970aebc57 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -319,12 +319,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Bool { - if (variationId == '') variationId = Constants.DEFAULT_VARIATION; - var variationSuffix:String = (variationId == Constants.DEFAULT_VARIATION) ? '' : '-$variationId'; - var difficulty:Null = difficulties.get('$diffId$variationSuffix'); - return variationId == null ? (difficulty != null) : (difficulty != null && difficulty.variation == variationId); + if (variationIds == null) variationIds = []; + if (variationId != null) variationIds.push(variationId); + + for (targetVariation in variationIds) + { + var variationSuffix = (targetVariation != Constants.DEFAULT_VARIATION) ? '-$targetVariation' : ''; + if (difficulties.exists('$diffId$variationSuffix')) return true; + } + return false; } /** @@ -481,12 +486,14 @@ class SongDifficulty { if (instrumental != '' && characters.altInstrumentals.contains(instrumental)) { - FlxG.sound.cache(Paths.inst(this.song.id, instrumental)); + var instId = '-$instrumental'; + FlxG.sound.cache(Paths.inst(this.song.id, instId)); } else { // Fallback to default instrumental. - FlxG.sound.cache(Paths.inst(this.song.id, characters.instrumental)); + var instId = (characters.instrumental ?? '') != '' ? '-${characters.instrumental}' : ''; + FlxG.sound.cache(Paths.inst(this.song.id, instId)); } } else diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index f0c39c13f..ea6940c4a 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -169,7 +169,7 @@ class Level implements IRegistryEntry for (difficulty in difficulties) { - if (!song.hasDifficulty(difficulty)) + if (!song.hasDifficulty(difficulty, [Constants.DEFAULT_VARIATION, "erect"])) { difficulties.remove(difficulty); } From 6f125a97b1a58cbec720eceb1c6c4da99c468b6c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 9 Feb 2024 23:44:35 -0500 Subject: [PATCH 27/49] Fix idle animations for Pico. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 53257161f..9308c50bd 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 53257161fae317ae018fadea4446df7e53915ca9 +Subproject commit 9308c50bd7fd904787e2fbac511bb83e1197347f From d595767e590f5c498d10c5eddf7999723d775ed2 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 10 Feb 2024 13:06:27 -0500 Subject: [PATCH 28/49] small compiling guide --- README.md | 2 +- docs/COMPILING.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 docs/COMPILING.md diff --git a/README.md b/README.md index 8f919d231..39c098af5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This game was made with love to Newgrounds and it's community. Extra love to Tom **PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME** -To learn how to install the necessary dependencies and compile the game from source, please check out our [building the game]() guide. +To learn how to install the necessary dependencies and compile the game from source, please check out our [building the game](/docs/compiling.md) guide. # Contributing diff --git a/docs/COMPILING.md b/docs/COMPILING.md new file mode 100644 index 000000000..f1dd8fe13 --- /dev/null +++ b/docs/COMPILING.md @@ -0,0 +1,16 @@ +# Compiling Friday Night Funkin' + +0. Setup + a. Download Haxe from [Haxe.org](https://haxe.org) +1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: + - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin-secret.git` + - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. +2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) +3. Install all haxelibs of the current branch by running `hmm install` +4. Platform setup + - Windows: [`lime setup windows` Documentation](https://lime.openfl.org/docs/advanced-setup/windows/) + - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) + - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) + - HTML5: Compiles without any extra setup +5. If you are targeting for native, you likely need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` +6. `lime test PLATFORM` ! From 75faf2289010a444808a427ff201c8c0d94f1ec5 Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 11 Feb 2024 11:01:33 -0500 Subject: [PATCH 29/49] Made the Windows documentation more clear. --- docs/COMPILING.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index f1dd8fe13..7f9c0cdb8 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -1,14 +1,17 @@ # Compiling Friday Night Funkin' 0. Setup - a. Download Haxe from [Haxe.org](https://haxe.org) + - Download Haxe from [Haxe.org](https://haxe.org) 1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin-secret.git` - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. 2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) 3. Install all haxelibs of the current branch by running `hmm install` 4. Platform setup - - Windows: [`lime setup windows` Documentation](https://lime.openfl.org/docs/advanced-setup/windows/) + - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) + - When prompted, select "Individual Components" and make sure to download the following: + - MSVC v143 VS 2022 C++ x64/x86 build tools + - Windows 10/11 SDK - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup From ce0860fc180252221772c150a68d3c2c735a23d9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:49:25 -0500 Subject: [PATCH 30/49] Error handling for frame buffer --- .../funkin/graphics/framebuffer/FrameBufferManager.hx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/funkin/graphics/framebuffer/FrameBufferManager.hx b/source/funkin/graphics/framebuffer/FrameBufferManager.hx index 6a0aaac53..4d484fb8f 100644 --- a/source/funkin/graphics/framebuffer/FrameBufferManager.hx +++ b/source/funkin/graphics/framebuffer/FrameBufferManager.hx @@ -53,6 +53,11 @@ class FrameBufferManager */ public function copySpriteTo(name:String, sprite:FlxSprite, color:Null = null):Void { + if (!frameBufferMap.exists(name)) + { + FlxG.log.warn('frame buffer "$name" does not exist'); + return; + } frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color)); } @@ -63,6 +68,11 @@ class FrameBufferManager */ public function moveSpriteTo(name:String, sprite:FlxSprite):Void { + if (!frameBufferMap.exists(name)) + { + FlxG.log.warn('frame buffer "$name" does not exist'); + return; + } frameBufferMap[name].moveSprite(sprite); } From f14193e17be7fa0bab41899d4590f63a63d83dbb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:49:36 -0500 Subject: [PATCH 31/49] Fix black screen on Grabbable Camera --- source/funkin/graphics/framebuffer/GrabbableCamera.hx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/framebuffer/GrabbableCamera.hx index 0fa372123..90d292d72 100644 --- a/source/funkin/graphics/framebuffer/GrabbableCamera.hx +++ b/source/funkin/graphics/framebuffer/GrabbableCamera.hx @@ -87,7 +87,7 @@ class GrabbableCamera extends FlxCamera BitmapDataUtil.applyFilter(bgBitmap, filter); } - function squashTo(bitmap:BitmapData, applyFilters:Bool, isolate:Bool):Void + function squashTo(bitmap:BitmapData, applyFilters:Bool, isolate:Bool, clearScreen:Bool = false):Void { if (applyFilters && isolate) { @@ -132,9 +132,12 @@ class GrabbableCamera extends FlxCamera bgBitmap.draw(bitmap); } - // clear graphics data - super.clearDrawStack(); - canvas.graphics.clear(); + if (clearScreen) + { + // clear graphics data + // super.clearDrawStack(); + // canvas.graphics.clear(); + } // render the background bitmap bgFrame.frame.set(0, 0, width, height); From f4d4d6a1943e43c8b793a678197c340f72e033df Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:49:56 -0500 Subject: [PATCH 32/49] Fix deprecation issue with FlxTweenType. --- source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx index 6c5152a29..376d0cebd 100644 --- a/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/CopyItemsCommand.hx @@ -138,7 +138,7 @@ class CopyItemsCommand implements ChartEditorCommand state.txtCopyNotif.y = FlxG.mouse.y - 16; FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5, { - type: FlxTween.ONESHOT, + type: FlxTweenType.ONESHOT, ease: FlxEase.quadOut, onComplete: function(_) { state.txtCopyNotif.visible = false; From c9ff35ab12f228a17fdd35fd57f5262d24c53ed8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:50:09 -0500 Subject: [PATCH 33/49] Add additional troubleshooting documentation --- docs/troubleshooting.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 27208689b..3b93bab64 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -10,3 +10,6 @@ - `Class lists not properly generated. Try cleaning out your export folder, restarting your IDE, and rebuilding your project.` - This is a bug specific to HTML5. Simply perform the steps listed (don't forget to restart the IDE too). + +- `LINK : fatal error LNK1201: error writing to program database ''; check for insufficient disk space, invalid path, or insufficient privilege` + - This error occurs if the PDB file located in your `export` folder is in use or exceeds 4 GB. Try deleting the `export` folder and building again from scratch. From 60cde5595053ca2a4c97e50be42e26d2f08a7d20 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:50:18 -0500 Subject: [PATCH 34/49] Add scripted FunkinSprite --- source/funkin/modding/base/ScriptedFunkinSprite.hx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 source/funkin/modding/base/ScriptedFunkinSprite.hx diff --git a/source/funkin/modding/base/ScriptedFunkinSprite.hx b/source/funkin/modding/base/ScriptedFunkinSprite.hx new file mode 100644 index 000000000..dd8d15007 --- /dev/null +++ b/source/funkin/modding/base/ScriptedFunkinSprite.hx @@ -0,0 +1,8 @@ +package funkin.modding.base; + +/** + * A script that can be tied to an FlxSprite. + * Create a scripted class that extends FlxSprite to use this. + */ +@:hscriptClass +class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements HScriptedClass {} From ef28d34520488b814f0516cf41d641e959607f7e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:50:29 -0500 Subject: [PATCH 35/49] Sort PlayState imports --- source/funkin/play/PlayState.hx | 73 ++++++++++++++++----------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 00be1ad99..e9ead8bbe 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1,80 +1,79 @@ package funkin.play; -import funkin.ui.SwagCamera; -import flixel.addons.transition.FlxTransitionableSubState; -import funkin.ui.debug.charting.ChartEditorState; -import haxe.Int64; -import funkin.play.notes.notestyle.NoteStyle; -import funkin.data.notestyle.NoteStyleData; -import funkin.data.notestyle.NoteStyleRegistry; import flixel.addons.display.FlxPieDial; -import flixel.addons.transition.Transition; +import flixel.addons.display.FlxPieDial; import flixel.addons.transition.FlxTransitionableState; +import flixel.addons.transition.FlxTransitionableState; +import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.Transition; +import flixel.addons.transition.Transition; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxSprite; import flixel.FlxState; import flixel.FlxSubState; -import flixel.addons.display.FlxPieDial; -import flixel.addons.transition.FlxTransitionableState; -import flixel.addons.transition.FlxTransitionableSubState; -import flixel.addons.transition.Transition; import flixel.math.FlxMath; -import funkin.play.components.ComboMilestone; import flixel.math.FlxPoint; -import funkin.play.components.HealthIcon; -import funkin.ui.MusicBeatSubState; import flixel.math.FlxRect; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.ui.FlxBar; import flixel.util.FlxColor; -import funkin.api.newgrounds.NGio; import flixel.util.FlxTimer; -import openfl.Lib; -import openfl.display.BitmapData; -import openfl.geom.Rectangle; +import funkin.api.newgrounds.NGio; import funkin.audio.VoicesGroup; -import funkin.save.Save; -import funkin.Highscore.Tallies; -import funkin.play.notes.NoteSplash; import funkin.audio.VoicesGroup; +import funkin.data.dialogue.ConversationRegistry; +import funkin.data.event.SongEventRegistry; +import funkin.data.notestyle.NoteStyleData; import funkin.data.notestyle.NoteStyleRegistry; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.data.song.SongData.SongCharacterData; +import funkin.data.song.SongData.SongEventData; +import funkin.data.song.SongData.SongNoteData; +import funkin.data.song.SongRegistry; +import funkin.data.stage.StageRegistry; +import funkin.Highscore.Tallies; import funkin.input.PreciseInputManager; import funkin.modding.events.ScriptEvent; -import funkin.ui.mainmenu.MainMenuState; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.BaseCharacter; import funkin.play.character.CharacterData.CharacterDataParser; +import funkin.play.components.ComboMilestone; +import funkin.play.components.HealthIcon; +import funkin.play.components.PopUpStuff; +import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.dialogue.Conversation; -import funkin.data.dialogue.ConversationRegistry; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; -import funkin.data.event.SongEventRegistry; -import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteDirection; +import funkin.play.notes.NoteSplash; import funkin.play.notes.NoteSprite; +import funkin.play.notes.NoteSprite; +import funkin.play.notes.notestyle.NoteStyle; +import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; -import funkin.play.notes.notestyle.NoteStyle; import funkin.play.scoring.Scoring; import funkin.play.song.Song; -import funkin.data.song.SongRegistry; -import funkin.data.stage.StageRegistry; -import funkin.data.song.SongData.SongEventData; -import funkin.data.song.SongData.SongNoteData; -import funkin.data.song.SongData.SongCharacterData; import funkin.play.stage.Stage; -import funkin.ui.transition.LoadingState; -import funkin.play.components.PopUpStuff; -import funkin.ui.options.PreferencesMenu; +import funkin.save.Save; +import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.debug.stage.StageOffsetSubState; +import funkin.ui.mainmenu.MainMenuState; +import funkin.ui.MusicBeatSubState; +import funkin.ui.options.PreferencesMenu; import funkin.ui.story.StoryMenuState; +import funkin.ui.SwagCamera; +import funkin.ui.transition.LoadingState; import funkin.util.SerializerUtil; +import haxe.Int64; import lime.ui.Haptic; -import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.ConversationDataParser; +import openfl.display.BitmapData; +import openfl.geom.Rectangle; +import openfl.Lib; #if discord_rpc import Discord.DiscordClient; #end From 2d8e686b331c69de1bf597edc271226624b66795 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:50:40 -0500 Subject: [PATCH 36/49] Attempt to fix StickerState bug --- .../funkin/ui/transition/StickerSubState.hx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index fa36cfd50..7b5a88491 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -116,10 +116,19 @@ class StickerSubState extends MusicBeatSubState { grpStickers.cameras = FlxG.cameras.list; - if (dipshit != null) + /* + if (dipshit != null) + { + FlxG.removeChild(dipshit); + dipshit = null; + } + */ + + if (grpStickers.members == null || grpStickers.members.length == 0) { - FlxG.removeChild(dipshit); - dipshit = null; + switchingState = false; + close(); + return; } for (ind => sticker in grpStickers.members) @@ -235,15 +244,19 @@ class StickerSubState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - dipshit = new Sprite(); - var scrn:BitmapData = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000); - var mat:Matrix = new Matrix(); - scrn.draw(grpStickers.cameras[0].canvas, mat); + // I think this grabs the screen and puts it under the stickers? + // Leaving this commented out rather than stripping it out because it's cool... + /* + dipshit = new Sprite(); + var scrn:BitmapData = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000); + var mat:Matrix = new Matrix(); + scrn.draw(grpStickers.cameras[0].canvas, mat); - var bitmap:Bitmap = new Bitmap(scrn); + var bitmap:Bitmap = new Bitmap(scrn); - dipshit.addChild(bitmap); - FlxG.addChildBelowMouse(dipshit); + dipshit.addChild(bitmap); + // FlxG.addChildBelowMouse(dipshit); + */ FlxG.switchState(() -> targetState(this)); } From 31e80381bbfe368c4e394fb4fe2bdcdc8680d181 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 16:51:41 -0500 Subject: [PATCH 37/49] Update assets submodule. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 6825d762b..56a79d3b0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6825d762bd007a50405a4e38939862f5ba70481f +Subproject commit 56a79d3b018876c3c9a2c47339335053b001acc0 From 92f52b2c3f62c3402d95562de45849ded60b02df Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 12 Feb 2024 23:15:18 -0500 Subject: [PATCH 38/49] flixel debugging on test builds --- .github/workflows/build-shit.yml | 8 ++++---- Project.xml | 12 +++--------- source/funkin/InitState.hx | 13 ++++++------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index 76126d106..8ea3b16f3 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -31,7 +31,7 @@ jobs: sudo apt-get install -y libx11-dev xorg-dev libgl-dev libxi-dev libxext-dev libasound2-dev libxinerama-dev libxrandr-dev libgl1-mesa-dev - name: build game run: | - haxelib run lime build html5 -release --times + haxelib run lime build html5 -release --times -DGITHUB_BUILD ls - uses: ./.github/actions/upload-itch with: @@ -68,7 +68,7 @@ jobs: key: ${{ runner.os }}-build-win-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }} - name: build game run: | - haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER + haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER -DGITHUB_BUILD dir env: HXCPP_COMPILE_CACHE: "${{ runner.temp }}\\hxcpp_cache" @@ -110,7 +110,7 @@ jobs: key: ${{ runner.os }}-build-mac-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }} - name: Build game run: | - haxelib run lime build macos -release --times + haxelib run lime build macos -release --times -DGITHUB_BUILD ls env: HXCPP_COMPILE_CACHE: "${{ runner.temp }}/hxcpp_cache" @@ -119,7 +119,7 @@ jobs: butler-key: ${{ secrets.BUTLER_API_KEY}} build-dir: export/release/macos/bin target: macos - + # test-unit-win: # needs: create-nightly-win # runs-on: windows-latest diff --git a/Project.xml b/Project.xml index 40f309e1f..560baeadf 100644 --- a/Project.xml +++ b/Project.xml @@ -91,7 +91,8 @@ NOT USING A DIRECT THING TO THE ASSET!!! --> - + + @@ -118,7 +119,7 @@ - + @@ -211,13 +212,6 @@ -
- - - - -
- --> --> diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 625a33ad7..21946819f 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -144,13 +144,12 @@ class InitState extends FlxState // Make errors and warnings less annoying. // TODO: Disable this so we know to fix warnings. - if (false) - { - LogStyle.ERROR.openConsole = false; - LogStyle.ERROR.errorSound = null; - LogStyle.WARNING.openConsole = false; - LogStyle.WARNING.errorSound = null; - } + #if FORCE_DEBUG_VERSION + LogStyle.ERROR.openConsole = false; + LogStyle.ERROR.errorSound = null; + LogStyle.WARNING.openConsole = false; + LogStyle.WARNING.errorSound = null; + #end #end // From 0494ddb5063ff549803cb77ee89fb82cb08aabd0 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 23:19:25 -0500 Subject: [PATCH 39/49] Fix crash in chart editor minimal Playtest --- source/funkin/play/PlayState.hx | 2 +- source/funkin/play/stage/Stage.hx | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e9ead8bbe..c2b4e5fc2 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1976,7 +1976,7 @@ class PlayState extends MusicBeatSubState // Mute vocals and play miss animation, but don't penalize. vocals.playerVolume = 0; - currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true); + if (currentStage != null && currentStage.getBoyfriend() != null) currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true); } } } diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index fc23b6ace..8c0a46d97 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -747,13 +747,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements } } - public function onUpdate(event:UpdateScriptEvent) - { - if (FlxG.keys.justPressed.F3) - { - debugIconGroup.visible = !debugIconGroup.visible; - } - } + public function onUpdate(event:UpdateScriptEvent) {} public override function kill() { From 1b5b7f0ae23dd502d89f0c0ffe4567161780ff95 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 23:34:40 -0500 Subject: [PATCH 40/49] Fix a bug where stickers were broken on story mode --- source/funkin/play/PauseSubState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index d38e3ac87..d44acf597 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -234,7 +234,7 @@ class PauseSubState extends MusicBeatSubState if (PlayStatePlaylist.isStoryMode) { PlayStatePlaylist.reset(); - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState())); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState(sticker))); } else { From 0ff258ce755cceb33f85659cd16c2fdd57e91469 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 12 Feb 2024 23:34:51 -0500 Subject: [PATCH 41/49] Use stickers to transition from the Results state --- source/funkin/play/ResultState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index caa576bcf..b8a5791c2 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -352,11 +352,11 @@ class ResultState extends MusicBeatSubState { if (params.storyMode) { - FlxG.switchState(() -> new StoryMenuState()); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState(sticker))); } else { - FlxG.switchState(() -> new FreeplayState()); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.FreeplayState(null, sticker))); } } From 5f5b2bb80b4fed29f5046dd26c046659372e6df4 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:00:27 -0500 Subject: [PATCH 42/49] pico death animation loop fix --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 594853037..3e6c397d1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 594853037cbea06caa5c141b0d9ed3736818e592 +Subproject commit 3e6c397d11d190fcbf90545dc35110b2bde7830c From 1d8e82b108a75455948d9ba8e292e6eda46fa029 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:18:47 -0500 Subject: [PATCH 43/49] makes prebuild.hx actually run on prebuild --- Project.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.xml b/Project.xml index 560baeadf..d15a5affd 100644 --- a/Project.xml +++ b/Project.xml @@ -213,7 +213,7 @@ - --> + --> --> From 6e0f577d54f48b77c6e1e7e58cbeddc5311d8c57 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:29:46 -0500 Subject: [PATCH 44/49] debugger fixes --- source/funkin/InitState.hx | 5 ++--- source/funkin/play/GameOverSubState.hx | 1 + source/funkin/play/PauseSubState.hx | 2 +- source/funkin/play/PlayState.hx | 20 +++++++++---------- .../ui/debug/charting/ChartEditorState.hx | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 21946819f..09afe6cf4 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -89,7 +89,7 @@ class InitState extends FlxState // // FLIXEL DEBUG SETUP // - #if debug + #if (debug || FORCE_DEBUG_VERSION) // Disable using ~ to open the console (we use that for the Editor menu) FlxG.debugger.toggleKeys = [F2]; @@ -141,16 +141,15 @@ class InitState extends FlxState FlxG.sound.music.pause(); FlxG.sound.music.time += FlxG.elapsed * 1000; }); + #end // Make errors and warnings less annoying. - // TODO: Disable this so we know to fix warnings. #if FORCE_DEBUG_VERSION LogStyle.ERROR.openConsole = false; LogStyle.ERROR.errorSound = null; LogStyle.WARNING.openConsole = false; LogStyle.WARNING.errorSound = null; #end - #end // // FLIXEL TRANSITIONS diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 36f72237e..f6d75c61b 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -90,6 +90,7 @@ class GameOverSubState extends MusicBeatSubState { animationSuffix = ""; musicSuffix = ""; + blueBallSuffix = ""; } override public function create() diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index c9039ce40..77999b60a 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -168,7 +168,7 @@ class PauseSubState extends MusicBeatSubState var downP = controls.UI_DOWN_P; var accepted = controls.ACCEPT; - #if debug + #if (debug || FORCE_DEBUG_VERSION) // to pause the game and get screenshots easy, press H on pause menu! if (FlxG.keys.justPressed.H) { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index aee9f2210..c9d41f254 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -638,7 +638,7 @@ class PlayState extends MusicBeatSubState rightWatermarkText.cameras = [camHUD]; // Initialize some debug stuff. - #if debug + #if (debug || FORCE_DEBUG_VERSION) // Display the version number (and git commit hash) in the bottom right corner. this.rightWatermarkText.text = Constants.VERSION; @@ -907,7 +907,7 @@ class PlayState extends MusicBeatSubState // Disable updates, preventing animations in the background from playing. persistentUpdate = false; - #if debug + #if (debug || FORCE_DEBUG_VERSION) if (FlxG.keys.pressed.THREE) { // TODO: Change the key or delete this? @@ -918,7 +918,7 @@ class PlayState extends MusicBeatSubState { #end persistentDraw = false; - #if debug + #if (debug || FORCE_DEBUG_VERSION) } #end @@ -1368,7 +1368,7 @@ class PlayState extends MusicBeatSubState // Add the stage to the scene. this.add(currentStage); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('stage', currentStage); #end } @@ -1458,7 +1458,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(girlfriend, GF); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('gf', girlfriend); #end } @@ -1467,7 +1467,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(boyfriend, BF); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('bf', boyfriend); #end } @@ -1478,7 +1478,7 @@ class PlayState extends MusicBeatSubState // Camera starts at dad. cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y); - #if debug + #if (debug || FORCE_DEBUG_VERSION) FlxG.console.registerObject('dad', dad); #end } @@ -2253,7 +2253,7 @@ class PlayState extends MusicBeatSubState })); } - #if debug + #if (debug || FORCE_DEBUG_VERSION) // 1: End the song immediately. if (FlxG.keys.justPressed.ONE) endSong(); @@ -2267,7 +2267,7 @@ class PlayState extends MusicBeatSubState // 9: Toggle the old icon. if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon(); - #if debug + #if (debug || FORCE_DEBUG_VERSION) // PAGEUP: Skip forward two sections. // SHIFT+PAGEUP: Skip forward twenty sections. if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2); @@ -2768,7 +2768,7 @@ class PlayState extends MusicBeatSubState FlxG.camera.focusOn(cameraFollowPoint.getPosition()); } - #if debug + #if (debug || FORCE_DEBUG_VERSION) /** * Jumps forward or backward a number of sections in the song. * Accounts for BPM changes, does not prevent death from skipped notes. diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index dab79a21c..759a54ec7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3183,7 +3183,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState handleTestKeybinds(); handleHelpKeybinds(); - #if debug + #if (debug || FORCE_DEBUG_VERSION) handleQuickWatch(); #end From 1ad17aa904153333b6ec26936a8c81c6c5055f2a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 00:45:29 -0500 Subject: [PATCH 45/49] flixel hmm update --- assets | 2 +- hmm.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 594853037..160acbd8a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 594853037cbea06caa5c141b0d9ed3736818e592 +Subproject commit 160acbd8a854a9f677ef7587396340e79a5ea6ca diff --git a/hmm.json b/hmm.json index b42a67928..7321e731f 100644 --- a/hmm.json +++ b/hmm.json @@ -11,7 +11,7 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "07c6018008801972d12275690fc144fcc22e3de6", + "ref": "25c84b29665329f7c6366342542a3978f29300ee", "url": "https://github.com/FunkinCrew/flixel" }, { From 681cfa0883d9ac823cc768301708bc5d14bf04d3 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 01:16:09 -0500 Subject: [PATCH 46/49] chart editor waveform fixes for single vocal audio files --- .../charting/toolboxes/ChartEditorFreeplayToolbox.hx | 2 +- .../charting/toolboxes/ChartEditorOffsetsToolbox.hx | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorFreeplayToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorFreeplayToolbox.hx index 8d3554a08..c384e7a6d 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorFreeplayToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorFreeplayToolbox.hx @@ -290,7 +290,7 @@ class ChartEditorFreeplayToolbox extends ChartEditorBaseToolbox // waveformMusic.waveform.forceUpdate = true; var perfStart = haxe.Timer.stamp(); var waveformData1 = playerVoice.waveformData; - var waveformData2 = opponentVoice.waveformData; + var waveformData2 = opponentVoice?.waveformData ?? playerVoice.waveformData; // this null check is for songs that only have 1 vocals file! var waveformData3 = chartEditorState.audioInstTrack.waveformData; var waveformData = waveformData1.merge(waveformData2).merge(waveformData3); trace('Waveform data merging took: ${haxe.Timer.stamp() - perfStart} seconds'); diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx index 67ca82b1b..fd9209294 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx @@ -276,8 +276,13 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox // Build opponent waveform. // waveformOpponent.waveform.forceUpdate = true; - waveformOpponent.waveform.waveformData = opponentVoice.waveformData; - waveformOpponent.waveform.duration = opponentVoice.length / Constants.MS_PER_SEC; + // note: if song only has one set of vocals (Vocals.ogg/mp3) then this is null and crashes charting editor + // so we null check + if (opponentVoice != null) + { + waveformOpponent.waveform.waveformData = opponentVoice.waveformData; + waveformOpponent.waveform.duration = opponentVoice.length / Constants.MS_PER_SEC; + } // Build instrumental waveform. // waveformInstrumental.waveform.forceUpdate = true; From e3623311743dd0f8a6d799ea3311cf2b0f46a19b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 13 Feb 2024 01:19:00 -0500 Subject: [PATCH 47/49] assets submod update --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 3e6c397d1..160acbd8a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3e6c397d11d190fcbf90545dc35110b2bde7830c +Subproject commit 160acbd8a854a9f677ef7587396340e79a5ea6ca From f0b5ef4491a4f664d7306a547c89279547a58dbb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 13 Feb 2024 01:38:11 -0500 Subject: [PATCH 48/49] Rest in peace, SwagCamera. --- hmm.json | 2 +- .../GrabbableCamera.hx => FunkinCamera.hx} | 20 +++- source/funkin/play/PauseSubState.hx | 3 +- source/funkin/play/PlayState.hx | 17 ++- source/funkin/play/ResultState.hx | 4 +- source/funkin/play/stage/Stage.hx | 6 +- source/funkin/ui/SwagCamera.hx | 104 ------------------ .../ui/debug/charting/ChartEditorState.hx | 5 +- source/funkin/ui/freeplay/FreeplayState.hx | 3 +- source/funkin/ui/mainmenu/MainMenuState.hx | 3 +- source/funkin/ui/options/ControlsMenu.hx | 3 +- source/funkin/ui/options/PreferencesMenu.hx | 3 +- .../funkin/ui/transition/StickerSubState.hx | 1 + 13 files changed, 49 insertions(+), 125 deletions(-) rename source/funkin/graphics/{framebuffer/GrabbableCamera.hx => FunkinCamera.hx} (90%) delete mode 100644 source/funkin/ui/SwagCamera.hx diff --git a/hmm.json b/hmm.json index 01f2a3e64..4842a62db 100644 --- a/hmm.json +++ b/hmm.json @@ -11,7 +11,7 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "07c6018008801972d12275690fc144fcc22e3de6", + "ref": "4d054bd10b05bb1309a0ba3427ffa5378e0b4b99", "url": "https://github.com/FunkinCrew/flixel" }, { diff --git a/source/funkin/graphics/framebuffer/GrabbableCamera.hx b/source/funkin/graphics/FunkinCamera.hx similarity index 90% rename from source/funkin/graphics/framebuffer/GrabbableCamera.hx rename to source/funkin/graphics/FunkinCamera.hx index 90d292d72..f80e799ea 100644 --- a/source/funkin/graphics/framebuffer/GrabbableCamera.hx +++ b/source/funkin/graphics/FunkinCamera.hx @@ -1,4 +1,4 @@ -package funkin.graphics.framebuffer; +package funkin.graphics; import flash.geom.ColorTransform; import flixel.FlxCamera; @@ -8,6 +8,8 @@ import flixel.math.FlxMatrix; import flixel.math.FlxRect; import flixel.system.FlxAssets.FlxShader; import funkin.graphics.shaders.RuntimeCustomBlendShader; +import funkin.graphics.framebuffer.BitmapDataUtil; +import funkin.graphics.framebuffer.FixedBitmapData; import openfl.Lib; import openfl.display.BitmapData; import openfl.display.BlendMode; @@ -16,7 +18,11 @@ import openfl.filters.BitmapFilter; import openfl.filters.ShaderFilter; /** - * A camera, but grabbable. Also supports several additional blend modes. + * A FlxCamera with additional powerful features: + * - Grab the camera screen as a `BitmapData` and use it as a texture + * - Support `sprite.blend = DARKEN/HARDLIGHT/LIGHTEN/OVERLAY` to apply visual effects using certain sprites + * - NOTE: Several other blend modes work without FunkinCamera. Some still do not work. + * - NOTE: Framerate-independent camera tweening is fixed in Flixel 6.x. Rest in peace, SwagCamera. */ @:access(openfl.display.DisplayObject) @:access(openfl.display.BitmapData) @@ -24,7 +30,7 @@ import openfl.filters.ShaderFilter; @:access(openfl.display3D.textures.TextureBase) @:access(flixel.graphics.FlxGraphic) @:access(flixel.graphics.frames.FlxFrame) -class GrabbableCamera extends FlxCamera +class FunkinCamera extends FlxCamera { final grabbed:Array = []; final texturePool:Array = []; @@ -39,6 +45,8 @@ class GrabbableCamera extends FlxCamera var filtersApplied:Bool = false; var bgItemCount:Int = 0; + public var shouldDraw:Bool = true; + public function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0) { super(x, y, width, height, zoom); @@ -135,8 +143,8 @@ class GrabbableCamera extends FlxCamera if (clearScreen) { // clear graphics data - // super.clearDrawStack(); - // canvas.graphics.clear(); + super.clearDrawStack(); + canvas.graphics.clear(); } // render the background bitmap @@ -169,6 +177,8 @@ class GrabbableCamera extends FlxCamera override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader):Void { + if (!shouldDraw) return; + if ( switch blend { case DARKEN | HARDLIGHT | LIGHTEN | OVERLAY: true; diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index d44acf597..5db4d7bea 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -38,7 +38,8 @@ class PauseSubState extends MusicBeatSubState var practiceText:FlxText; - var exitingToMenu:Bool = false; + public var exitingToMenu:Bool = false; + var bg:FlxSprite; var metaDataGrp:FlxTypedGroup; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index c2b4e5fc2..d1dab766a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -66,7 +66,7 @@ import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatSubState; import funkin.ui.options.PreferencesMenu; import funkin.ui.story.StoryMenuState; -import funkin.ui.SwagCamera; +import funkin.graphics.FunkinCamera; import funkin.ui.transition.LoadingState; import funkin.util.SerializerUtil; import haxe.Int64; @@ -420,7 +420,7 @@ class PlayState extends MusicBeatSubState /** * The camera which contains, and controls visibility of, the stage and characters. */ - public var camGame:SwagCamera; + public var camGame:FlxCamera; /** * The camera which contains, and controls visibility of, a video cutscene. @@ -456,6 +456,17 @@ class PlayState extends MusicBeatSubState return this.subState != null; } + var isExitingViaPauseMenu(get, never):Bool; + + function get_isExitingViaPauseMenu():Bool + { + if (this.subState == null) return false; + if (!Std.isOfType(this.subState, PauseSubState)) return false; + + var pauseSubState:PauseSubState = cast this.subState; + return pauseSubState.exitingToMenu; + } + /** * Data for the current difficulty for the current song. * Includes chart data, scroll speed, and other information. @@ -1293,7 +1304,7 @@ class PlayState extends MusicBeatSubState */ function initCameras():Void { - camGame = new SwagCamera(); + camGame = new FlxCamera(); camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index b8a5791c2..9ffeefcfd 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -352,11 +352,11 @@ class ResultState extends MusicBeatSubState { if (params.storyMode) { - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState(sticker))); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker))); } else { - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.FreeplayState(null, sticker))); + openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new FreeplayState(null, sticker))); } } diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 8c0a46d97..af5765b25 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -4,7 +4,7 @@ import openfl.display.BlendMode; import funkin.graphics.framebuffer.FrameBufferManager; import flixel.util.FlxColor; import funkin.graphics.framebuffer.SpriteCopy; -import funkin.graphics.framebuffer.GrabbableCamera; +import funkin.graphics.FunkinCamera; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; @@ -805,9 +805,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements */ function grabScreen(applyFilters:Bool):BitmapData { - if (Std.isOfType(FlxG.camera, GrabbableCamera)) + if (Std.isOfType(FlxG.camera, FunkinCamera)) { - final cam:GrabbableCamera = cast FlxG.camera; + final cam:FunkinCamera = cast FlxG.camera; return cam.grabScreen(applyFilters); } else diff --git a/source/funkin/ui/SwagCamera.hx b/source/funkin/ui/SwagCamera.hx deleted file mode 100644 index bb6a24c3d..000000000 --- a/source/funkin/ui/SwagCamera.hx +++ /dev/null @@ -1,104 +0,0 @@ -package funkin.ui; - -import funkin.graphics.framebuffer.GrabbableCamera; -import flixel.FlxCamera; -import flixel.FlxSprite; -import flixel.math.FlxPoint; -import funkin.util.MathUtil; - -class SwagCamera extends GrabbableCamera -{ - /** - * properly follow framerate - * most of this just copied from FlxCamera, - * only lines 96 and 97 are changed - */ - override public function updateFollow():Void - { - // Either follow the object closely, - // or double check our deadzone and update accordingly. - if (deadzone == null) - { - target.getMidpoint(_point); - _point.addPoint(targetOffset); - focusOn(_point); - } - else - { - var edge:Float; - var targetX:Float = target.x + targetOffset.x; - var targetY:Float = target.y + targetOffset.y; - - if (style == SCREEN_BY_SCREEN) - { - if (targetX >= (scroll.x + width)) - { - _scrollTarget.x += width; - } - else if (targetX < scroll.x) - { - _scrollTarget.x -= width; - } - - if (targetY >= (scroll.y + height)) - { - _scrollTarget.y += height; - } - else if (targetY < scroll.y) - { - _scrollTarget.y -= height; - } - } - else - { - edge = targetX - deadzone.x; - if (_scrollTarget.x > edge) - { - _scrollTarget.x = edge; - } - edge = targetX + target.width - deadzone.x - deadzone.width; - if (_scrollTarget.x < edge) - { - _scrollTarget.x = edge; - } - - edge = targetY - deadzone.y; - if (_scrollTarget.y > edge) - { - _scrollTarget.y = edge; - } - edge = targetY + target.height - deadzone.y - deadzone.height; - if (_scrollTarget.y < edge) - { - _scrollTarget.y = edge; - } - } - - if ((target is FlxSprite)) - { - if (_lastTargetPosition == null) - { - _lastTargetPosition = FlxPoint.get(target.x, target.y); // Creates this point. - } - _scrollTarget.x += (target.x - _lastTargetPosition.x) * followLead.x; - _scrollTarget.y += (target.y - _lastTargetPosition.y) * followLead.y; - - _lastTargetPosition.x = target.x; - _lastTargetPosition.y = target.y; - } - - if (followLerp >= 60 / FlxG.updateFramerate) - { - scroll.copyFrom(_scrollTarget); // no easing - } - else - { - // THIS THE PART THAT ACTUALLY MATTERS LOL - scroll.x = MathUtil.coolLerp(scroll.x, _scrollTarget.x, followLerp); - scroll.y = MathUtil.coolLerp(scroll.y, _scrollTarget.y, followLerp); - // scroll.x += (_scrollTarget.x - scroll.x) * MathUtil.cameraLerp(followLerp); - // scroll.y += (_scrollTarget.y - scroll.y) * MathUtil.cameraLerp(followLerp); - } - } - } -} diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 7730fdc95..0209eaf11 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -8,6 +8,7 @@ import flixel.FlxSprite; import flixel.FlxSubState; import flixel.graphics.FlxGraphic; import flixel.group.FlxGroup.FlxTypedGroup; +import funkin.graphics.FunkinCamera; import flixel.group.FlxSpriteGroup; import flixel.input.keyboard.FlxKey; import flixel.input.mouse.FlxMouseEvent; @@ -2071,7 +2072,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState loadPreferences(); - uiCamera = new FlxCamera(); + uiCamera = new FunkinCamera(); FlxG.cameras.reset(uiCamera); buildDefaultSongData(); @@ -5357,7 +5358,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Kill and replace the UI camera so it doesn't get destroyed during the state transition. uiCamera.kill(); FlxG.cameras.remove(uiCamera, false); - FlxG.cameras.reset(new FlxCamera()); + FlxG.cameras.reset(new FunkinCamera()); this.persistentUpdate = false; this.persistentDraw = false; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 9cbab2cb5..e7c615313 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -14,6 +14,7 @@ import flixel.group.FlxSpriteGroup; import flixel.input.touch.FlxTouch; import flixel.math.FlxAngle; import flixel.math.FlxMath; +import funkin.graphics.FunkinCamera; import flixel.math.FlxPoint; import flixel.system.debug.watch.Tracker.TrackerProfile; import flixel.text.FlxText; @@ -573,7 +574,7 @@ class FreeplayState extends MusicBeatSubState var swag:Alphabet = new Alphabet(1, 0, "swag"); - var funnyCam = new FlxCamera(0, 0, FlxG.width, FlxG.height); + var funnyCam = new FunkinCamera(0, 0, FlxG.width, FlxG.height); funnyCam.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(funnyCam); diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 3da041ada..8842c37de 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -13,6 +13,7 @@ import flixel.group.FlxGroup.FlxTypedGroup; import flixel.input.touch.FlxTouch; import flixel.text.FlxText; import flixel.tweens.FlxEase; +import funkin.graphics.FunkinCamera; import flixel.tweens.FlxTween; import funkin.ui.MusicBeatState; import flixel.util.FlxTimer; @@ -152,7 +153,7 @@ class MainMenuState extends MusicBeatState function resetCamStuff() { - FlxG.cameras.reset(new SwagCamera()); + FlxG.cameras.reset(new FunkinCamera()); FlxG.camera.follow(camFollow, null, 0.06); } diff --git a/source/funkin/ui/options/ControlsMenu.hx b/source/funkin/ui/options/ControlsMenu.hx index ea04e1208..bda071846 100644 --- a/source/funkin/ui/options/ControlsMenu.hx +++ b/source/funkin/ui/options/ControlsMenu.hx @@ -4,6 +4,7 @@ import funkin.util.InputUtil; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxSprite; +import funkin.graphics.FunkinCamera; import flixel.group.FlxGroup; import flixel.input.actions.FlxActionInput; import flixel.input.gamepad.FlxGamepadInputID; @@ -47,7 +48,7 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page { super(); - menuCamera = new FlxCamera(); + menuCamera = new FunkinCamera(); FlxG.cameras.add(menuCamera, false); menuCamera.bgColor = 0x0; camera = menuCamera; diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx index b4b3c7db8..c23c3f165 100644 --- a/source/funkin/ui/options/PreferencesMenu.hx +++ b/source/funkin/ui/options/PreferencesMenu.hx @@ -6,6 +6,7 @@ import flixel.FlxSprite; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import funkin.ui.AtlasText.AtlasFont; import funkin.ui.options.OptionsState.Page; +import funkin.graphics.FunkinCamera; import funkin.ui.TextMenuList.TextMenuItem; class PreferencesMenu extends Page @@ -20,7 +21,7 @@ class PreferencesMenu extends Page { super(); - menuCamera = new SwagCamera(); + menuCamera = new FunkinCamera(); FlxG.cameras.add(menuCamera, false); menuCamera.bgColor = 0x0; camera = menuCamera; diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 7b5a88491..e94eed7d5 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -241,6 +241,7 @@ class StickerSubState extends MusicBeatSubState if (ind == grpStickers.members.length - 1) { switchingState = true; + FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; From f7da12acbcde7295043fa658f13a93764522827b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 13 Feb 2024 02:09:25 -0500 Subject: [PATCH 49/49] Apparently the transition bug was TransitionableSubState's fault so kill it with fire. --- assets | 2 +- source/funkin/play/PlayState.hx | 10 +++++----- source/funkin/ui/MusicBeatSubState.hx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets b/assets index 56a79d3b0..a5f992452 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 56a79d3b018876c3c9a2c47339335053b001acc0 +Subproject commit a5f9924529f4ce28f7576eccb4369d1d42d9ef65 diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index d1dab766a..92dacbcbe 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -907,7 +907,7 @@ class PlayState extends MusicBeatSubState // TODO: Add a song event for Handle GF dance speed. // Handle player death. - if (!isInCutscene && !disableKeys && !_exiting) + if (!isInCutscene && !disableKeys) { // RESET = Quick Game Over Screen if (controls.RESET) @@ -1304,7 +1304,7 @@ class PlayState extends MusicBeatSubState */ function initCameras():Void { - camGame = new FlxCamera(); + camGame = new FunkinCamera(); camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. @@ -1761,7 +1761,7 @@ class PlayState extends MusicBeatSubState */ function resyncVocals():Void { - if (_exiting || vocals == null) return; + if (vocals == null) return; // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.) if (!FlxG.sound.music.playing) return; @@ -2541,8 +2541,8 @@ class PlayState extends MusicBeatSubState { FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu')); - transIn = FlxTransitionableState.defaultTransIn; - transOut = FlxTransitionableState.defaultTransOut; + // transIn = FlxTransitionableState.defaultTransIn; + // transOut = FlxTransitionableState.defaultTransOut; // TODO: Rework week unlock logic. // StoryMenuState.weekUnlocked[Std.int(Math.min(storyWeek + 1, StoryMenuState.weekUnlocked.length - 1))] = true; diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index 17c6e7fad..9a6f1a323 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -16,7 +16,7 @@ import funkin.input.Controls; /** * MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState. */ -class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandler +class MusicBeatSubState extends FlxSubState implements IEventHandler { public var leftWatermarkText:FlxText = null; public var rightWatermarkText:FlxText = null;