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);
+ }
+}