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/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/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/commandline/commands/Setup.hx b/commandline/commands/Setup.hx index 4e97e989..f93645e0 100644 --- a/commandline/commands/Setup.hx +++ b/commandline/commands/Setup.hx @@ -18,11 +18,32 @@ class Setup { FileSystem.deleteDirectory(path); } + public static function addMissingFolders(path:String):String { + #if sys + var folders:Array = path.split("/"); + var currentPath:String = ""; + + for (folder in folders) { + currentPath += folder + "/"; + if (!FileSystem.exists(currentPath)) + FileSystem.createDirectory(currentPath); + } + #end + return path; + } + public static function main(args:Array) { - var args = ArgParser.parse(args, ["s" => "silent-progress", "S" => "silent-progress", "silent" => "silent-progress"]); + var args = ArgParser.parse(args, [ + "s" => "silent-progress", + "S" => "silent-progress", + "silent" => "silent-progress", + "f" => "fast", + "F" => "fast" + ]); var CHECK_VSTUDIO = !args.existsOption("no-vscheck"); var REINSTALL_ALL = args.existsOption("reinstall"); var SILENT = args.existsOption("silent-progress"); + var FAST = args.existsOption("fast"); // to prevent messing with currently installed libs if (!FileSystem.exists('.haxelib')) @@ -134,6 +155,11 @@ class Setup { var commandSuffix = " --always"; if (SILENT) commandSuffix += " --quiet"; + function command(cmd:String) { + Sys.println(cmd); // TODO: ansi color + Sys.command(cmd); + } + for(event in events) { switch(event.type) { case INSTALL: @@ -144,10 +170,23 @@ class Setup { switch(lib.type) { case "lib": prettyPrint((lib.global == "true" ? "Globally installing" : "Locally installing") + ' "${lib.name}"...'); - Sys.command('haxelib$commandPrefix install ${lib.name} ${lib.version != null ? " " + lib.version : " "}'); + command('haxelib$commandPrefix install ${lib.name} ${lib.version != null ? " " + lib.version : " "}'); case "git": prettyPrint((lib.global == "true" ? "Globally installing" : "Locally installing") + ' "${lib.name}" from git url "${lib.url}"'); - Sys.command('haxelib$commandPrefix git ${lib.name} ${lib.url}${lib.ref != null ? ' ${lib.ref}' : ''}'); + if(FAST) { + var oldcwd = Sys.getCwd(); + var newCwd = oldcwd + "/.haxelib/" + lib.name + "/"; + var newCwdGit = newCwd + "git/"; + addMissingFolders(newCwdGit); + Sys.setCwd(newCwd); + File.saveContent(".current", "git"); + if(FileSystem.exists(newCwdGit)) recursiveDelete(newCwdGit); + command('git clone ${lib.url} ${lib.name} --depth 1${lib.ref != null ? " --branch " + lib.ref : ""} --progress'); + FileSystem.rename(newCwd + lib.name, newCwdGit); + Sys.setCwd(oldcwd); + } else { + command('haxelib$commandPrefix git ${lib.name} ${lib.url}${lib.ref != null ? ' ${lib.ref}' : ''}'); + } default: prettyPrint('Cannot resolve library of type "${lib.type}"'); } @@ -188,7 +227,7 @@ class Setup { for(line in cmd.lines) { var line = StringTools.replace(line, "$PLATFORM", platform); //Sys.println(line); - Sys.command(line); + command(line); } Sys.setCwd(oldCwd); case PRINT: diff --git a/project.xml b/project.xml index 34683e4b..81ef21ec 100644 --- a/project.xml +++ b/project.xml @@ -131,7 +131,7 @@ - + diff --git a/source/StringTools.hx b/source/StringTools.hx index 0b495af1..2eac4b10 100644 --- a/source/StringTools.hx +++ b/source/StringTools.hx @@ -230,6 +230,8 @@ class StringTools { return (cast s).startsWith(start); #elseif lua return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start); + #elseif cpp + return untyped s.__StartsWith(start); #else return (s.length >= start.length && s.indexOf(start, 0) == 0); #end @@ -255,6 +257,8 @@ class StringTools { return (cast s).endsWith(end); #elseif lua return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end); + #elseif cpp + return untyped s.__EndsWith(end); #else var elen = end.length; var slen = s.length; diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 21bea64b..7cd40806 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -176,7 +176,7 @@ class Paths return FlxAtlasFrames.fromAseprite('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.json'); inline static public function getAssetsRoot():String - return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : './assets'; + return ModsFolder.currentModFolder != null ? '${ModsFolder.modsPath}${ModsFolder.currentModFolder}' : #if (sys && TEST_BUILD) './${Main.pathBack}assets/' #else './assets' #end; /** * Gets frames at specified path. diff --git a/source/funkin/backend/scripting/events/NoteHitEvent.hx b/source/funkin/backend/scripting/events/NoteHitEvent.hx index 4bac078e..d0b58f5a 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 25c4796a..0166e08e 100644 --- a/source/funkin/backend/shaders/CustomShader.hx +++ b/source/funkin/backend/shaders/CustomShader.hx @@ -26,6 +26,9 @@ class CustomShader extends FunkinShader { var vertCode = Assets.exists(vertShaderPath) ? Assets.getText(vertShaderPath) : null; fileName = name; + fragFileName = fragShaderPath; + vertFileName = vertShaderPath; + path = fragShaderPath+vertShaderPath; if (fragCode == null && vertCode == null) diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx index 5ff9656c..b7c1be14 100644 --- a/source/funkin/backend/shaders/FunkinShader.hx +++ b/source/funkin/backend/shaders/FunkinShader.hx @@ -1,17 +1,17 @@ package funkin.backend.shaders; +import flixel.system.FlxAssets.FlxShader; +import flixel.util.FlxSignal.FlxTypedSignal; import haxe.Exception; import hscript.IHScriptCustomBehaviour; -import flixel.util.FlxSignal.FlxTypedSignal; -import flixel.system.FlxAssets.FlxShader; - import lime.utils.Float32Array; - -import openfl.display3D._internal.GLProgram; -import openfl.display.ShaderParameterType; -import openfl.display.ShaderParameter; import openfl.display.BitmapData; import openfl.display.ShaderInput; +import openfl.display.ShaderParameter; +import openfl.display.ShaderParameterType; +import openfl.display3D._internal.GLProgram; +import openfl.display3D._internal.GLShader; +import openfl.utils._internal.Log; using StringTools; @:access(openfl.display3D.Context3D) @@ -20,12 +20,16 @@ using StringTools; @:access(openfl.display.ShaderParameter) class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { private static var __instanceFields = Type.getInstanceFields(FunkinShader); + private static var FRAGMENT_SHADER = 0; + private static var VERTEX_SHADER = 1; public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>(); public var glslVer:String = "120"; public var fileName:String = "FunkinShader"; + public var fragFileName:String = "FunkinShader"; + public var vertFileName:String = "FunkinShader"; public var shaderPrefix:String = ""; public var fragmentPrefix:String = ""; @@ -50,13 +54,14 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { static var IMPORT_REGEX = ~/#import\s+<(.*)>/; - private function processImports(value:String):String + private function processImports(value:String, type:Int):String { while(IMPORT_REGEX.match(value)) { var importPath = IMPORT_REGEX.matched(1); var importSource = Assets.getText("assets/shaders/" + importPath); if(importSource == null) { + var fileName = type == FRAGMENT_SHADER ? fragFileName : vertFileName; Logs.traceColored([ Logs.logText('[Shader] ', RED), Logs.logText('Failed to import shader ${importPath} in ${fileName}', RED), @@ -68,12 +73,150 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { return value; } + static var ERROR_POS_REGEX = ~/(\d+):(\d+): (.*)/g; + static var ERROR_REGEX = ~/ERROR: (\d+):(\d+): (.*)/g; + static var ERROR_REGEX_2 = ~/(\d+)\((\d+)\) : error ([^:]+): (.*)/g; + @:noCompletion private override function __createGLShader(source:String, type:Int):GLShader + { + var gl = __context.gl; + + var shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + var shaderInfoLog = gl.getShaderInfoLog(shader); + var hasInfoLog = shaderInfoLog != null && StringTools.trim(shaderInfoLog) != ""; + var compileStatus = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + + if (hasInfoLog || compileStatus == 0) + { + var isVertexShader = type == gl.VERTEX_SHADER; + var messageBuf = new StringBuf(); + messageBuf.add((compileStatus == 0) ? "Error" : "Info"); + if(isVertexShader) { + messageBuf.add(" compiling vertex shader"); + if(vertFileName != null && vertFileName.length > 0) { + messageBuf.add(" (" + vertFileName + ")"); + } + } else { + messageBuf.add(" compiling fragment shader"); + if(fragFileName != null && fragFileName.length > 0) { + messageBuf.add(" (" + fragFileName + ")"); + } + } + messageBuf.add("\n"); + var errorPositions = []; + var regex = null; + var tmp = shaderInfoLog; + if(shaderInfoLog.contains(" : error ")) { + regex = ERROR_REGEX_2; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(4))); + tmp = regex.matchedRight(); + } + } else if(shaderInfoLog.contains("ERROR: ")) { + regex = ERROR_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } else { + regex = ERROR_POS_REGEX; + + while(regex.match(tmp)) { + errorPositions.push(new ShaderErrorPosition(regex.matched(2), regex.matched(1), regex.matched(3))); + tmp = regex.matchedRight(); + } + } + var splitSource = source.split("\n"); + for(error in errorPositions) { + messageBuf.add("ERROR: Line: " + error.line); + if(error.column > 0) { + messageBuf.add(", Column: " + error.column); + } + messageBuf.add(", " + error.message); + if(error.line < splitSource.length) { + messageBuf.add("\nLine: "); + messageBuf.add(splitSource[error.line-1].trim()); + } + messageBuf.add("\n\n"); + } + var hasErrorPosition = errorPositions.length > 0; + if(hasErrorPosition) { + messageBuf.add("Raw shader info log:\n"); + } + messageBuf.add(shaderInfoLog); + messageBuf.add("\n"); + messageBuf.add(source); + + var message = messageBuf.toString(); + if (compileStatus == 0) Log.error(message); + else if (hasInfoLog) Log.debug(message); + } + + return shader; + } + + @:noCompletion private override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram + { + var program:GLProgram = null; + try + { + var gl = __context.gl; + + var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER); + var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER); + + program = gl.createProgram(); + + // Fix support for drivers that don't draw if attribute 0 is disabled + for (param in __paramFloat) + { + if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_")) + { + gl.bindAttribLocation(program, 0, param.name); + break; + } + } + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0) + { + var messageBuf = new StringBuf(); + messageBuf.add("Unable to initialize the shader program"); + messageBuf.add("\n"); + messageBuf.add(gl.getProgramInfoLog(program)); + var message = messageBuf.toString(); + Log.error(message); + } + } + catch (error:Dynamic) + { + Logs.traceColored([ + Logs.logText('[Shader] ', BLUE), + Logs.logText('Failed to compile shader ${fileName}: ', RED), + Logs.logText(Std.string(error)) + ], TRACE); + } + return program; + + return program; + } + + var glRawFragmentSource:String; + var glRawVertexSource:String; + @:noCompletion override private function set_glFragmentSource(value:String):String { if(value == null) value = ShaderTemplates.defaultFragmentSource; + glRawFragmentSource = value; value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody); - value = processImports(value); + value = processImports(value, FRAGMENT_SHADER); if (value != __glFragmentSource) { __glSourceDirty = true; @@ -86,8 +229,9 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { if(value == null) value = ShaderTemplates.defaultVertexSource; + glRawVertexSource = value; value = value.replace("#pragma header", ShaderTemplates.vertHeader).replace("#pragma body", ShaderTemplates.vertBody); - value = processImports(value); + value = processImports(value, VERTEX_SHADER); if (value != __glVertexSource) { __glSourceDirty = true; @@ -96,24 +240,6 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { return __glVertexSource = value; } - override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram - { - var program:GLProgram = null; - try - { - program = super.__createGLProgram(vertexSource, fragmentSource); - } - catch (error:Dynamic) - { - Logs.traceColored([ - Logs.logText('[Shader] ', BLUE), - Logs.logText('Failed to compile shader ${fileName}: ', RED), - Logs.logText(Std.string(error)) - ], TRACE); - } - return program; - } - override function __updateGL():Void { onGLUpdate.dispatch(); super.__updateGL(); @@ -138,20 +264,25 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { if (__context != null && program == null) { - var prefix = '#version ${glslVer}\n' + shaderPrefix; + var prefixBuf = new StringBuf(); + prefixBuf.add('#version ${glslVer}\n'); + prefixBuf.add(shaderPrefix); var gl = __context.gl; - prefix += "#ifdef GL_ES - " - + (precisionHint == FULL ? "#ifdef GL_FRAGMENT_PRECISION_HIGH - precision highp float; - #else - precision mediump float; - #endif" : "precision lowp float;") - + " - #endif - "; + prefixBuf.add("#ifdef GL_ES\n"); + if (precisionHint == FULL) { + prefixBuf.add("#ifdef GL_FRAGMENT_PRECISION_HIGH\n"); + prefixBuf.add("precision highp float;\n"); + prefixBuf.add("#else\n"); + prefixBuf.add("precision mediump float;\n"); + prefixBuf.add("#endif\n"); + } else { + prefixBuf.add("precision lowp float;\n"); + } + prefixBuf.add("#endif\n"); + + var prefix = prefixBuf.toString(); var vertex = prefix + vertexPrefix + glVertexSource; var fragment = prefix + fragmentPrefix + glFragmentSource; @@ -165,9 +296,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { else { program = __context.createProgram(GLSL); - program.__glProgram = __createGLProgram(vertex, fragment); - __context.__programs.set(id, program); } @@ -175,56 +304,35 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { { glProgram = program.__glProgram; - for (input in __inputBitmapData) - { + for (input in __inputBitmapData) { - if (input.__isUniform) - { + if (input.__isUniform) { input.index = gl.getUniformLocation(glProgram, input.name); - } - else - { + } else { input.index = gl.getAttribLocation(glProgram, input.name); } } - for (parameter in __paramBool) - { - - if (parameter.__isUniform) - { + for (parameter in __paramBool) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramFloat) - { - - if (parameter.__isUniform) - { + for (parameter in __paramFloat) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } - for (parameter in __paramInt) - { - - if (parameter.__isUniform) - { - + for (parameter in __paramInt) { + if (parameter.__isUniform) { parameter.index = gl.getUniformLocation(glProgram, parameter.name); - } - else - { - + } else { parameter.index = gl.getAttribLocation(glProgram, parameter.name); } } @@ -590,4 +698,16 @@ class ShaderTypeException extends Exception { this.name = name; super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.'); } +} + +class ShaderErrorPosition { + public var column:Int; + public var line:Int; + public var message:String; + + public function new(line:String, column:String, message:String) { + this.line = Std.parseInt(line); + this.column = Std.parseInt(column); + this.message = message; + } } \ No newline at end of file diff --git a/source/funkin/backend/system/framerate/FlixelInfo.hx b/source/funkin/backend/system/framerate/FlixelInfo.hx index 259b7f8f..297fcde7 100644 --- a/source/funkin/backend/system/framerate/FlixelInfo.hx +++ b/source/funkin/backend/system/framerate/FlixelInfo.hx @@ -1,5 +1,7 @@ package funkin.backend.system.framerate; +import funkin.backend.scripting.ModState; + class FlixelInfo extends FramerateCategory { public function new() { super("Flixel Info"); @@ -14,7 +16,12 @@ class FlixelInfo extends FramerateCategory { for(_ in FlxG.bitmap._cache.keys()) c++; - _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + if((FlxG.state is ModState)) { + var state:ModState = cast FlxG.state; + _text = "Mod State: " + state.scriptName; + } else { + _text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}'; + } _text += '\nObject Count: ${FlxG.state.members.length}'; _text += '\nCamera Count: ${FlxG.cameras.list.length}'; _text += '\nBitmaps Count: ${c}'; @@ -22,9 +29,9 @@ class FlixelInfo extends FramerateCategory { _text += '\nFlxG.game Childs Count: ${FlxG.game.numChildren}'; // _text += '\nCached objects count: ${cachedObjects}'; #if FLX_POINT_POOL - var points = flixel.math.FlxPoint.FlxBasePoint.pool; + //var points = flixel.math.FlxPoint.FlxBasePoint.pool; //_text += '\nPoint Count: ${points._count} | +${points.made} | -${points.gotten} | ${points.balance} | >${points.putted}'; - _text += '\nPoint Count: ${points._count}'; + //_text += '\nPoint Count: ${points._count}'; #end } diff --git a/source/funkin/backend/system/github/GitHub.hx b/source/funkin/backend/system/github/GitHub.hx index 6129a2d9..515e2f63 100644 --- a/source/funkin/backend/system/github/GitHub.hx +++ b/source/funkin/backend/system/github/GitHub.hx @@ -4,6 +4,7 @@ import haxe.Exception; #if GITHUB_API import haxe.Json; #end +import haxe.Exception; // TODO: Document further and perhaps make this a Haxelib. class GitHub { diff --git a/source/funkin/backend/system/macros/HashLinkFixer.hx b/source/funkin/backend/system/macros/HashLinkFixer.hx index 046929e8..697d3c01 100644 --- a/source/funkin/backend/system/macros/HashLinkFixer.hx +++ b/source/funkin/backend/system/macros/HashLinkFixer.hx @@ -42,26 +42,12 @@ class HashLinkFixer { var cl = clRef.get(); if (cl.isAbstract || cl.isExtern || cl.isInterface) return fields; - if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC") && !cl.name.endsWith("_HLFHelper")) { - var metas = cl.meta.get(); - + if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith("_HSX") && !cl.name.endsWith("_HSC")) { if(cl.params.length > 0) return fields; - if(cl.module == "EReg") return fields; // private typedef in same class - if(cl.module == "hl.Format") return fields; // enum in same class - var definedFields = []; - var helperClass = macro class { - - }; - - helperClass.pos = cl.pos; - - var module = cl.module + "_HLFHelper"; - var hcClassName = cl.name + "_HLFHelper"; - for(f in fields.copy()) { if (f == null) continue; @@ -70,10 +56,12 @@ class HashLinkFixer { if(definedFields.contains(f.name)) continue; // no duplicate fields + var hlNativeMeta = null; var hasHlNative = false; for(m in f.meta) if (m.name == ":hlNative") { hasHlNative = true; + hlNativeMeta = m; break; } @@ -92,16 +80,39 @@ class HashLinkFixer { var overrideExpr:Expr; var returns:Bool = !fun.ret.match(TPath({name: "Void"})); + var printer = new haxe.macro.Printer(); + if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void + var name = 'hlf_${f.name}'; var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}]; - var funcExpr:Expr = returns ? { - //macro return $i{name}($a{arguments}); - macro return @:privateAccess $i{hcClassName}.$name($a{arguments}); - } : { - macro @:privateAccess $i{hcClassName}.$name($a{arguments}); - }; + var funcExpr:Expr = macro @:privateAccess $i{name}($a{arguments}); + if(returns) funcExpr = macro return $funcExpr; + + var cleanMeta = f.meta.copy().filter(function(m) return m.name != ":hlNative"); + var hasBareMeta = hlNativeMeta.params.length == 0; + + var meta = f.meta.copy(); + switch hlNativeMeta { + case {params: []}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CString(name))]}: + meta = [{name: ":hlNative", params: [macro "std", macro $v{name}], pos: Context.currentPos()}].concat(cleanMeta); + case {params: [_.expr => EConst(CFloat(version))]}: + var curVersion = Context.definedValue("hl_ver"); + if(curVersion == null) curVersion = ""; + if(version > curVersion) { + meta = cleanMeta; + if(f.meta.filter((m) -> m.name == ":noExpr").length > 0) + Context.error("Missing function body", f.pos); + funcExpr = fun.expr; // restore to default behaviour + } else { + meta = [{name: ":hlNative", params: [macro "std", macro $v{f.name}], pos: Context.currentPos()}].concat(cleanMeta); + } + default: + } var fiel:Field = { name: name, @@ -109,50 +120,32 @@ class HashLinkFixer { kind: FFun({ ret: fun.ret, params: fun.params.copy(), - expr: fun.expr, + expr: funcExpr, args: fun.args.copy() }), - access: f.access.copy(), - meta: f.meta.copy() + access: f.access.copy().filter(function(a) return a != APublic && a != APrivate).concat([APrivate]), + meta: meta }; - helperClass.fields.push(fiel); + fields.push(fiel); definedFields.push(f.name); + // Remove meta from original function for(m in f.meta.copy()) if (m.name == ":hlNative") { f.meta.remove(m); } - fun.expr = funcExpr; default: } } - helperClass.pack = cl.pack.copy(); - helperClass.pos = cl.pos; - helperClass.name = hcClassName; - - if(definedFields.length > 0) { + /*if(definedFields.length > 0) { trace(cl.module); - /*for(m in metas.copy()) { - trace(" " + m.name); - if(m.name == ":coreApi") { - metas.remove(m); - } - }*/ - - var imports = Context.getLocalImports().copy(); - Context.defineModule(module, [helperClass], imports); - - Context.getLocalImports().push({ - path: [for(m in module.split(".")) { - name: m, - pos: Context.currentPos() - }], - mode: INormal - }); - } + var printer = new haxe.macro.Printer(); + for(field in fields) if(field.name.startsWith("hlf_")) + Sys.println(printer.printField(field)); + }*/ } return fields; diff --git a/source/funkin/backend/system/updating/AsyncUpdater.hx b/source/funkin/backend/system/updating/AsyncUpdater.hx index ac31b044..36b79ad4 100644 --- a/source/funkin/backend/system/updating/AsyncUpdater.hx +++ b/source/funkin/backend/system/updating/AsyncUpdater.hx @@ -99,7 +99,7 @@ class AsyncUpdater { progress.files = files.length; progress.step = DOWNLOADING_ASSETS; trace('starting assets download'); - doFile([for(e in files) e], fileNames.copy(), function() { + doFile(files.copy(), fileNames.copy(), function() { progress.curFile = -1; progress.curFileName = null; progress.files = 1; diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index f25fc31d..a870884d 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -525,7 +525,7 @@ typedef DPresence = var ?button1Url:String; /* max 512 bytes */ var ?button2Label:String; /* max 32 bytes */ var ?button2Url:String; /* max 512 bytes */ - var ?activityType:ActivityType; + var ?activityType:#if DISCORD_RPC ActivityType #else Dynamic #end; var ?streamUrl:String; /* max 512 bytes */ } diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index e59e3575..131346ce 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1661,7 +1661,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/game/Character.hx b/source/funkin/game/Character.hx index b11b8183..acb0845b 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -295,15 +295,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset @:noCompletion var __reverseTrailProcedure:Bool = false; - // When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;` + // When using trails on characters you should do `trail.beforeCache = char.beforeTrailCache;` and `trail.afterCache = char.afterTrailCache;` public dynamic function beforeTrailCache() if (isFlippedOffsets()) { flipX = !flipX; scale.x *= -1; __reverseTrailProcedure = true; } - - // When using trails on characters you should do `trail.afterCache = char.afterTrailCache;` + public dynamic function afterTrailCache() if (__reverseTrailProcedure) { flipX = !flipX; @@ -561,4 +560,4 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset list.push(CoolUtil.getFilename(path)); return list; } -} \ No newline at end of file +} diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 74496268..f68e1a10 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -463,6 +463,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. */ @@ -1683,9 +1699,11 @@ class PlayState extends MusicBeatState var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = gameAndCharsEvent("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 = gameAndCharsEvent("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); gameAndCharsEvent("onNoteHit", event); @@ -1702,7 +1720,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; } } @@ -1732,7 +1751,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 { @@ -1761,14 +1780,12 @@ class PlayState extends MusicBeatState } public function displayCombo(?evt:NoteHitEvent = null):Void { - var hasEvent = evt != null; - var pre:String = hasEvent ? evt.ratingPrefix : ""; - var suf:String = hasEvent ? evt.ratingSuffix : ""; + if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) { + var hasEvent = evt != null; + var pre:String = hasEvent ? evt.ratingPrefix : ""; + var suf:String = hasEvent ? evt.ratingSuffix : ""; - var separatedScore:String = Std.string(combo).addZeros(3); - - if (combo == 0 || combo >= 10) { - if (combo >= 10) { + if (evt.displayCombo) { var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}')); comboSpr.resetSprite(comboGroup.x, comboGroup.y); comboSpr.acceleration.y = 600; @@ -1790,6 +1807,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/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index 7a4d7a38..d7ebd934 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -143,8 +143,8 @@ class DialogueCutscene extends ScriptedCutscene { 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) { @@ -159,7 +159,7 @@ class DialogueCutscene extends ScriptedCutscene { } if (curLine.callback != null) - dialogueScript.call(curLine.callback, [playFirst]); + dialogueScript.call(curLine.callback, [event.playFirst]); for (k=>c in charMap) if (k != curLine.char) @@ -172,8 +172,8 @@ class DialogueCutscene extends ScriptedCutscene { 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) { @@ -183,7 +183,7 @@ class DialogueCutscene extends ScriptedCutscene { 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() { 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); + } +}