diff --git a/README.md b/README.md index 604cd9c2..6600a2a9 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ Also, the command `.\cne test` uses the source assets folder instead of the expo ## Codename Engine Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying modding, along with extensiblity and ease of use.
-Make sure to read the wiki and join the Discord server linked there!
-#### It includes many new features, as seen [here](FEATURES.md)
+### Before making issues or need help with something, check our website [HERE](https://fnf-cne-devs.github.io/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!! +#### The Engine includes many new features, as seen [here](FEATURES.md)
#### Wanna see the new features added in the most recent update? Click [here](PATCHNOTES.md)
## How to download diff --git a/assets/data/characters/bf-car.xml b/assets/data/characters/bf-car.xml index bd276ca8..b73d5a12 100644 --- a/assets/data/characters/bf-car.xml +++ b/assets/data/characters/bf-car.xml @@ -10,9 +10,9 @@ - - - - - + + + + + \ No newline at end of file diff --git a/assets/data/characters/bf-christmas.xml b/assets/data/characters/bf-christmas.xml index 88feb049..1289abdc 100644 --- a/assets/data/characters/bf-christmas.xml +++ b/assets/data/characters/bf-christmas.xml @@ -1,13 +1,13 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/bf-holding-gf.xml b/assets/data/characters/bf-holding-gf.xml index 390504bc..9053ec5c 100644 --- a/assets/data/characters/bf-holding-gf.xml +++ b/assets/data/characters/bf-holding-gf.xml @@ -1,13 +1,13 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/assets/data/characters/bf-pixel.xml b/assets/data/characters/bf-pixel.xml index b1c668a3..621ec9e0 100644 --- a/assets/data/characters/bf-pixel.xml +++ b/assets/data/characters/bf-pixel.xml @@ -1,15 +1,12 @@ - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/bf.xml b/assets/data/characters/bf.xml index 245fbffd..96f90b81 100644 --- a/assets/data/characters/bf.xml +++ b/assets/data/characters/bf.xml @@ -1,18 +1,18 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/assets/data/characters/dad.xml b/assets/data/characters/dad.xml index 39319727..3c8da4c2 100644 --- a/assets/data/characters/dad.xml +++ b/assets/data/characters/dad.xml @@ -1,13 +1,12 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/gf-car.xml b/assets/data/characters/gf-car.xml index 72ca5814..02fe7e1b 100644 --- a/assets/data/characters/gf-car.xml +++ b/assets/data/characters/gf-car.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/assets/data/characters/gf-christmas.xml b/assets/data/characters/gf-christmas.xml index 1a995a2a..d9a05715 100644 --- a/assets/data/characters/gf-christmas.xml +++ b/assets/data/characters/gf-christmas.xml @@ -1,14 +1,14 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/assets/data/characters/gf-pixel.xml b/assets/data/characters/gf-pixel.xml index 73aa39e9..eefc4cba 100644 --- a/assets/data/characters/gf-pixel.xml +++ b/assets/data/characters/gf-pixel.xml @@ -1,5 +1,5 @@ - - - - \ No newline at end of file + + + + diff --git a/assets/data/characters/gf-tankmen.xml b/assets/data/characters/gf-tankmen.xml index 7ba7df0a..a332b124 100644 --- a/assets/data/characters/gf-tankmen.xml +++ b/assets/data/characters/gf-tankmen.xml @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/assets/data/characters/gf.xml b/assets/data/characters/gf.xml index 911d2073..54a8ad59 100644 --- a/assets/data/characters/gf.xml +++ b/assets/data/characters/gf.xml @@ -1,14 +1,14 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/assets/data/characters/mom-car.xml b/assets/data/characters/mom-car.xml index e511498f..ff23aa88 100644 --- a/assets/data/characters/mom-car.xml +++ b/assets/data/characters/mom-car.xml @@ -1,16 +1,13 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + diff --git a/assets/data/characters/mom.xml b/assets/data/characters/mom.xml index 43d52076..88cbc3c9 100644 --- a/assets/data/characters/mom.xml +++ b/assets/data/characters/mom.xml @@ -1,16 +1,14 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/monster-christmas.xml b/assets/data/characters/monster-christmas.xml index 61bae9ee..73eeed57 100644 --- a/assets/data/characters/monster-christmas.xml +++ b/assets/data/characters/monster-christmas.xml @@ -1,8 +1,8 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/monster.xml b/assets/data/characters/monster.xml index df8d70bd..0da80ca9 100644 --- a/assets/data/characters/monster.xml +++ b/assets/data/characters/monster.xml @@ -1,8 +1,8 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/parents-christmas.xml b/assets/data/characters/parents-christmas.xml index 991fff09..4371389f 100644 --- a/assets/data/characters/parents-christmas.xml +++ b/assets/data/characters/parents-christmas.xml @@ -1,12 +1,25 @@ - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/data/characters/pico-speakers.xml b/assets/data/characters/pico-speakers.xml index ffb35177..36ec2528 100644 --- a/assets/data/characters/pico-speakers.xml +++ b/assets/data/characters/pico-speakers.xml @@ -1,16 +1,11 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/assets/data/characters/pico.xml b/assets/data/characters/pico.xml index 74e9baed..e5685dd8 100644 --- a/assets/data/characters/pico.xml +++ b/assets/data/characters/pico.xml @@ -1,16 +1,12 @@ - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/assets/data/characters/senpai-angry.xml b/assets/data/characters/senpai-angry.xml index c1203449..b9450a24 100644 --- a/assets/data/characters/senpai-angry.xml +++ b/assets/data/characters/senpai-angry.xml @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/assets/data/characters/senpai.xml b/assets/data/characters/senpai.xml index f243b2e5..762afb67 100644 --- a/assets/data/characters/senpai.xml +++ b/assets/data/characters/senpai.xml @@ -1,10 +1,9 @@ - - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/assets/data/characters/spirit.xml b/assets/data/characters/spirit.xml index d1ee5dc9..9885a662 100644 --- a/assets/data/characters/spirit.xml +++ b/assets/data/characters/spirit.xml @@ -1,10 +1,8 @@ - - - - - - - - - \ No newline at end of file + + + + + + + diff --git a/assets/data/characters/spooky.xml b/assets/data/characters/spooky.xml index 2a434f21..f60758e4 100644 --- a/assets/data/characters/spooky.xml +++ b/assets/data/characters/spooky.xml @@ -1,9 +1,9 @@ - - - - - - - - \ No newline at end of file + + + + + + + + diff --git a/assets/data/characters/tankman.xml b/assets/data/characters/tankman.xml index 092e0368..c06aa6c4 100644 --- a/assets/data/characters/tankman.xml +++ b/assets/data/characters/tankman.xml @@ -1,10 +1,10 @@ - - - - - - - - + + + + + + + + diff --git a/assets/data/config/credits.xml b/assets/data/config/credits.xml deleted file mode 100644 index 4f300fcb..00000000 --- a/assets/data/config/credits.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/assets/data/config/options.xml b/assets/data/config/options.xml deleted file mode 100644 index 888288d7..00000000 --- a/assets/data/config/options.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/assets/images/characters/senpai.png b/assets/images/characters/senpai.png index fa5a3293..1c7f8f52 100644 Binary files a/assets/images/characters/senpai.png and b/assets/images/characters/senpai.png differ diff --git a/assets/images/characters/senpai.xml b/assets/images/characters/senpai.xml index d8e532e4..fb01b854 100644 --- a/assets/images/characters/senpai.xml +++ b/assets/images/characters/senpai.xml @@ -1,159 +1,164 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/shaders/engine/editorBlurFast.frag b/assets/shaders/engine/editorBlurFast.frag index 75ef96af..f748a65f 100644 --- a/assets/shaders/engine/editorBlurFast.frag +++ b/assets/shaders/engine/editorBlurFast.frag @@ -13,32 +13,16 @@ float uBrightness = 0.6; vec4 getColor(vec2 pos) { vec2 ps = (pos); - if (ps.x < 0.0) ps.x = 0.0; - //else if (ps.x > 1.0 - (1.0 / iResolution.x)) ps.x = 1.0 - (1.0 / iResolution.x); - else if (ps.x > 1.0) return vec4(0.0); - if (ps.y < 0.0) ps.y = 0.0; - //else if (ps.y > 1.0 - (1.0 / iResolution.y)) return ps.y = 1.0 - (1.0 / iResolution.y); - else if (ps.y > 1.0) return vec4(0.0); + ps = clamp(ps, vec2(0.0), vec2(1.0));//1.0 - (1.0 / iResolution.xy)); return flixel_texture2D(bitmap, (ps)); } - -vec2 fixvec2(float x, float y) { // makes an uv the same across sizes - vec2 val = vec2(x, y); - val.xy *= vec2(1280.0, 720.0); - val.xy /= iResolution.xy; - return val; -} -vec2 fixvec2(vec2 uv) { // makes an uv the same across sizes - vec2 val = uv; - val.xy *= vec2(1280.0, 720.0); - val.xy /= iResolution.xy; - return val; -} +uniform sampler2D noiseTexture; +uniform vec2 noiseTextureSize; vec2 random(vec2 p) { - p = vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))); - return fract(sin(p)*4375.5); + p *= openfl_TextureSize; + return texture2D(noiseTexture, p/noiseTextureSize).rg; } void main() { @@ -47,13 +31,11 @@ void main() { if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0) return; - //vec4 color = getColor(camPos); - vec2 blur = vec2(uBlur) * vec2(1.0, iResolution.x / iResolution.y); vec4 a = getColor(camPos+(random(camPos)*blur - blur / 2.0)) * uBrightness; a += getColor(camPos+(random(camPos+0.1)*blur - blur / 2.0)) * uBrightness; - a += getColor(camPos+(random(camPos+0.2)*blur - blur / 2.0)) * uBrightness; + //a += getColor(camPos+(random(camPos+0.2)*blur - blur / 2.0)) * uBrightness; //a += getColor(camPos+(random(camPos+0.3)*blur - blur / 2.0)) * uBrightness; - gl_FragColor = a / 3.0; + gl_FragColor = a / 2.0; } \ No newline at end of file diff --git a/assets/shaders/noise128.png b/assets/shaders/noise128.png new file mode 100644 index 00000000..682cb32a Binary files /dev/null and b/assets/shaders/noise128.png differ diff --git a/assets/shaders/noise256.png b/assets/shaders/noise256.png new file mode 100644 index 00000000..fd2a1410 Binary files /dev/null and b/assets/shaders/noise256.png differ diff --git a/assets/shaders/noise512.png b/assets/shaders/noise512.png new file mode 100644 index 00000000..d50270af Binary files /dev/null and b/assets/shaders/noise512.png differ diff --git a/assets/songs/test/dialogue.hx b/assets/songs/test/dialogue.hx new file mode 100644 index 00000000..5d3e7fa0 --- /dev/null +++ b/assets/songs/test/dialogue.hx @@ -0,0 +1,5 @@ +function next(event) + if (!event.playFirst) trace("-"); + +function postNext(event) + trace(curLine.char + " says: " + curLine.text); \ No newline at end of file diff --git a/project.xml b/project.xml index 9b396fa7..6f01bc10 100644 --- a/project.xml +++ b/project.xml @@ -134,7 +134,7 @@ - + diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 1bc880ff..39783823 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -182,7 +182,7 @@ class Paths return FlxAtlasFrames.fromAseprite('$key.png', '$key.json'); inline static public function getAssetsRoot():String - return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets'; + return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end; /** * Gets frames at specified path. diff --git a/source/funkin/backend/scripting/events/CharacterNodeEvent.hx b/source/funkin/backend/scripting/events/CharacterNodeEvent.hx new file mode 100644 index 00000000..9577a64f --- /dev/null +++ b/source/funkin/backend/scripting/events/CharacterNodeEvent.hx @@ -0,0 +1,21 @@ +package funkin.backend.scripting.events; + +import funkin.game.Character; +import haxe.xml.Access; + +final class CharacterNodeEvent extends CancellableEvent { + /** + * The character instance + */ + public var character:Character; + + /** + * The node which is currently being parsed + */ + public var node:Access; + + /** + * The name of the node, quicker access than e.node.name + */ + public var name:String; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/CharacterXMLEvent.hx b/source/funkin/backend/scripting/events/CharacterXMLEvent.hx new file mode 100644 index 00000000..e4f107fd --- /dev/null +++ b/source/funkin/backend/scripting/events/CharacterXMLEvent.hx @@ -0,0 +1,16 @@ +package funkin.backend.scripting.events; + +import funkin.game.Character; +import haxe.xml.Access; + +final class CharacterXMLEvent extends CancellableEvent { + /** + * The character instance + */ + public var character:Character; + + /** + * The xml + */ + public var xml:Access; +} \ No newline at end of file diff --git a/source/funkin/backend/scripting/events/NoteHitEvent.hx b/source/funkin/backend/scripting/events/NoteHitEvent.hx index 7a20fed0..a4595fd1 100644 --- a/source/funkin/backend/scripting/events/NoteHitEvent.hx +++ b/source/funkin/backend/scripting/events/NoteHitEvent.hx @@ -24,9 +24,19 @@ final class NoteHitEvent extends CancellableEvent { */ public var countScore:Bool = true; /** - * Whenever ratings should be shown or not. + * Whenever ratings (Rating sprite, Digits sprites and Combo sprite) should be shown or not. + * + * NOTE: Whether it's `true` use `displayRating` and `displayCombo` (plus `minDigitDisplay` in the PlayState class) to change what's going to pop up! */ public var showRating:Null = null; + /** + * Whenever the Rating sprites should be shown or not. + */ + public var displayRating:Bool; + /** + * Whenever the Combo sprite should be shown or not (like old Week 7 patches). + */ + public var displayCombo:Bool; /** * Note that has been pressed */ @@ -123,6 +133,13 @@ final class NoteHitEvent extends CancellableEvent { @:dox(hide) public function cancelDeletion() {preventDeletion();} + /** + * Forces the note to be deleted. + **/ + public function forceDeletion() { + deleteNote = true; + } + /** * Prevents the vocals volume from being set to 1 after pressing the note. */ diff --git a/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx b/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx new file mode 100644 index 00000000..7a5e3eac --- /dev/null +++ b/source/funkin/backend/scripting/events/dialogue/DialogueNextLineEvent.hx @@ -0,0 +1,14 @@ +package funkin.backend.scripting.events.dialogue; + +/** + * CANCEL this event to prevent continuing with the next dialogue. + * + * **NOTE**: To access the current, past or next lines, use `curLine`, `lastLine` or also `dialogueLines` in the `DialogueCutscene` class (they're globals and can be accessed at any time as long as the Dialogue Cutscene is active)! + */ +final class DialogueNextLineEvent extends CancellableEvent +{ + /** + * If the dialogue has just been opened. + */ + public var playFirst:Bool = false; +} \ No newline at end of file diff --git a/source/funkin/backend/shaders/CustomShader.hx b/source/funkin/backend/shaders/CustomShader.hx index 7ea07c7e..9be4300f 100644 --- a/source/funkin/backend/shaders/CustomShader.hx +++ b/source/funkin/backend/shaders/CustomShader.hx @@ -27,6 +27,9 @@ class CustomShader extends FunkinShader { var fragCode = Assets.exists(fragShaderPath) ? Assets.getText(fragShaderPath) : null; var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null; + fragFileName = fragShaderPath; + vertFileName = vertShaderPath; + path = fragShaderPath+vertShaderPath; if (fragCode == null && vertCode == null) diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx index ef91d128..cbfe8846 100644 --- a/source/funkin/backend/shaders/FunkinShader.hx +++ b/source/funkin/backend/shaders/FunkinShader.hx @@ -6,6 +6,9 @@ import flixel.graphics.tile.FlxGraphicsShader; import openfl.display3D.Program3D; import flixel.system.FlxAssets.FlxShader; +import openfl.display3D._internal.GLProgram; +import openfl.display3D._internal.GLShader; +import openfl.utils._internal.Log; import openfl.display.BitmapData; import openfl.display.ShaderParameter; import openfl.display.ShaderParameterType; @@ -26,6 +29,8 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { private static var __instanceFields = Type.getInstanceFields(FunkinShader); public var glslVer:String = "120"; + public var fragFileName:String; + public var vertFileName:String; /** * Creates a new shader from the specified fragment and vertex source. @@ -44,10 +49,135 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { super(); } + var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g; + var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g; + var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g; + @:noCompletion private override function __createGLShader(source:String, type:Int):GLShader + { + var gl = __context.gl; + + var shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + var shaderInfoLog = gl.getShaderInfoLog(shader); + var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != ""; + var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + + if (hasInfoLog || compileStatus == 0) + { + var isVertexShader = type == gl.VERTEX_SHADER; + var messageBuf = new StringBuf(); + messageBuf.add((compileStatus == 0) ? "Error" : "Info"); + if(isVertexShader) { + messageBuf.add(" compiling vertex shader"); + if(vertFileName != null && vertFileName.length > 0) { + messageBuf.add(" (" + vertFileName + ")"); + } + } else { + messageBuf.add(" compiling fragment shader"); + if(fragFileName != null && fragFileName.length > 0) { + messageBuf.add(" (" + fragFileName + ")"); + } + } + messageBuf.add("\n"); + var errorPositions = []; + var regex = null; + var tmp = shaderInfoLog; + if(shaderInfoLog.contains(" : error ")) { + regex = ERROR_REGEX_2; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4))); + tmp = regex.matchedRight(); + } + } else if(shaderInfoLog.contains("ERROR: ")) { + regex = ERROR_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } else { + regex = ERROR_POS_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } + var splitSource = source.split("\n"); + for(error in errorPositions) { + messageBuf.add("ERROR: Line: " + error.line); + if(error.column > 0) { + messageBuf.add(", Column: " + error.column); + } + messageBuf.add(", " + error.message); + if(error.line < splitSource.length) { + messageBuf.add("\nLine: "); + messageBuf.add(splitSource[error.line-1].trim()); + } + messageBuf.add("\n\n"); + } + var hasErrorPosition = errorPositions.length > 0; + if(hasErrorPosition) { + messageBuf.add("Raw shader info log:\n"); + } + messageBuf.add(shaderInfoLog); + messageBuf.add("\n"); + messageBuf.add(source); + + var message = messageBuf.toString(); + if (compileStatus == 0) Log.error(message); + else if (hasInfoLog) Log.debug(message); + } + + return shader; + } + + @:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram + { + var gl = __context.gl; + + var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER); + var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER); + + var program = gl.createProgram(); + + // Fix support for drivers that don't draw if attribute 0 is disabled + for (param in __paramFloat) + { + if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_")) + { + gl.bindAttribLocation(program, 0, param.name); + break; + } + } + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0) + { + var messageBuf = new StringBuf(); + messageBuf.add("Unable to initialize the shader program"); + messageBuf.add("\n"); + messageBuf.add(gl.getProgramInfoLog(program)); + var message = messageBuf.toString(); + Log.error(message); + } + + return program; + } + + var glRawFragmentSource:String; + var glRawVertexSource:String; + @:noCompletion override private function set_glFragmentSource(value:String):String { if(value == null) value = ShaderTemplates.defaultFragmentSource; + glRawFragmentSource = value; value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody); if (value != __glFragmentSource) { @@ -61,6 +191,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { if(value == null) value = ShaderTemplates.defaultVertexSource; + glRawVertexSource = value; value = value.replace("#pragma header", ShaderTemplates.vertHeader).replace("#pragma body", ShaderTemplates.vertBody); if (value != __glVertexSource) { @@ -89,20 +220,24 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { if (__context != null && program == null) { - var prefix = '#version ${glslVer}\n'; + var prefixBuf = new StringBuf(); + prefixBuf.add('#version ${glslVer}\n'); var gl = __context.gl; - prefix += "#ifdef GL_ES - " - + (precisionHint == FULL ? "#ifdef GL_FRAGMENT_PRECISION_HIGH - precision highp float; - #else - precision mediump float; - #endif" : "precision lowp float;") - + " - #endif - "; + prefixBuf.add("#ifdef GL_ES\n"); + if (precisionHint == FULL) { + prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n"); + prefixBuf.add("precision highp float;\n"); + prefixBuf.add("#else\n"); + prefixBuf.add("precision mediump float;\n"); + prefixBuf.add("#endif\n"); + } else { + prefixBuf.add("precision lowp float;\n"); + } + prefixBuf.add("#endif\n"); + + var prefix = prefixBuf.toString(); var vertex = prefix + glVertexSource; var fragment = prefix + glFragmentSource; @@ -116,9 +251,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { else { program = __context.createProgram(GLSL); - program.__glProgram = __createGLProgram(vertex, fragment); - __context.__programs.set(id, program); } @@ -126,56 +259,35 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { glProgram = program.__glProgram; - for (input in __inputBitmapData) - { + for (input in __inputBitmapData) { - if (input.__isUniform) - { + if (input.__isUniform) { input.index = gl.getUniformLocation(glProgram, input.name); - } - else - { + } else { input.index = gl.getAttribLocation(glProgram, input.name); } } - for (parameter in __paramBool) - { - - if (parameter.__isUniform) - { + for (parameter in __paramBool) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramFloat) - { - - if (parameter.__isUniform) - { + for (parameter in __paramFloat) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramInt) - { - - if (parameter.__isUniform) - { - + for (parameter in __paramInt) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { - + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } @@ -213,7 +325,6 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { var input = new ShaderInput(); input.name = name; - @:privateAccess input.__isUniform = isUniform; __inputBitmapData.push(input); @@ -280,15 +391,10 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { case BOOL, BOOL2, BOOL3, BOOL4: var parameter = new ShaderParameter(); parameter.name = name; - @:privateAccess parameter.type = parameterType; - @:privateAccess parameter.__arrayLength = arrayLength; - @:privateAccess parameter.__isBool = true; - @:privateAccess parameter.__isUniform = isUniform; - @:privateAccess parameter.__length = length; __paramBool.push(parameter); @@ -303,17 +409,11 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { case INT, INT2, INT3, INT4: var parameter = new ShaderParameter(); parameter.name = name; - @:privateAccess parameter.type = parameterType; - @:privateAccess parameter.__arrayLength = arrayLength; - @:privateAccess parameter.__isInt = true; - @:privateAccess parameter.__isUniform = isUniform; - @:privateAccess parameter.__length = length; - @:privateAccess __paramInt.push(parameter); Reflect.setField(__data, name, parameter); try{Reflect.setField(this, name, parameter);} catch(e) {} @@ -321,19 +421,13 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { default: var parameter = new ShaderParameter(); parameter.name = name; - @:privateAccess parameter.type = parameterType; - @:privateAccess parameter.__arrayLength = arrayLength; #if lime - @:privateAccess if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength); #end - @:privateAccess parameter.__isFloat = true; - @:privateAccess parameter.__isUniform = isUniform; - @:privateAccess parameter.__length = length; __paramFloat.push(parameter); @@ -399,20 +493,16 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { var cl = Type.getClassName(Type.getClass(field)); // cant do "field is ShaderInput" for some reason if (cl.startsWith("openfl.display.ShaderParameter")) { - @:privateAccess if (field.__length <= 1) { // that means we wait for a single number, instead of an array - @:privateAccess if (field.__isInt && !(val is Int)) { throw new ShaderTypeException(name, Type.getClass(val), 'Int'); return null; } else - @:privateAccess if (field.__isBool && !(val is Bool)) { throw new ShaderTypeException(name, Type.getClass(val), 'Bool'); return null; } else - @:privateAccess if (field.__isFloat && !(val is Float)) { throw new ShaderTypeException(name, Type.getClass(val), 'Float'); return null; @@ -452,41 +542,33 @@ uniform sampler2D bitmap; uniform bool hasTransform; uniform bool hasColorTransform; -vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) -{ - vec4 color = texture2D(bitmap, coord); - if (!hasTransform) - { +vec4 applyFlixelEffects(vec4 color) { + if(!hasTransform) { return color; } - if (color.a == 0.0) - { + if(color.a == 0.0) { return vec4(0.0, 0.0, 0.0, 0.0); } - if (!hasColorTransform) - { + if(!hasColorTransform) { return color * openfl_Alphav; } - color = vec4(color.rgb / color.a, color.a); + color.rgb = color.rgb / color.a; + color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0); - mat4 colorMultiplier = mat4(0); - colorMultiplier[0][0] = openfl_ColorMultiplierv.x; - colorMultiplier[1][1] = openfl_ColorMultiplierv.y; - colorMultiplier[2][2] = openfl_ColorMultiplierv.z; - colorMultiplier[3][3] = openfl_ColorMultiplierv.w; - - color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); - - if (color.a > 0.0) - { + if(color.a > 0.0) { return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); } return vec4(0.0, 0.0, 0.0, 0.0); } +vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) { + vec4 color = texture2D(bitmap, coord); + return applyFlixelEffects(color); +} + uniform vec4 _camSize; float map(float value, float min1, float max1, float min2, float max2) { @@ -505,39 +587,7 @@ vec4 textureCam(sampler2D bitmap, vec2 pos) { return flixel_texture2D(bitmap, camToOg(pos)); }"; - public static final fragBody:String = "vec4 color = texture2D (bitmap, openfl_TextureCoordv); - -if (color.a == 0.0) { - - gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0); - -} else if (openfl_HasColorTransform) { - - color = vec4 (color.rgb / color.a, color.a); - - mat4 colorMultiplier = mat4 (0); - colorMultiplier[0][0] = openfl_ColorMultiplierv.x; - colorMultiplier[1][1] = openfl_ColorMultiplierv.y; - colorMultiplier[2][2] = openfl_ColorMultiplierv.z; - colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w; - - color = clamp (openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); - - if (color.a > 0.0) { - - gl_FragColor = vec4 (color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); - - } else { - - gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0); - - } - -} else { - - gl_FragColor = color * openfl_Alphav; - -}"; + public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);"; public static final vertHeader:String = "attribute float openfl_Alpha; attribute vec4 openfl_ColorMultiplier; attribute vec4 openfl_ColorOffset; @@ -551,16 +601,26 @@ varying vec2 openfl_TextureCoordv; uniform mat4 openfl_Matrix; uniform bool openfl_HasColorTransform; -uniform vec2 openfl_TextureSize;"; +uniform vec2 openfl_TextureSize; + +attribute float alpha; +attribute vec4 colorMultiplier; +attribute vec4 colorOffset; +uniform bool hasColorTransform;"; public static final vertBody:String = "openfl_Alphav = openfl_Alpha; openfl_TextureCoordv = openfl_TextureCoord; -if (openfl_HasColorTransform) { - +if(openfl_HasColorTransform) { openfl_ColorMultiplierv = openfl_ColorMultiplier; openfl_ColorOffsetv = openfl_ColorOffset / 255.0; +} +openfl_Alphav = openfl_Alpha * alpha; + +if(hasColorTransform) { + openfl_ColorOffsetv = colorOffset / 255.0; + openfl_ColorMultiplierv = colorMultiplier; } gl_Position = openfl_Matrix * openfl_Position;"; @@ -568,31 +628,14 @@ gl_Position = openfl_Matrix * openfl_Position;"; public static final defaultVertexSource:String = "#pragma header -attribute float alpha; -attribute vec4 colorMultiplier; -attribute vec4 colorOffset; -uniform bool hasColorTransform; - -void main(void) -{ +void main(void) { #pragma body - - openfl_Alphav = openfl_Alpha * alpha; - - if (hasColorTransform) - { - openfl_ColorOffsetv = colorOffset / 255.0; - openfl_ColorMultiplierv = colorMultiplier; - } }"; - - // TODO: camera stuff public static final defaultFragmentSource:String = "#pragma header -void main(void) -{ - gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv); +void main(void) { + #pragma body }"; } @@ -607,4 +650,16 @@ class ShaderTypeException extends Exception { this.name = name; super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.'); } +} + +class ShaderErrorPosition { + public var column:Int; + public var line:Int; + public var message:String; + + public function new(line:String, column:String, message:String) { + this.line = Std.parseInt(line); + this.column = Std.parseInt(column); + this.message = message; + } } \ No newline at end of file diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx index 73060bba..d599f95d 100644 --- a/source/funkin/backend/system/Logs.hx +++ b/source/funkin/backend/system/Logs.hx @@ -11,8 +11,7 @@ class Logs { public static var nativeTrace = Log.trace; public static function init() { - - Log.trace = function(v : Dynamic, ?infos : Null) { + Log.trace = function(v:Dynamic, ?infos:Null) { var data = [ logText('${infos.fileName}:${infos.lineNumber}: ', CYAN), logText(Std.string(v)) @@ -28,22 +27,48 @@ class Logs { __showInConsole(prepareColoredTrace(data, TRACE)); }; - LogFrontEnd.onLogs = function(Data, Style, FireOnce) { + LogFrontEnd.onLogs = function(data, style, fireOnce) { var prefix = "[FLIXEL]"; var color:ConsoleColor = LIGHTGRAY; var level:Level = INFO; - if (Style == LogStyle.CONSOLE) {prefix = "> "; color = WHITE; level = INFO; } else - if (Style == LogStyle.ERROR) {prefix = "[FLIXEL]"; color = RED; level = ERROR; } else - if (Style == LogStyle.NORMAL) {prefix = "[FLIXEL]"; color = WHITE; level = INFO; } else - if (Style == LogStyle.NOTICE) {prefix = "[FLIXEL]"; color = GREEN; level = VERBOSE;} else - if (Style == LogStyle.WARNING) {prefix = "[FLIXEL]"; color = YELLOW; level = WARNING;} + if (style == LogStyle.CONSOLE) // cant place a switch here as these arent inline values - Nex + { + prefix = "> "; + color = WHITE; + level = INFO; + } + else if (style == LogStyle.ERROR) + { + prefix = "[FLIXEL]"; + color = RED; + level = ERROR; + } + else if (style == LogStyle.NORMAL) + { + prefix = "[FLIXEL]"; + color = WHITE; + level = INFO; + } + else if (style == LogStyle.NOTICE) + { + prefix = "[FLIXEL]"; + color = GREEN; + level = VERBOSE; + } + else if (style == LogStyle.WARNING) + { + prefix = "[FLIXEL]"; + color = YELLOW; + level = WARNING; + } - var d:Dynamic = Data; + var d:Dynamic = data; if (!(d is Array)) d = [d]; var a:Array = d; var strs = [for(e in a) Std.string(e)]; - for(e in strs) { + for(e in strs) + { Logs.trace('$prefix $e', level, color); } }; @@ -55,12 +80,18 @@ class Logs { logText('[ '), logText('${Std.string(time.getHours()).addZeros(2)}:${Std.string(time.getMinutes()).addZeros(2)}:${Std.string(time.getSeconds()).addZeros(2)}', DARKMAGENTA), logText(' |'), - switch(level) { - case WARNING: logText(' WARNING ', DARKYELLOW); - case ERROR: logText(' ERROR ', DARKRED); - case TRACE: logText(' TRACE ', GRAY); - case VERBOSE: logText(' VERBOSE ', DARKMAGENTA); - default: logText(' INFORMATION ', CYAN); + switch (level) + { + case WARNING: + logText(' WARNING ', DARKYELLOW); + case ERROR: + logText(' ERROR ', DARKRED); + case TRACE: + logText(' TRACE ', GRAY); + case VERBOSE: + logText(' VERBOSE ', DARKMAGENTA); + default: + logText(' INFORMATION ', CYAN); }, logText('] ') ]; @@ -77,7 +108,7 @@ class Logs { } public static function __showInConsole(text:Array) { - #if sys + #if (sys && !mobile) while(__showing) { Sys.sleep(0.05); } @@ -89,20 +120,30 @@ class Logs { NativeAPI.setConsoleColors(); Sys.print("\r\n"); __showing = false; + #elseif mobile + while(__showing) { + Sys.sleep(0.05); + } + __showing = true; + @:privateAccess + Sys.print([for(t in text) t.text].join("")); + __showing = false; #else @:privateAccess nativeTrace([for(t in text) t.text].join("")); #end } - public static function traceColored(text:Array, level:Level = INFO) { + public static function traceColored(text:Array, level:Level = INFO) __showInConsole(prepareColoredTrace(text, level)); - } + public static function trace(text:String, level:Level = INFO, color:ConsoleColor = LIGHTGRAY) { - traceColored([{ - text: text, - color: color - }], level); + traceColored([ + { + text: text, + color: color + } + ], level); } } @@ -113,7 +154,8 @@ enum abstract Level(Int) { var TRACE = 3; var VERBOSE = 4; } + typedef LogText = { var text:String; var color:ConsoleColor; -} \ No newline at end of file +} diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx index cad2266e..6705b93c 100644 --- a/source/funkin/backend/system/Main.hx +++ b/source/funkin/backend/system/Main.hx @@ -166,6 +166,8 @@ class Main extends Sprite } public static function refreshAssets() { + WindowUtils.resetTitle(); + FlxSoundTray.volumeChangeSFX = Paths.sound('menu/volume'); FlxSoundTray.volumeUpChangeSFX = null; FlxSoundTray.volumeDownChangeSFX = null; diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx index 259b7f8f..297fcde7 100644 --- a/source/funkin/backend/system/framerate/FlixelInfo.hx +++ b/source/funkin/backend/system/framerate/FlixelInfo.hx @@ -1,5 +1,7 @@ package funkin.backend.system.framerate; +import funkin.backend.scripting.ModState; + class FlixelInfo extends FramerateCategory { public function new() { super("Flixel Info"); @@ -14,7 +16,12 @@ class FlixelInfo extends FramerateCategory { for(_ in FlxG.bitmap._cache.keys()) c++; - _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + if((FlxG.state is ModState)) { + var state:ModState = cast FlxG.state; + _text = "Mod State: " + state.scriptName; + } else { + _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + } _text += '\nObject Count: ${FlxG.state.members.length}'; _text += '\nCamera Count: ${FlxG.cameras.list.length}'; _text += '\nBitmaps Count: ${c}'; @@ -22,9 +29,9 @@ class FlixelInfo extends FramerateCategory { _text += '\nFlxG.game Childs Count: ${FlxG.game.numChildren}'; // _text += '\nCached objects count: ${cachedObjects}'; #if FLX_POINT_POOL - var points = flixel.math.FlxPoint.FlxBasePoint.pool; + //var points = flixel.math.FlxPoint.FlxBasePoint.pool; //_text += '\nPoint Count: ${points._count} | +${points.made} | -${points.gotten} | ${points.balance} | >${points.putted}'; - _text += '\nPoint Count: ${points._count}'; + //_text += '\nPoint Count: ${points._count}'; #end } diff --git a/source/funkin/backend/system/github/GitHub.hx b/source/funkin/backend/system/github/GitHub.hx index 27ba825b..6a100f18 100644 --- a/source/funkin/backend/system/github/GitHub.hx +++ b/source/funkin/backend/system/github/GitHub.hx @@ -2,8 +2,8 @@ package funkin.backend.system.github; #if GITHUB_API import haxe.Json; -import haxe.Exception; #end +import haxe.Exception; // TODO: Document further and perhaps make this a Haxelib. class GitHub { diff --git a/source/funkin/backend/system/macros/HashLinkFixer.hx b/source/funkin/backend/system/macros/HashLinkFixer.hx index 046929e8..697d3c01 100644 --- a/source/funkin/backend/system/macros/HashLinkFixer.hx +++ b/source/funkin/backend/system/macros/HashLinkFixer.hx @@ -42,26 +42,12 @@ class HashLinkFixer { var cl = clRef.get(); if (cl.isAbstract || cl.isExtern || cl.isInterface) return fields; - if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC") && !cl.name.endsWith("_HLFHelper")) { - var metas = cl.meta.get(); - + if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC")) { if(cl.params.length > 0) return fields; - if(cl.module == "EReg") return fields; // private typedef in same class - if(cl.module == "hl.Format") return fields; // enum in same class - var definedFields = []; - var helperClass = macro class { - - }; - - helperClass.pos = cl.pos; - - var module = cl.module + "_HLFHelper"; - var hcClassName = cl.name + "_HLFHelper"; - for(f in fields.copy()) { if (f == null) continue; @@ -70,10 +56,12 @@ class HashLinkFixer { if(definedFields.contains(f.name)) continue; // no duplicate fields + var hlNativeMeta = null; var hasHlNative = false; for(m in f.meta) if (m.name == ":hlNative") { hasHlNative = true; + hlNativeMeta = m; break; } @@ -92,16 +80,39 @@ class HashLinkFixer { var overrideExpr:Expr; var returns:Bool = !fun.ret.match(TPath({name: "Void"})); + var printer = new haxe.macro.Printer(); + if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + var name = 'hlf_${f.name}'; var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}]; - var funcExpr:Expr = returns ? { - //macro return $i{name}($a{arguments}); - macro return @:privateAccess $i{hcClassName}.$name($a{arguments}); - } : { - macro @:privateAccess $i{hcClassName}.$name($a{arguments}); - }; + var funcExpr:Expr = macro @:privateAccess $i{name}($a{arguments}); + if(returns) funcExpr = macro return $funcExpr; + + var cleanMeta = f.meta.copy().filter(function(m) return m.name != ":hlNative"); + var hasBareMeta = hlNativeMeta.params.length == 0; + + var meta = f.meta.copy(); + switch hlNativeMeta { + case {params: []}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CString(name))]}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CFloat(version))]}: + var curVersion = Context.definedValue("hl_ver"); + if(curVersion == null) curVersion = ""; + if(version > curVersion) { + meta = cleanMeta; + if(f.meta.filter((m) -> m.name == ":noExpr").length > 0) + Context.error("Missing function body", f.pos); + funcExpr = fun.expr; // restore to default behaviour + } else { + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + } + default: + } var fiel:Field = { name: name, @@ -109,50 +120,32 @@ class HashLinkFixer { kind: FFun({ ret: fun.ret, params: fun.params.copy(), - expr: fun.expr, + expr: funcExpr, args: fun.args.copy() }), - access: f.access.copy(), - meta: f.meta.copy() + access: f.access.copy().filter(function(a) return a != APublic && a != APrivate).concat([APrivate]), + meta: meta }; - helperClass.fields.push(fiel); + fields.push(fiel); definedFields.push(f.name); + // Remove meta from original function for(m in f.meta.copy()) if (m.name == ":hlNative") { f.meta.remove(m); } - fun.expr = funcExpr; default: } } - helperClass.pack = cl.pack.copy(); - helperClass.pos = cl.pos; - helperClass.name = hcClassName; - - if(definedFields.length > 0) { + /*if(definedFields.length > 0) { trace(cl.module); - /*for(m in metas.copy()) { - trace(" " + m.name); - if(m.name == ":coreApi") { - metas.remove(m); - } - }*/ - - var imports = Context.getLocalImports().copy(); - Context.defineModule(module, [helperClass], imports); - - Context.getLocalImports().push({ - path: [for(m in module.split(".")) { - name: m, - pos: Context.currentPos() - }], - mode: INormal - }); - } + var printer = new haxe.macro.Printer(); + for(field in fields) if(field.name.startsWith("hlf_")) + Sys.println(printer.printField(field)); + }*/ } return fields; diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx index db8360c1..5674660b 100644 --- a/source/funkin/backend/system/modules/AudioSwitchFix.hx +++ b/source/funkin/backend/system/modules/AudioSwitchFix.hx @@ -62,6 +62,7 @@ class AudioSwitchFix { } } +@:dox(hide) typedef PlayingSound = { var sound:FlxSound; var time:Float; diff --git a/source/funkin/backend/system/updating/AsyncUpdater.hx b/source/funkin/backend/system/updating/AsyncUpdater.hx index af7cfff5..e742e92d 100644 --- a/source/funkin/backend/system/updating/AsyncUpdater.hx +++ b/source/funkin/backend/system/updating/AsyncUpdater.hx @@ -101,7 +101,7 @@ class AsyncUpdater { progress.files = files.length; progress.step = DOWNLOADING_ASSETS; trace('starting assets download'); - doFile([for(e in files) e], [for(e in fileNames) e], function() { + doFile(files.copy(), fileNames.copy(), function() { progress.curFile = -1; progress.curFileName = null; progress.files = 1; diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index aa659968..5cf3695c 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -706,7 +706,10 @@ class CoolUtil */ @:noUsing public static inline function openURL(url:String) { #if linux - Sys.command('/usr/bin/xdg-open', [url]); + // generally `xdg-open` should work in every distro + var cmd = Sys.command("xdg-open", [url]); + // run old command JUST IN CASE it fails, which it shouldn't + if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [url]); #else FlxG.openURL(url); #end diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index a4c63f4f..c453a234 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -165,6 +165,7 @@ class DiscordUtil #end } + #if cpp @:noCompletion public static function fixString(str:String) { return new cpp.ConstCharStar(cast(str, String)); @@ -174,6 +175,7 @@ class DiscordUtil { return cast(str, String); } + #end public static function changePresenceAdvanced(data:DPresence) { @@ -469,7 +471,7 @@ typedef DPresence = var ?matchSecret:String; /* max 128 bytes */ var ?joinSecret:String; /* max 128 bytes */ var ?spectateSecret:String; /* max 128 bytes */ - var ?instance:OneOfTwo; + var ?instance:#if cpp OneOfTwo #else Int #end; var ?button1Label:String; /* max 32 bytes */ var ?button1Url:String; /* max 512 bytes */ var ?button2Label:String; /* max 32 bytes */ diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx index 6905d6b2..3facba15 100644 --- a/source/funkin/backend/utils/MemoryUtil.hx +++ b/source/funkin/backend/utils/MemoryUtil.hx @@ -130,14 +130,16 @@ class MemoryUtil { reg.match(process.stdout.readAll().toString()); if (process.exitCode() == 0) return reg.matched(1); #elseif linux - var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]); + /*var process = new HiddenProcess("sudo", ["dmidecode", "--type", "17"]); if (process.exitCode() != 0) return "Unknown"; var lines = process.stdout.readAll().toString().split("\n"); for (line in lines) { if (line.indexOf("Type:") == 0) { return line.substring("Type:".length).trim(); } - } + }*/ + // TODO: sort of unsafe? also requires users to use `sudo` + // when launching the engine through the CLI, REIMPLEMENT LATER. #end return "Unknown"; } diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index dc39244d..afb8ae04 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -6,6 +6,9 @@ import funkin.backend.system.ErrorCode; import funkin.backend.FunkinSprite.XMLAnimType; import flixel.util.FlxColor; import haxe.xml.Access; +import funkin.backend.scripting.Script; +import funkin.backend.scripting.DummyScript; +import funkin.backend.scripting.ScriptPack; import flixel.util.typeLimit.OneOfTwo; import funkin.backend.system.interfaces.IOffsetCompatible; @@ -326,6 +329,51 @@ class XMLUtil { } } +class XMLImportedScriptInfo { + public var path:String; + public var shortLived:Bool = false; + public var loadBefore:Bool = true; + public var importStageSprites:Bool = false; // maybe will change later?? - Nex + public var parentScriptPack:ScriptPack = null; + + public function new(path:String, parentScriptPack:ScriptPack) { + this.parentScriptPack = parentScriptPack; + this.path = path; + } + + public function getScript():Script + return parentScriptPack == null ? null : parentScriptPack.getByPath(path); + + public static function prepareInfos(node:Access, parentScriptPack:ScriptPack, ?onScriptPreLoad:XMLImportedScriptInfo->Void):XMLImportedScriptInfo { + if (!node.has.script || parentScriptPack == null) return null; + + var folder = node.getAtt("folder").getDefault("data/scripts/"); + if (!folder.endsWith("/")) folder += "/"; + + var path = Paths.script(folder + node.getAtt("script")); + var daScript = Script.create(path); + if (daScript is DummyScript) { + Logs.trace('Script Extension at ${path} does not exist.', ERROR); + return null; + } + + var infos = new XMLImportedScriptInfo(daScript.path, parentScriptPack); + infos.shortLived = node.getAtt("isShortLived") == "true" || node.getAtt("shortLived") == "true"; + infos.importStageSprites = node.getAtt("importStageSprites") == "true"; + @:privateAccess infos.loadBefore = shouldLoadBefore(node); + + if (onScriptPreLoad != null) onScriptPreLoad(infos); + parentScriptPack.add(daScript); + daScript.set("scriptInfo", infos); + daScript.load(); + + return infos; + } + + @:dox(hide) public static inline function shouldLoadBefore(node:Access):Bool + return node.getAtt("loadBefore") != "false"; +} + typedef AnimData = { var name:String; var anim:String; diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 2ff39aa0..99f6f690 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1657,7 +1657,7 @@ class Charter extends UIState { changeNoteSustain(-1); function _note_selectall(_) { - selection = [for (note in notesGroup.members) note]; + selection = cast notesGroup.members.copy(); } function _note_selectmeasure(_) { diff --git a/source/funkin/editors/charter/CharterBackdropGroup.hx b/source/funkin/editors/charter/CharterBackdropGroup.hx index e9240ad1..21eeb90f 100644 --- a/source/funkin/editors/charter/CharterBackdropGroup.hx +++ b/source/funkin/editors/charter/CharterBackdropGroup.hx @@ -253,7 +253,7 @@ class CharterBackdropDummy extends UISprite { public override function draw() { @:privateAccess - __lastDrawCameras = [for(c in cameras) c]; + __lastDrawCameras = cameras.copy(); } } diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx index a16cd722..0eb0fc81 100644 --- a/source/funkin/editors/charter/CharterEvent.hx +++ b/source/funkin/editors/charter/CharterEvent.hx @@ -2,6 +2,7 @@ package funkin.editors.charter; import flixel.group.FlxSpriteGroup; import flixel.math.FlxPoint; +import flixel.system.FlxAssets.FlxGraphicAsset; import funkin.backend.chart.ChartData.ChartEvent; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; @@ -106,18 +107,35 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { private static function generateDefaultIcon(name:String) { var isBase64:Bool = false; var path:String = Paths.image('editors/charter/event-icons/$name'); + var defaultPath = Paths.image('editors/charter/event-icons/Unknown'); if(!Assets.exists(path)) { - path = Paths.image('editors/charter/event-icons/Unknown'); + path = defaultPath; var packData = getPackData(name); - if(packData != null && packData[3] != null) { - isBase64 = true; - path = packData[3]; + if(packData != null) { + var packImg = packData[3]; + if(packImg != null && packImg.length > 0) { + isBase64 = !packImg.startsWith("assets/"); + path = packImg; + } } } + path = path.trim(); - var spr = new FlxSprite().loadGraphic(isBase64 ? BitmapData.fromBase64(path.trim(), 'UTF8') : path); - return spr; + var graphic:FlxGraphicAsset = try { + isBase64 ? openfl.display.BitmapData.fromBase64(path, 'UTF8') : path; + } catch(e:Dynamic) { + Logs.trace('Failed to load event icon: ${e.toString()}', ERROR); + isBase64 = false; + defaultPath; + } + + if(!isBase64) { + if (!Assets.exists(graphic)) + graphic = defaultPath; + } + + return new FlxSprite().loadGraphic(graphic); } /** diff --git a/source/funkin/editors/ui/UISprite.hx b/source/funkin/editors/ui/UISprite.hx index 5a41cec3..1246e6de 100644 --- a/source/funkin/editors/ui/UISprite.hx +++ b/source/funkin/editors/ui/UISprite.hx @@ -69,7 +69,7 @@ class UISprite extends FlxSprite { public function drawSuper() { super.draw(); - __lastDrawCameras = [for(c in cameras) c]; + __lastDrawCameras = cameras.copy(); } public function drawMembers() { diff --git a/source/funkin/editors/ui/UISubstateWindow.hx b/source/funkin/editors/ui/UISubstateWindow.hx index b17afbba..98d96275 100644 --- a/source/funkin/editors/ui/UISubstateWindow.hx +++ b/source/funkin/editors/ui/UISubstateWindow.hx @@ -1,5 +1,7 @@ package funkin.editors.ui; +import openfl.display.ShaderParameter; +import openfl.display.ShaderInput; import openfl.filters.ShaderFilter; import flixel.tweens.FlxTween; import funkin.backend.shaders.CustomShader; @@ -7,7 +9,17 @@ import funkin.backend.shaders.CustomShader; // TODO: make UIWarningSubstate extend this class UISubstateWindow extends MusicBeatSubstate { var camShaders:Array = []; - var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + var blurShader:CustomShader = { + var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + if(!Options.intensiveBlur) { + var noiseTexture:ShaderInput = _.data.noiseTexture; + noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png"); + noiseTexture.wrap = REPEAT; + var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize; + noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height]; + } + _; + }; var titleSpr:UIText; var messageSpr:UIText; diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx index 2d652f6b..3fd8de5d 100644 --- a/source/funkin/editors/ui/UIWarningSubstate.hx +++ b/source/funkin/editors/ui/UIWarningSubstate.hx @@ -1,5 +1,7 @@ package funkin.editors.ui; +import openfl.display.ShaderParameter; +import openfl.display.ShaderInput; import openfl.filters.ShaderFilter; import flixel.tweens.FlxTween; import flixel.tweens.FlxEase; @@ -7,7 +9,17 @@ import funkin.backend.shaders.CustomShader; class UIWarningSubstate extends MusicBeatSubstate { var camShaders:Array = []; - var blurShader:CustomShader = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + var blurShader:CustomShader = { + var _ = new CustomShader(Options.intensiveBlur ? "engine/editorBlur" : "engine/editorBlurFast"); + if(!Options.intensiveBlur) { + var noiseTexture:ShaderInput = _.data.noiseTexture; + noiseTexture.input = Assets.getBitmapData("assets/shaders/noise256.png"); + noiseTexture.wrap = REPEAT; + var noiseTextureSize:ShaderParameter = _.data.noiseTextureSize; + noiseTextureSize.value = [noiseTexture.input.width, noiseTexture.input.height]; + } + _; + }; var title:String; var message:String; diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index ac13c820..456dcae3 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -8,6 +8,9 @@ import flixel.util.FlxColor; import funkin.backend.FunkinSprite; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; +import funkin.backend.scripting.ScriptPack; +import funkin.backend.scripting.events.CharacterNodeEvent; +import funkin.backend.scripting.events.CharacterXMLEvent; import funkin.backend.scripting.events.DanceEvent; import funkin.backend.scripting.events.DirectionAnimEvent; import funkin.backend.scripting.events.PlayAnimEvent; @@ -44,8 +47,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var cameraOffset:FlxPoint = FlxPoint.get(0, 0); public var globalOffset:FlxPoint = FlxPoint.get(0, 0); - public var script:Script; public var xml:Access; + public var scripts:ScriptPack; + public var xmlImportedScripts:Array = []; + public var script(default, set):Script; + + public function prepareInfos(node:Access) + return XMLImportedScriptInfo.prepareInfos(node, scripts, (infos) -> xmlImportedScripts.push(infos)); public var idleSuffix:String = ""; public var stunned(default, set):Bool = false; @@ -71,16 +79,15 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset script = Script.create(Paths.script(Path.withoutExtension(Paths.xml('characters/$curCharacter')), null, true)); else script = new DummyScript(curCharacter); - script.setParent(this); script.load(); buildCharacter(xml); - script.call("create"); + scripts.call("create"); if (script == null) script = new DummyScript(curCharacter); - script.call("postCreate"); + scripts.call("postCreate"); } @:noCompletion var __swappedLeftRightAnims:Bool = false; @@ -92,9 +99,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset __autoInterval = autoInterval; // character is flipped - if (isPlayer != playerOffsets && switchAnims) + if (isPlayer != playerOffsets && switchAnims) swapLeftRightAnimations(); - + frameOffset.set(getAnimOffset(getAnimName()).x, getAnimOffset(getAnimName()).y); if (isPlayer) flipX = !flipX; __baseFlipped = flipX; @@ -115,7 +122,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset override function update(elapsed:Float) { super.update(elapsed); - script.call("update", [elapsed]); + scripts.call("update", [elapsed]); if (stunned) { __stunnedTime += elapsed; if (__stunnedTime > 5 / 60) @@ -134,7 +141,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if(debugMode) return; var event = EventManager.get(DanceEvent).recycle(danced); - script.call("onDance", [event]); + scripts.call("onDance", [event]); if (event.cancelled) return; if (isDanceLeftDanceRight) @@ -164,14 +171,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset */ public var danceOnBeat:Bool = true; public override function beatHit(curBeat:Int) { - script.call("beatHit", [curBeat]); + scripts.call("beatHit", [curBeat]); if (danceOnBeat && (curBeat + beatOffset) % beatInterval == 0 && !__lockAnimThisFrame) tryDance(); } public override function stepHit(curStep:Int) - script.call("stepHit", [curStep]); + scripts.call("stepHit", [curStep]); @:noCompletion var __reverseDrawProcedure:Bool = false; public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect { @@ -209,14 +216,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, ?Force:Null = null, Reversed:Bool = false, Frame:Int = 0) { var event = EventManager.get(DirectionAnimEvent).recycle(singAnims[direction % singAnims.length] + suffix, direction, suffix, Context, Reversed, Frame, Force); - script.call("onPlaySingAnim", [event]); + scripts.call("onPlaySingAnim", [event]); if (!event.cancelled) playAnim(event.animName, event.force, event.context, event.reversed, event.frame); } public override function playAnim(AnimName:String, ?Force:Bool, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) { var event = EventManager.get(PlayAnimEvent).recycle(AnimName, Force, Reversed, Frame, Context); - script.call("onPlayAnim", [event]); + scripts.call("onPlayAnim", [event]); if (event.cancelled) return; super.playAnim(event.animName, event.force, event.context, event.reverse, event.startingFrame); @@ -231,15 +238,15 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset var event = EventManager.get(PointEvent).recycle( midpoint.x + (isPlayer ? -100 : 150) + globalOffset.x + cameraOffset.x, midpoint.y - 100 + globalOffset.y + cameraOffset.y); - script.call("onGetCamPos", [event]); + scripts.call("onGetCamPos", [event]); midpoint.put(); return new FlxPoint(event.x, event.y); } public override function destroy() { - script.call('destroy'); - script.destroy(); + scripts.call('destroy'); + scripts.destroy(); super.destroy(); cameraOffset.put(); @@ -281,9 +288,18 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } public inline function buildCharacter(xml:Access) { - this.xml = xml; // Modders wassup :D + for(node in xml.elements) + switch(node.name) { + case "use-extension" | "extension" | "ext": + if (!XMLImportedScriptInfo.shouldLoadBefore(node)) continue; + prepareInfos(node); + } + + xml = scripts.event("onCharacterXMLParsed", EventManager.get(CharacterXMLEvent).recycle(this, xml)).xml; + sprite = curCharacter; spriteAnimType = BEAT; + this.xml = xml; // Modders wassup :D if (xml.x.exists("isPlayer")) playerOffsets = (xml.x.get("isPlayer") == "true"); if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x")); @@ -307,9 +323,19 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval")); loadSprite(Paths.image('characters/$sprite')); + for(node in xml.elements) { + switch(node.name) { + case "anim": + XMLUtil.addXMLAnimation(this, node); + case "use-extension" | "extension" | "ext": + if (XMLImportedScriptInfo.shouldLoadBefore(node)) continue; + prepareInfos(node); + default: + // nothing + } - for (anim in xml.nodes.anim) - XMLUtil.addXMLAnimation(this, anim); + scripts.event("onCharacterNodeParsed", EventManager.get(CharacterNodeEvent).recycle(this, node, node.name)); + } for (attribute in xml.x.attributes()) if (!characterProperties.contains(attribute)) @@ -317,11 +343,19 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset fixChar(__switchAnims, !hasInterval); dance(); + + for (info in xmlImportedScripts) if (info.shortLived) { + var script = info.getScript(); + if (script == null) continue; + + scripts.remove(script); + script.destroy(); + } } public static var characterProperties:Array = [ - "x", "y", "sprite", "scale", "antialiasing", - "flipX", "camx", "camy", "isPlayer", "icon", + "x", "y", "sprite", "scale", "antialiasing", + "flipX", "camx", "camy", "isPlayer", "icon", "color", "gameOverChar", "holdTime" ]; public static var characterAnimProperties:Array = [ @@ -331,7 +365,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public inline function buildXML(?animsOrder:Array):Xml { var xml:Xml = Xml.createElement("character"); xml.attributeOrder = characterProperties.copy(); - + if (globalOffset.x != 0) xml.set("x", Std.string(FlxMath.roundDecimal(globalOffset.x, 2))); if (globalOffset.y != 0) xml.set("y", Std.string(FlxMath.roundDecimal(globalOffset.y, 2))); @@ -400,6 +434,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset return stunned = b; } + // ---- Backwards compat ---- // Interval at which the character will dance (higher number = slower dance) @:noCompletion public var danceInterval(get, set):Int; @:noCompletion private function set_danceInterval(v:Int) @@ -407,6 +442,22 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset @:noCompletion private function get_danceInterval() return beatInterval; + private function set_script(script:Script):Script { + if (scripts == null) (scripts = new ScriptPack("Character")).setParent(this); + + var lastIndex = scripts.scripts.indexOf(this.script); + if(lastIndex >= 0) { + if(script == null) // last != null && new == null + scripts.scripts.splice(lastIndex, 1); + else // last != null && new != null + scripts.scripts[lastIndex] = script; + } else if(script != null) // last == null + scripts.insert(0, script); + + return this.script = script; + } + // ---- end of Backwards compat ---- + public static var FALLBACK_CHARACTER:String = "bf"; public static var FALLBACK_DEAD_CHARACTER:String = "bf-dead"; @@ -436,7 +487,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset Logs.trace('Error while loading character ${character}: ${e}', ERROR); character = FALLBACK_CHARACTER; - if (char != null) + if (char != null) char.curCharacter = character; continue; } diff --git a/source/funkin/game/HealthIcon.hx b/source/funkin/game/HealthIcon.hx index d55bf14b..70f5024b 100644 --- a/source/funkin/game/HealthIcon.hx +++ b/source/funkin/game/HealthIcon.hx @@ -85,12 +85,11 @@ class HealthIcon extends FlxSprite if (animation.curAnim != null) { var i:Int = -1; var oldKey:Int = -1; - for(k=>icon in healthSteps) { - if (k > oldKey && k < health * 100) { - oldKey = k; - i = icon; - } + for (k=>icon in healthSteps) if (k > oldKey && k <= health * 100) { + oldKey = k; + i = icon; } + if (i >= 0) animation.curAnim.curFrame = i; } } diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index b8baf48e..9b06fc81 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -98,6 +98,9 @@ class Note extends FlxSprite return __customNoteTypeExists[path]; return __customNoteTypeExists[path] = Assets.exists(path); } + + static var DEFAULT_FIELDS:Array = ["time", "id", "type", "sLen"]; + public function new(strumLine:StrumLine, noteData:ChartNote, sustain:Bool = false, sustainLength:Float = 0, sustainOffset:Float = 0, ?prev:Note) { super(); @@ -115,7 +118,7 @@ class Note extends FlxSprite this.sustainLength = sustainLength; this.strumLine = strumLine; for(field in Reflect.fields(noteData)) { - if(!["time", "id", "type", "sLen"].contains(field)) { + if(!DEFAULT_FIELDS.contains(field)) { this.extra.set(field, Reflect.field(noteData, field)); } } @@ -290,7 +293,7 @@ class Note extends FlxSprite setClipRect(swagRect); } - public function setClipRect(rect:FlxRect) { + public inline function setClipRect(rect:FlxRect) { this.clipRect = rect; } diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 17ed9bc0..27f5c5ba 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -455,6 +455,22 @@ class PlayState extends MusicBeatState * Group containing all of the combo sprites. */ public var comboGroup:RotatingSpriteGroup; + /** + * Whenever the Rating sprites should be shown or not. + * + * NOTE: This is just a default value for the final value, the final value can be changed through notes hit events. + */ + public var defaultDisplayRating:Bool = true; + /** + * Whenever the Combo sprite should be shown or not (like old Week 7 patches). + * + * NOTE: This is just a default value for the final value, the final value can be changed through notes hit events. + */ + public var defaultDisplayCombo:Bool = false; + /** + * Minimum Combo Count to display the combo digits. Anything less than 0 means it won't be shown. + */ + public var minDigitDisplay:Int = 10; /** * Array containing all of the note types names. */ @@ -1646,9 +1662,11 @@ class PlayState extends MusicBeatState var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick")); + event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick"); else - event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false)); + event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false); + event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted + event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event); strumLine.onHit.dispatch(event); scripts.event("onNoteHit", event); @@ -1665,7 +1683,8 @@ class PlayState extends MusicBeatState if (event.showRating || (event.showRating == null && event.player)) { displayCombo(event); - displayRating(event.rating, event); + if (event.displayRating) + displayRating(event.rating, event); ratingNum += 1; } } @@ -1695,7 +1714,7 @@ class PlayState extends MusicBeatState } } - if (event.deleteNote && !note.isSustainNote) strumLine.deleteNote(note); + if (event.deleteNote) strumLine.deleteNote(note); } public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void { @@ -1723,13 +1742,11 @@ class PlayState extends MusicBeatState } public function displayCombo(?evt:NoteHitEvent = null):Void { - var pre:String = evt != null ? evt.ratingPrefix : ""; - var suf:String = evt != null ? evt.ratingSuffix : ""; + if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) { + var pre:String = evt != null ? evt.ratingPrefix : ""; + var suf:String = evt != null ? evt.ratingSuffix : ""; - var separatedScore:String = Std.string(combo).addZeros(3); - - if (combo == 0 || combo >= 10) { - if (combo >= 10) { + if (evt.displayCombo) { var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}')); comboSpr.resetSprite(comboGroup.x, comboGroup.y); comboSpr.acceleration.y = 600; @@ -1751,6 +1768,7 @@ class PlayState extends MusicBeatState }); } + var separatedScore:String = Std.string(combo).addZeros(3); for (i in 0...separatedScore.length) { var numScore:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}')); diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index 7687d897..9b3782ca 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -5,6 +5,7 @@ import funkin.backend.scripting.events.StageNodeEvent; import flixel.math.FlxPoint; import flixel.FlxState; import haxe.xml.Access; +import funkin.backend.utils.XMLUtil.XMLImportedScriptInfo; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; @@ -30,6 +31,9 @@ class Stage extends FlxBasic implements IBeatReceiver { public function setStagesSprites(script:Script) for (k=>e in stageSprites) script.set(k, e); + public function prepareInfos(node:Access) + return XMLImportedScriptInfo.prepareInfos(node, PlayState.instance.scripts, (infos) -> xmlImportedScripts.push(infos)); + public function new(stage:String, ?state:FlxState) { super(); @@ -136,7 +140,7 @@ class Stage extends FlxBasic implements IBeatReceiver { PlayState.instance.add(PlayState.instance.comboGroup); PlayState.instance.comboGroup; case "use-extension" | "extension" | "ext": - if (__shouldLoadBefore(node) || XMLImportedScriptInfo.prepareInfos(node, this) == null) continue; + if (XMLImportedScriptInfo.shouldLoadBefore(node) || prepareInfos(node) == null) continue; null; default: null; } @@ -201,13 +205,10 @@ class Stage extends FlxBasic implements IBeatReceiver { @:dox(hide) private function __pushNcheckNode(array:Array, node:Access) { array.push(node); - if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && __shouldLoadBefore(node)) - XMLImportedScriptInfo.prepareInfos(node, this); + if ((node.name == "use-extension" || node.name == "extension" || node.name == "ext") && XMLImportedScriptInfo.shouldLoadBefore(node)) + prepareInfos(node); } - @:dox(hide) private static inline function __shouldLoadBefore(node:Access):Bool - return node.getAtt("loadBefore") != "false"; - public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos { var charPos = new StageCharPos(); charPos.visible = charPos.active = false; @@ -317,45 +318,4 @@ typedef StageCharPosInfo = { var y:Float; var flip:Bool; var scroll:Float; -} - -class XMLImportedScriptInfo { - public var path:String; - public var shortLived:Bool = false; - public var loadBefore:Bool = true; - public var importStageSprites:Bool = false; - - public function new(path:String) - this.path = path; - - public function getScript():Script - return PlayState.instance == null ? null : PlayState.instance.scripts.getByPath(path); - - public static function prepareInfos(node:Access, ?stage:Stage):XMLImportedScriptInfo { - if (!node.has.script || PlayState.instance == null) return null; - - var folder = node.getAtt("folder").getDefault("data/scripts/"); - if (!folder.endsWith("/")) folder += "/"; - - var path = Paths.script(folder + node.getAtt("script")); - var daScript = Script.create(path); - if (daScript is DummyScript) { - var msg = 'Script Extension at ${path} does not exist'; - if (stage != null) msg += ' through stage "${stage.stageName}"'; - Logs.trace(msg + ".", ERROR); - return null; - } - - var infos = new XMLImportedScriptInfo(daScript.path); - infos.shortLived = node.getAtt("isShortLived") == "true"; - infos.importStageSprites = node.getAtt("importStageSprites") == "true"; - @:privateAccess infos.loadBefore = Stage.__shouldLoadBefore(node); - - if (stage != null) stage.xmlImportedScripts.push(infos); - PlayState.instance.scripts.add(daScript); - daScript.set("scriptInfo", infos); - daScript.load(); - - return infos; - } } \ No newline at end of file diff --git a/source/funkin/game/Strum.hx b/source/funkin/game/Strum.hx index e2b6ccd0..cbce2da4 100644 --- a/source/funkin/game/Strum.hx +++ b/source/funkin/game/Strum.hx @@ -74,7 +74,7 @@ class Strum extends FlxSprite { } public override function draw() { - lastDrawCameras = [for(c in cameras) c]; + lastDrawCameras = cameras.copy(); super.draw(); } diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index 656df6f2..99ba142b 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -3,7 +3,7 @@ package funkin.game.cutscenes; import funkin.backend.utils.FunkinParentDisabler; import funkin.backend.scripting.events.dialogue.*; import funkin.backend.scripting.events.CancellableEvent; -import funkin.backend.scripting.events.DynamicEvent; +import funkin.backend.scripting.events.dialogue.DialogueNextLineEvent; import funkin.backend.scripting.Script; import flixel.sound.FlxSound; import funkin.game.cutscenes.dialogue.*; @@ -89,7 +89,7 @@ class DialogueCutscene extends Cutscene { musicVolume: node.has.musicVolume ? (volume = Std.parseFloat(node.att.speed).getDefault(0.8)) : null, changeMusic: node.has.changeMusic ? FlxG.sound.load(Paths.music(node.att.changeMusic), volume, true) : null, playSound: node.has.playSound ? FlxG.sound.load(Paths.sound(node.att.playSound)) : null, - nextSound: node.has.nextSound ? FlxG.sound.load(Paths.music(node.att.nextSound)) : null, + nextSound: node.has.nextSound ? FlxG.sound.load(Paths.sound(node.att.nextSound)) : null, textSound: null }; @@ -138,8 +138,8 @@ class DialogueCutscene extends Cutscene { public var canProceed:Bool = true; public function next(playFirst:Bool = false) { - var event = EventManager.get(DynamicEvent).recycle(playFirst); - dialogueScript.call("next", [playFirst]); + var event = EventManager.get(DialogueNextLineEvent).recycle(playFirst); + dialogueScript.call("next", [event]); if(event.cancelled || !canProceed) return; if ((curLine = dialogueLines.shift()) == null) { @@ -154,7 +154,7 @@ class DialogueCutscene extends Cutscene { } if (curLine.callback != null) - dialogueScript.call(curLine.callback, [playFirst]); + dialogueScript.call(curLine.callback, [event.playFirst]); for (k=>c in charMap) if (k != curLine.char) @@ -167,8 +167,8 @@ class DialogueCutscene extends Cutscene { dialogueBox.popupChar(char, force); } - var finalSuffix:String = playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : ""; - dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !playFirst); + var finalSuffix:String = event.playFirst && dialogueBox.hasAnimation(curLine.bubble + "-firstOpen") ? "-firstOpen" : dialogueBox.hasAnimation(curLine.bubble + "-open") ? "-open" : ""; + dialogueBox.playBubbleAnim(curLine.bubble, finalSuffix, curLine.text, curLine.format, curLine.speed, curLine.nextSound, curLine.textSound != null ? [curLine.textSound] : null, finalSuffix == "-firstOpen" || finalSuffix == "-open", !event.playFirst); if(curLine.playSound != null) curLine.playSound.play(); if(curLine.changeMusic != null) { @@ -178,7 +178,7 @@ class DialogueCutscene extends Cutscene { curMusic.fadeIn(1, 0, curMusic.volume); } else if(curLine.musicVolume != null && curMusic != null) curMusic.volume = curLine.musicVolume; - dialogueScript.call("postNext", [playFirst]); + dialogueScript.call("postNext", [event]); } public override function close() { @@ -215,4 +215,4 @@ typedef DialogueLine = { var nextSound:FlxSound; var textSound:FlxSound; var changeDefAnim:String; -} \ No newline at end of file +} diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 4d30a262..448552ac 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -162,7 +162,7 @@ class MainMenuState extends MusicBeatState var event = event("onSelectItem", EventManager.get(NameEvent).recycle(daChoice)); if (event.cancelled) return; - switch (daChoice) + switch (event.name) { case 'story mode': FlxG.switchState(new StoryMenuState()); case 'freeplay': FlxG.switchState(new FreeplayState()); diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index fb2326e5..bff3cd4d 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -14,6 +14,7 @@ import funkin.options.keybinds.KeybindsOptions; import funkin.menus.StoryMenuState; import funkin.backend.system.Conductor; import funkin.backend.utils.FunkinParentDisabler; +import funkin.options.TreeMenu; class PauseSubState extends MusicBeatSubstate { @@ -153,6 +154,7 @@ class PauseSubState extends MusicBeatSubstate persistentDraw = false; openSubState(new KeybindsOptions()); case "Change Options": + TreeMenu.lastState = PlayState; FlxG.switchState(new OptionsMenu()); case "Exit to charter": FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false)); @@ -205,4 +207,4 @@ class PauseSubState extends MusicBeatSubstate item.alpha = 0.6; } } -} \ No newline at end of file +} diff --git a/source/funkin/menus/StoryMenuState.hx b/source/funkin/menus/StoryMenuState.hx index 27d700d7..d69d0c5b 100644 --- a/source/funkin/menus/StoryMenuState.hx +++ b/source/funkin/menus/StoryMenuState.hx @@ -114,6 +114,12 @@ class StoryMenuState extends MusicBeatState { } } + // default difficulty should be the middle difficulty in the array + // to be consistent with base game and whatnot, you know the drill + curDifficulty = Math.floor(weeks[0].difficulties.length * 0.5); + // debug stuff lol + Logs.trace('Middle Difficulty for Week 1 is ${weeks[0].difficulties[curDifficulty]} (ID: $curDifficulty)'); + changeWeek(0, true); DiscordUtil.call("onMenuLoaded", ["Story Menu"]); @@ -163,6 +169,7 @@ class StoryMenuState extends MusicBeatState { if (!force) CoolUtil.playMenuSFX(); for(k=>e in weekSprites.members) { e.targetY = k - curWeek; + e.alpha = k == curWeek ? 1.0 : 0.6; } tracklist.text = 'TRACKS\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.name.toUpperCase()].join('\n')}'; weekTitle.text = weeks[curWeek].name.getDefault(""); @@ -333,13 +340,13 @@ class StoryMenuState extends MusicBeatState { if (char.animation.exists("confirm")) char.animation.play("confirm"); - PlayState.loadWeek(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty]); + PlayState.loadWeek(event.week, event.difficulty); new FlxTimer().start(1, function(tmr:FlxTimer) { FlxG.switchState(new PlayState()); }); - weekSprites.members[curWeek].startFlashing(); + weekSprites.members[event.weekID].startFlashing(); } } diff --git a/source/lime/system/System.hx b/source/lime/system/System.hx index 34599615..604bcdce 100644 --- a/source/lime/system/System.hx +++ b/source/lime/system/System.hx @@ -304,7 +304,10 @@ class System #elseif mac Sys.command("/usr/bin/open", [path]); #elseif linux - Sys.command("/usr/bin/xdg-open", [path, "&"]); + // generally `xdg-open` should work in every distro + var cmd = Sys.command("xdg-open", [path, "&"]); + // run old command JUST IN CASE it fails, which it shouldn't + if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [path, "&"]); #elseif (js && html5) Browser.window.open(path, "_blank"); #elseif flash diff --git a/source/openfl/display/GraphicsShader.hx b/source/openfl/display/GraphicsShader.hx new file mode 100644 index 00000000..72c0ed97 --- /dev/null +++ b/source/openfl/display/GraphicsShader.hx @@ -0,0 +1,89 @@ +package openfl.display; + +import openfl.utils.ByteArray; + +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +class GraphicsShader extends Shader +{ + @:glVertexHeader("attribute float openfl_Alpha; +attribute vec4 openfl_ColorMultiplier; +attribute vec4 openfl_ColorOffset; +attribute vec4 openfl_Position; +attribute vec2 openfl_TextureCoord; + +varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform mat4 openfl_Matrix; +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize;") + @:glVertexBody("openfl_Alphav = openfl_Alpha; +openfl_TextureCoordv = openfl_TextureCoord; + +if (openfl_HasColorTransform) { + openfl_ColorMultiplierv = openfl_ColorMultiplier; + openfl_ColorOffsetv = openfl_ColorOffset / 255.0; +} + +gl_Position = openfl_Matrix * openfl_Position;") + @:glVertexSource("#pragma header + +void main(void) { + #pragma body +}") + @:glFragmentHeader("varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize; +uniform sampler2D bitmap;") + @:glFragmentBody("vec4 color = texture2D (bitmap, openfl_TextureCoordv); + +if (color.a == 0.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} else if (openfl_HasColorTransform) { + color = vec4(color.rgb / color.a, color.a); + + mat4 colorMultiplier = mat4(0); + colorMultiplier[0][0] = openfl_ColorMultiplierv.x; + colorMultiplier[1][1] = openfl_ColorMultiplierv.y; + colorMultiplier[2][2] = openfl_ColorMultiplierv.z; + colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w; + + color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); + + if (color.a > 0.0) { + gl_FragColor = vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); + } else { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } +} else { + gl_FragColor = color * openfl_Alphav; +}") + #if emscripten + @:glFragmentSource("#pragma header + +void main(void) { + #pragma body + + gl_FragColor = gl_FragColor.bgra; +}") + #else + @:glFragmentSource("#pragma header + +void main(void) { + #pragma body +}") + #end + public function new(code:ByteArray = null) + { + super(code); + } +}