diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..96d12b21 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,36 @@ +# You need to run the following command in your repository: +# +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# +# afterwards, revisions specified in this file will be ignored in git blame +# any revision needs to be in a unabbreviated sha1 format +# see https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt for more information + +# A good format to use is: + +# # reason for ignoring the revisions +# + +# Removed unused imports & reordered imports +d17ea72c74bbbadac0bb00e11868dddf6aebd346 + +# ScriptPack revert +8a2ac9e1d4e51e688cd2339bdccd6b39ed7d52a3 + +# Spaces to tabs +a63e5f5958ed0643fab92e9d5708a4c26e5238a1 + +# Removed useless indentation and more spaces to tabs +f8b20774e3f035eeddb9211a28a391b20db74456 + +# Revert a revert +75f6bec8294e5b783368cfd7ae6e45698c326cb3 +4aa95838d7df9c307bcac6456e937081645a0f4a + +# Revert a revert (note types use int) +17e177a69554f4d798f5acdd3799687dc9f01a93 +008ad05bf1407d9772805ed69985cfbcde79e495 + +# Revert a revert (empty script) +392416e00cbd82e1c49e82a9fafdcf2e069a654e +159467de832a172681200c463d244a46e295c3c8 \ No newline at end of file diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4f1084dc..1efcfcfa 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -38,12 +38,12 @@ jobs: - name: Tar files run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin . - name: Uploading artifact (entire build) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Codename Engine path: CodenameEngine.tar.gz # - name: Uploading artifact (executable) - # uses: actions/upload-artifact@v2 + # uses: actions/upload-artifact@v4 # with: # name: Update-Linux # path: export/release/linux/bin/CodenameEngine diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c11a793f..cdd60670 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -35,12 +35,12 @@ jobs: - name: Tar files run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin . - name: Uploading artifact (entire build) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Codename Engine path: CodenameEngine.tar.gz # - name: Uploading artifact (executable) - # uses: actions/upload-artifact@v2 + # uses: actions/upload-artifact@v4 # with: # name: Update-Mac # path: export/release/macos/bin/CodenameEngine diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3ec71f90..2986be18 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,12 +33,12 @@ jobs: run: | haxelib run lime build windows - name: Uploading artifact (entire build) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Codename Engine path: export/release/windows/bin # - name: Uploading artifact (executable) - # uses: actions/upload-artifact@v2 + # uses: actions/upload-artifact@v4 # with: # name: Update-Windows # path: export/release/windows/bin/CodenameEngine.exe diff --git a/BETAMESSAGE.md b/BETAMESSAGE.md index 38685be3..cf86b374 100644 --- a/BETAMESSAGE.md +++ b/BETAMESSAGE.md @@ -35,7 +35,7 @@ Shoutout to the people who stuck with us throughout the alpha and supported us! - ### New Features - Opponent & Co-op modes - New volume change SFX (no more loud flixel beep, customizable) - - New FPS counter allowing you to see advanced info by pressing F3. + - New FPS counter allowing you to see advanced info by pressing F3 (infos can be selected and copied too when in advanced). - (Windows) FNF is no longer blurry on 125-150% DPI - FNF auto fixes audio on state change when you plug in/out your headphones. - FNF auto applies dark titlebar diff --git a/FEATURES.md b/FEATURES.md index 9d53fcaf..caa85417 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -17,7 +17,7 @@ _**QOL = Quality of Life**_ - Usage of [flxanimate](https://github.com/Dot-Stuff/flxanimate) on big sprites, such as Girlfriend to save memory. - You can further optimize it on certain stages by enabling `Low Memory Mode` in `Options > Appearance`. - New volume change SFX (no more loud flixel beep, customizable) -- New FPS counter allowing you to see advanced info by pressing F3. +- New FPS counter allowing you to see advanced info by pressing F3 (infos can be selected and copied too when in advanced). - Simple but advanced modding system (press TAB on main menu) - **Windows only:** - (Windows) FNF is no longer blurry on 125-150% DPI diff --git a/README.md b/README.md index 604cd9c2..b4d4bcc9 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ Make sure to read the wiki and join the Discord server linked there!
Latest builds for the engine can be found in the [Actions](https://github.com/YoshiCrafter29/CodenameEngine/actions) tab.
In the future (when the engine won't be a WIP anymore) we're gonna also publish the engine on platforms like Gamebanana; stay tuned! +If you don't have a github account please go onto https://fnf-cne-devs.github.io/ and click the download button for the respective operating system. +

How to build

@@ -76,7 +78,7 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish - Steal code from Codename Engine for another different project that is not Codename Engine related (Codename Engine mods excluded) without properly crediting - Release the entire Codename Engine on platforms (Mods that use Codename Engine as source are fine, if it's specified even better) - #### *If you need more info or feel like asking to do something which is not listed here, ask us directly on our discord (linked in the wiki)!* + #### *If you need more info or feel like asking to do something which is not listed here, ask us directly on our discord (linked on the website)!*
diff --git a/art/flashFiles/Editor_Icons.fla b/art/flashFiles/Editor_Icons.fla new file mode 100644 index 00000000..6ad80d3d Binary files /dev/null and b/art/flashFiles/Editor_Icons.fla differ diff --git a/art/generateDoc.bat b/art/generateDoc.bat index 5159ca88..e2d35e4f 100644 --- a/art/generateDoc.bat +++ b/art/generateDoc.bat @@ -1,7 +1,8 @@ @ECHO OFF cd .. echo Building Game... -lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml" -echo Generating Documentation... -haxelib run dox -i docs --include funkin --include scripting -echo Done! \ No newline at end of file +lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml" -D DOCUMENTATION --no-output +echo art + +echo Generated the api xml file at docs/doc.xml +echo Please put this in FNF-CNE-Devs.github.io/api-generator/api/doc.xml \ No newline at end of file diff --git a/assets/data/scripts/pixel.hx b/assets/data/scripts/pixel.hx index 5539c1f4..88bb5af5 100644 --- a/assets/data/scripts/pixel.hx +++ b/assets/data/scripts/pixel.hx @@ -1,3 +1,4 @@ +// import funkin.game.HudCamera; import funkin.backend.scripting.events.NoteHitEvent; diff --git a/assets/data/stages/limo.hx b/assets/data/stages/limo.hx index 0116d55a..d9a58d3c 100644 --- a/assets/data/stages/limo.hx +++ b/assets/data/stages/limo.hx @@ -1,39 +1,43 @@ var fastCarCanDrive:Bool = true; -function create() { +function create() resetFastCar(); -} -function update(elapsed:Float) { + +function update(elapsed:Float) +{ if (!fastCarCanDrive && fastCar.x > 2000) { resetFastCar(); fastCarCanDrive = true; } + var offset = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85; + // back limo drive if (Options.lowMemoryMode) { - bgLimo.frameOffset.x = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85; + bgLimo.frameOffset.x = offset; } else { for(e in [bgLimo, dancer1, dancer2, dancer3, dancer4, dancer5]) { - e.frameOffset.x = Math.sin(curBeatFloat / 4) * Math.cos(curBeatFloat / 16) * 85; + e.frameOffset.x = offset; } } } -function beatHit(curBeat:Int) { +function beatHit(curBeat:Int) if (FlxG.random.bool(10) && fastCarCanDrive) fastCarDrive(); -} - -function resetFastCar() { +function resetFastCar() +{ fastCar.x = -12600; fastCar.y = FlxG.random.int(140, 250); fastCar.velocity.x = 0; + fastCar.moves = false; fastCarCanDrive = true; } function fastCarDrive() { FlxG.sound.play(Paths.soundRandom('carPass', 0, 1), 0.7); - fastCar.velocity.x = (FlxG.random.int(170, 220) / FlxG.elapsed) * 3; + fastCar.velocity.x = FlxG.random.int(170, 220) * 60 * 3; + fastCar.moves = true; fastCarCanDrive = false; } \ No newline at end of file diff --git a/assets/data/stages/philly.hx b/assets/data/stages/philly.hx index 989b29b5..6b120b51 100644 --- a/assets/data/stages/philly.hx +++ b/assets/data/stages/philly.hx @@ -1,3 +1,4 @@ +// import funkin.game.PlayState; // phillyTrain is automatically added by Stage.hx diff --git a/assets/data/stages/school-evil.hx b/assets/data/stages/school-evil.hx index af086dab..c29dbeea 100644 --- a/assets/data/stages/school-evil.hx +++ b/assets/data/stages/school-evil.hx @@ -1,5 +1,4 @@ function create() { - importScript("data/scripts/pixel"); isSpooky = true; // Make transition work between school types diff --git a/assets/data/stages/school-evil.xml b/assets/data/stages/school-evil.xml index 568b3aad..48ee14b7 100644 --- a/assets/data/stages/school-evil.xml +++ b/assets/data/stages/school-evil.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/assets/data/stages/school.hx b/assets/data/stages/school.hx index 5cba9421..dc55a69d 100644 --- a/assets/data/stages/school.hx +++ b/assets/data/stages/school.hx @@ -1,5 +1,5 @@ +// function create() { - importScript("data/scripts/pixel"); if (PlayState.SONG.meta.name.toLowerCase() == "roses") { bgGirls.animation.remove("danceLeft"); bgGirls.animation.remove("danceRight"); diff --git a/assets/data/stages/school.xml b/assets/data/stages/school.xml index dabf463b..7a2740ad 100644 --- a/assets/data/stages/school.xml +++ b/assets/data/stages/school.xml @@ -1,5 +1,7 @@ + + diff --git a/assets/images/editors/icons.png b/assets/images/editors/icons.png index 41dee63c..cccc10f9 100644 Binary files a/assets/images/editors/icons.png and b/assets/images/editors/icons.png differ diff --git a/assets/shaders/engine/editorBlur.frag b/assets/shaders/engine/editorBlur.frag index 937aa028..59b91c72 100644 --- a/assets/shaders/engine/editorBlur.frag +++ b/assets/shaders/engine/editorBlur.frag @@ -18,17 +18,17 @@ void main() { return; vec4 color = getColor(camPos); - float fsteps = steps; - for(float inside = 1.0; inside < stepsInside+1.0; inside++) { + float fsteps = float(steps); + for(float inside = 1.0; inside < float(stepsInside)+1.0; inside++) { for(int i = 0; i < steps; i++) { - float fi = i; + float fi = float(i); color += getColor(camPos + vec2( - strength * (inside / stepsInside) * cos(fi / fsteps * (PI * 2.0)), - strength * (inside / stepsInside) * sin(fi / fsteps * (PI * 2.0)) + strength * (inside / float(stepsInside)) * cos(fi / fsteps * (PI * 2.0)), + strength * (inside / float(stepsInside)) * sin(fi / fsteps * (PI * 2.0)) )); } } - color /= steps * stepsInside; + color /= vec4(steps * stepsInside); gl_FragColor = color; -} \ No newline at end of file +} diff --git a/commandline/commands/Setup.hx b/commandline/commands/Setup.hx index 2fd3554e..87927075 100644 --- a/commandline/commands/Setup.hx +++ b/commandline/commands/Setup.hx @@ -55,10 +55,10 @@ class Setup { case "lib" | "git": final lib:Library = { name: libNode.att.name, - type: libNode.name + type: libNode.name, + skipDeps: libNode.has.skipDeps ? libNode.att.skipDeps == "true" : false, }; if (libNode.has.global) lib.global = libNode.att.global; - if (libNode.has.skipDeps) lib.skipDeps = libNode.att.skipDeps; switch (lib.type) { case "lib": if (libNode.has.version) lib.version = libNode.att.version; @@ -143,7 +143,7 @@ class Setup { case INSTALL: var lib:Library = event.data; var globalSuffix:Null = lib.global == "true" ? " --global" : ""; - var skipDeps = lib.skipDeps == "true" ? " --skip-dependencies" : ""; + var skipDeps = lib.skipDeps ? " --skip-dependencies" : ""; var commandPrefix = commandSuffix + globalSuffix + skipDeps;// + " --no-timeout"; switch(lib.type) { case "lib": @@ -278,8 +278,8 @@ class Setup { typedef Library = { var name:String; var type:String; + var skipDeps:Bool; var ?global:String; - var ?skipDeps:String; var ?recursive:String; var ?version:String; var ?ref:String; diff --git a/libs.xml b/libs.xml index 6e4b44c4..b824cbcb 100644 --- a/libs.xml +++ b/libs.xml @@ -46,7 +46,7 @@ Rebuilding Lime... - + --> diff --git a/project.xml b/project.xml index 7318589a..24538c29 100644 --- a/project.xml +++ b/project.xml @@ -195,6 +195,7 @@ + diff --git a/source/funkin/backend/FlxAnimate.hx b/source/funkin/backend/FlxAnimate.hx index c2b6e8c6..a0df7cf9 100644 --- a/source/funkin/backend/FlxAnimate.hx +++ b/source/funkin/backend/FlxAnimate.hx @@ -15,7 +15,7 @@ class FlxAnimate extends flxanimate.FlxAnimate { for (camera in cameras) { rMatrix.identity(); - rMatrix.translate(-limb.offset.x, -limb.offset.y); + limb.prepareMatrix(rMatrix, FlxFrameAngle.ANGLE_0, _checkFlipX() != camera.flipX, _checkFlipY() != camera.flipY); rMatrix.concat(_rMatrix); if (!camera.visible || !camera.exists || !limbOnScreen(limb, _rMatrix, camera)) return; @@ -25,10 +25,13 @@ class FlxAnimate extends flxanimate.FlxAnimate { if (limb != _pivot) { if (frameOffsetAngle != null && frameOffsetAngle != angle) { - var angleOff = (-angle + frameOffsetAngle) * FlxAngle.TO_RAD; - rMatrix.rotate(-angleOff); + var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD; + var cos = Math.cos(angleOff); + var sin = Math.sin(angleOff); + // cos doesnt need to be negated + rMatrix.rotateWithTrig(cos, -sin); rMatrix.translate(-frameOffset.x, -frameOffset.y); - rMatrix.rotate(angleOff); + rMatrix.rotateWithTrig(cos, sin); } else { diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index 9e64b5a4..e084c5a6 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -112,7 +112,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I if (!debugMode && isAnimFinished()) { var name = getAnimName() + '-loop'; if (hasAnimation(name)) - playAnim(name, false, lastAnimContext); + playAnim(name, null, lastAnimContext); } } @@ -290,11 +290,16 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I #if REGION public var lastAnimContext:PlayAnimContext = DANCE; - public function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0):Void + public function playAnim(AnimName:String, ?Force:Null, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0):Void { if (AnimName == null) return; + if (Force == null) { + var anim = animDatas.get(AnimName); + Force = anim != null && anim.forced; + } + if (animateAtlas != null) { @:privateAccess diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 67b92967..81b35d96 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -76,14 +76,16 @@ class Paths inline static public function voices(song:String, difficulty:String = "normal", ?prefix:String = "") { - var diff = getPath('songs/${song.toLowerCase()}/song/Voices$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Voices$prefix.$SOUND_EXT', null); + song = song.toLowerCase(); + var diff = getPath('songs/${song}/song/Voices$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Voices$prefix.$SOUND_EXT', null); } inline static public function inst(song:String, difficulty:String = "normal", ?prefix:String = "") { - var diff = getPath('songs/${song.toLowerCase()}/song/Inst$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Inst$prefix.$SOUND_EXT', null); + song = song.toLowerCase(); + var diff = getPath('songs/${song}/song/Inst$prefix-${difficulty.toLowerCase()}.$SOUND_EXT', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song}/song/Inst$prefix.$SOUND_EXT', null); } static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String = "png") diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index 347ba33a..09050b91 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -69,7 +69,11 @@ final class EventsData { hscriptInterp.variables.set("String", TString); hscriptInterp.variables.set("StrumLine", TStrumLine); hscriptInterp.variables.set("ColorWheel", TColorWheel); - hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs((args) -> {return args.length > 0 ? TDropDown([for (arg in args) Std.string(arg)]) : TDropDown(["null"]);})); + hscriptInterp.variables.set("DropDown", Reflect.makeVarArgs(function(args:Array):EventParamType { + var flatArgs = CoolUtil.deepFlatten(args); + if(flatArgs.length == 0) return TDropDown(["null"]); + return TDropDown([for (arg in flatArgs) Std.string(arg)]); + })); hscriptInterp.variables.set("Character", TCharacter); hscriptInterp.variables.set("Stage", TStage); @@ -77,11 +81,12 @@ final class EventsData { hscriptParser.allowJSON = hscriptParser.allowMetadata = false; for (file in Paths.getFolderContent('data/events/', true, BOTH)) { - if (Path.extension(file) != "json" && Path.extension(file) != "pack") continue; + var ext = Path.extension(file); + if (ext != "json" && ext != "pack") continue; var eventName:String = CoolUtil.getFilename(file); var fileTxt:String = Assets.getText(file); - if (Path.extension(file) == "pack") { + if (ext == "pack") { var arr = fileTxt.split("________PACKSEP________"); eventName = Path.withoutExtension(arr[0]); fileTxt = arr[2]; @@ -93,13 +98,17 @@ final class EventsData { eventsParams.set(eventName, []); try { - var data:Dynamic = Json.parse(fileTxt); + var data:EventInfoFile = cast Json.parse(fileTxt); if (data == null || data.params == null) continue; var finalParams:Array = []; - for (paramData in cast(data.params, Array)) { + for (paramData in data.params) { try { - finalParams.push({name: paramData.name, type: hscriptInterp.expr(hscriptParser.parseString(paramData.type)), defValue: paramData.defaultValue}); + finalParams.push({ + name: paramData.name, + type: hscriptInterp.expr(hscriptParser.parseString(paramData.type)), + defValue: paramData.defaultValue + }); } catch (e) {trace('Error parsing event param ${paramData.name} - ${eventName}: $e'); finalParams.push(null);} } eventsParams.set(eventName, finalParams); @@ -110,6 +119,14 @@ final class EventsData { } } +typedef EventInfoFile = { + var params:Array<{ + var name:String; + var type:String; + var defaultValue:Dynamic; + }>; +} + typedef EventInfo = { var params:Array; var paramValues:Array; diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx index 0e5f55df..0d066a61 100644 --- a/source/funkin/backend/chart/FNFLegacyParser.hx +++ b/source/funkin/backend/chart/FNFLegacyParser.hx @@ -226,7 +226,7 @@ typedef SwagSong = typedef SwagSection = { - var sectionNotes:Array; + var sectionNotes:Array>; var lengthInSteps:Int; var ?sectionBeats:Float; var ?typeOfSection:Int; diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx index 5c8d66d7..5ecb6299 100644 --- a/source/funkin/backend/scripting/HScript.hx +++ b/source/funkin/backend/scripting/HScript.hx @@ -64,7 +64,7 @@ class HScript extends Script { } private function importFailedCallback(cl:Array):Bool { - if(_importFailedCallback(cl, "") || _importFailedCallback(cl, "source/")) { + if(_importFailedCallback(cl, "source/") || _importFailedCallback(cl, "")) { return true; } return false; diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index 2e8f9f47..a0433ba3 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -283,7 +283,7 @@ class Script extends FlxBasic implements IFlxDestroyable { public function get(variable:String):Dynamic {return null;} /** - * Gets the variable `variable` from the script's variables. + * Sets the variable `variable` from the script's variables. * @param variable Name of the variable. * @return Variable (or null if it doesn't exists) */ diff --git a/source/funkin/backend/scripting/events/DirectionAnimEvent.hx b/source/funkin/backend/scripting/events/DirectionAnimEvent.hx index a23626f4..dce2eaff 100644 --- a/source/funkin/backend/scripting/events/DirectionAnimEvent.hx +++ b/source/funkin/backend/scripting/events/DirectionAnimEvent.hx @@ -26,7 +26,7 @@ final class DirectionAnimEvent extends CancellableEvent { **/ public var frame:Int; /** - Force the animation to replay even if it's already playing. + Force the animation to replay even if it's already playing (if it's null it will be forced based on the sprite's data xml, if it has one). **/ - public var force:Bool = true; + public var force:Null; } diff --git a/source/funkin/backend/scripting/events/NoteHitEvent.hx b/source/funkin/backend/scripting/events/NoteHitEvent.hx index d5f64dc3..4bac078e 100644 --- a/source/funkin/backend/scripting/events/NoteHitEvent.hx +++ b/source/funkin/backend/scripting/events/NoteHitEvent.hx @@ -100,9 +100,9 @@ final class NoteHitEvent extends CancellableEvent { */ public var ratingAntialiasing:Bool = true; /** - * Whenever the animation should be forced to play. + * Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one). */ - public var forceAnim:Bool = true; + public var forceAnim:Null = true; /** * Prevents the default sing animation from being played. diff --git a/source/funkin/backend/scripting/events/NoteMissEvent.hx b/source/funkin/backend/scripting/events/NoteMissEvent.hx index e9475bb0..72f87ddd 100644 --- a/source/funkin/backend/scripting/events/NoteMissEvent.hx +++ b/source/funkin/backend/scripting/events/NoteMissEvent.hx @@ -29,9 +29,9 @@ final class NoteMissEvent extends CancellableEvent { public var gfSadAnim:String; public var forceGfAnim:Bool; /** - * Whenever the animation should be forced to play. + * Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one). */ - public var forceAnim:Bool; + public var forceAnim:Null; /** * Suffix of the animation. "miss" for miss notes, "-alt" for alt notes, "" for normal ones. */ diff --git a/source/funkin/backend/scripting/events/PlayAnimEvent.hx b/source/funkin/backend/scripting/events/PlayAnimEvent.hx index a555c3ed..f51c8243 100644 --- a/source/funkin/backend/scripting/events/PlayAnimEvent.hx +++ b/source/funkin/backend/scripting/events/PlayAnimEvent.hx @@ -7,9 +7,9 @@ final class PlayAnimEvent extends CancellableEvent { public var animName:String; /** - Whenever the animation will be forced or not. + Whenever the animation will be forced or not (if it's null it will be forced based on the sprite's data xml, if it has one). **/ - public var force:Bool; + public var force:Null; /** Whenever the animation will play in reverse or not diff --git a/source/funkin/backend/shaders/FunkinShader.hx b/source/funkin/backend/shaders/FunkinShader.hx index b676ae45..0b282ffb 100644 --- a/source/funkin/backend/shaders/FunkinShader.hx +++ b/source/funkin/backend/shaders/FunkinShader.hx @@ -438,34 +438,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); - 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) { @@ -484,9 +483,7 @@ vec4 textureCam(sampler2D bitmap, vec2 pos) { return flixel_texture2D(bitmap, camToOg(pos)); }"; - public static final fragBody:String = "vec4 color = flixel_texture2D (bitmap, openfl_TextureCoordv); - gl_FragColor = color; -"; + 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; @@ -500,16 +497,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;"; @@ -517,31 +524,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 }"; } diff --git a/source/funkin/backend/system/framerate/Framerate.hx b/source/funkin/backend/system/framerate/Framerate.hx index 2a81b014..0c05af40 100644 --- a/source/funkin/backend/system/framerate/Framerate.hx +++ b/source/funkin/backend/system/framerate/Framerate.hx @@ -125,9 +125,16 @@ class Framerate extends Sprite { bgSprite.scaleX = width; bgSprite.scaleY = height; - var y:Float = height + 4; + var selectable = debugMode == 2; + { // idk i tried to make it more lookable:sob: - Nex + memoryCounter.memoryText.selectable = memoryCounter.memoryPeakText.selectable = + fpsCounter.fpsNum.selectable = fpsCounter.fpsLabel.selectable = + #if SHOW_BUILD_ON_FPS codenameBuildField.selectable = #end selectable; + } + var y:Float = height + 4; for(c in categories) { + c.title.selectable = c.text.selectable = selectable; c.alpha = debugAlpha; c.x = FlxMath.lerp(-c.width - offset.x, 0, debugAlpha); c.y = y; diff --git a/source/funkin/backend/system/framerate/FramerateCategory.hx b/source/funkin/backend/system/framerate/FramerateCategory.hx index a862505c..db89137f 100644 --- a/source/funkin/backend/system/framerate/FramerateCategory.hx +++ b/source/funkin/backend/system/framerate/FramerateCategory.hx @@ -29,6 +29,7 @@ class FramerateCategory extends Sprite { label.x = 0; label.y = 0; label.defaultTextFormat = new TextFormat(Framerate.fontName, label == this.title ? 18 : 12, -1); + label.selectable = false; addChild(label); } this.title.text = title; @@ -51,4 +52,4 @@ class FramerateCategory extends Sprite { bgSprite.scaleX = width; bgSprite.scaleY = height; } -} \ No newline at end of file +} diff --git a/source/funkin/backend/system/framerate/FramerateCounter.hx b/source/funkin/backend/system/framerate/FramerateCounter.hx index c5641ee3..d6ff4b67 100644 --- a/source/funkin/backend/system/framerate/FramerateCounter.hx +++ b/source/funkin/backend/system/framerate/FramerateCounter.hx @@ -22,6 +22,7 @@ class FramerateCounter extends Sprite { label.text = "FPS"; label.multiline = label.wordWrap = false; label.defaultTextFormat = new TextFormat(Framerate.fontName, label == fpsNum ? 18 : 12, -1); + label.selectable = false; addChild(label); } } @@ -37,4 +38,4 @@ class FramerateCounter extends Sprite { fpsLabel.x = fpsNum.x + fpsNum.width; fpsLabel.y = (fpsNum.y + fpsNum.height) - fpsLabel.height; } -} \ No newline at end of file +} diff --git a/source/funkin/backend/system/framerate/MemoryCounter.hx b/source/funkin/backend/system/framerate/MemoryCounter.hx index f38de1d8..1c26a27d 100644 --- a/source/funkin/backend/system/framerate/MemoryCounter.hx +++ b/source/funkin/backend/system/framerate/MemoryCounter.hx @@ -24,6 +24,7 @@ class MemoryCounter extends Sprite { label.text = "FPS"; label.multiline = label.wordWrap = false; label.defaultTextFormat = new TextFormat(Framerate.fontName, 12, -1); + label.selectable = false; addChild(label); } memoryPeakText.alpha = 0.5; @@ -42,4 +43,4 @@ class MemoryCounter extends Sprite { memoryPeakText.x = memoryText.x + memoryText.width; } -} \ No newline at end of file +} diff --git a/source/funkin/backend/system/macros/EventMacro.hx b/source/funkin/backend/system/macros/EventMacro.hx index 810ff1fd..7219a528 100644 --- a/source/funkin/backend/system/macros/EventMacro.hx +++ b/source/funkin/backend/system/macros/EventMacro.hx @@ -75,34 +75,13 @@ class EventMacro { // add a "set this" expr for each variable for(v in values) { var name = v.name; - exprs.push({ - pos: Context.currentPos(), - expr: EBinop(OpAssign, { - pos: Context.currentPos(), - expr: EField({ - pos: Context.currentPos(), - expr: EConst(CIdent("this")) - }, name) - }, { - pos: Context.currentPos(), - expr: EConst(CIdent(name)) - }) - }); + exprs.push(macro this.$name = $i{name}); } // add a "set this" expr to reset each private/hidden variables for(v in hiddenValues) { var name = v.name; - exprs.push({ - pos: Context.currentPos(), - expr: EBinop(OpAssign, { - pos: Context.currentPos(), - expr: EField({ - pos: Context.currentPos(), - expr: EConst(CIdent("this")) - }, name) - }, v.expr) - }); + exprs.push(macro this.$name = ${v.expr}); } exprs.push(macro return this); diff --git a/source/funkin/backend/utils/BitmapUtil.hx b/source/funkin/backend/utils/BitmapUtil.hx index fb9eefb2..e4f7596c 100644 --- a/source/funkin/backend/utils/BitmapUtil.hx +++ b/source/funkin/backend/utils/BitmapUtil.hx @@ -20,7 +20,7 @@ final class BitmapUtil { for(y in 0...bmap.height) { for(x in 0...bmap.width) { color = bmap.getPixel32(x, y); - fixedColor = 0xFF000000 + (color % 0x1000000); + fixedColor = 0xFF000000 | (color & 0xFFFFFF); if (!colorMap.exists(fixedColor)) colorMap[fixedColor] = 0; colorMap[fixedColor] += color.alphaFloat; @@ -51,7 +51,7 @@ final class BitmapUtil { for(y in 0...bmap.height) { for(x in 0...bmap.width) { color = bmap.getPixel32(x, y); - fixedColor = 0xFF000000 + (color % 0x1000000); + fixedColor = 0xFF000000 | (color & 0xFFFFFF); if (!colorMap.exists(fixedColor)) colorMap[fixedColor] = 0; colorMap[fixedColor] += color.alphaFloat * 0.33 + (0.67 * (color.saturation * (2 * (color.lightness > 0.5 ? 0.5 - (color.lightness) : color.lightness)))); @@ -75,25 +75,33 @@ final class BitmapUtil { */ public static function crop(bitmap:BitmapData) { var bitmapBounds:Rectangle = BitmapUtil.bounds(bitmap); - + var croppedBitmap:BitmapData = new BitmapData(Std.int(bitmapBounds.width), Std.int(bitmapBounds.height), true, 0x00000000); croppedBitmap.copyPixels(bitmap, bitmapBounds, new Point(0,0)); return croppedBitmap; } - + /** * Get bounds of non empty pixels in the bitmap - * @param bitmap + * @param bitmap * @return */ public static function bounds(bitmap:BitmapData, ?limit:Rectangle = null):Rectangle { - var minX:Int = limit != null ? Std.int(limit.width) : bitmap.width; - var minY:Int = limit != null ? Std.int(limit.height) : bitmap.height; - var maxX:Int = limit != null ? Std.int(limit.x) : 0; - var maxY:Int = limit != null ? Std.int(limit.y) : 0; - - for (y in (limit != null ? Std.int(limit.x) : 0)...(limit != null ? Std.int(limit.height) : bitmap.height)) - for (x in (limit != null ? Std.int(limit.y) : 0)...(limit != null ? Std.int(limit.width) : bitmap.width)) { + var hasLimit:Bool = limit != null; + // Searching bounds + var startX:Int = hasLimit ? Std.int(limit.x) : 0; + var startY:Int = hasLimit ? Std.int(limit.y) : 0; + var endX:Int = hasLimit ? Std.int(limit.width) : bitmap.width; + var endY:Int = hasLimit ? Std.int(limit.height) : bitmap.height; + + // Detected bounds + var minX:Int = endX; + var minY:Int = endY; + var maxX:Int = startX; + var maxY:Int = startY; + + for (y in startY...endY) { + for (x in startX...endX) { if (bitmap.getPixel32(x, y) != 0x00000000) { if (x < minX) minX = x; if (y < minY) minY = y; @@ -101,6 +109,7 @@ final class BitmapUtil { if (y > maxY) maxY = y; } } + } return new Rectangle(minX, minY, maxX-minX+1, maxY-minY+1); } diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 365e0a82..56dd7634 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -1010,6 +1010,19 @@ final class CoolUtil return result.join(seperator); } + + public static function deepFlatten(arr:Array, ?result:Array):Array { + if(arr == null) return []; + if(result == null) result = []; + for (e in arr) { + if (Std.isOfType(e, Array)) { + deepFlatten(e, result); + } else { + result.push(e); + } + } + return result; + } } /** @@ -1022,4 +1035,4 @@ enum abstract CoolSfx(Int) from Int { var CHECKED = 3; var UNCHECKED = 4; var WARNING = 5; -} \ No newline at end of file +} diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx index 81a6087a..e3a64b02 100644 --- a/source/funkin/backend/utils/MemoryUtil.hx +++ b/source/funkin/backend/utils/MemoryUtil.hx @@ -117,7 +117,17 @@ final class MemoryUtil { 22 => "DDR2 FB-DIMM", 24 => "DDR3", 25 => "FBD2", - 26 => "DDR4" + 26 => "DDR4", + 27 => "LPDDR", + 28 => "LPDDR2", + 29 => "LPDDR3", + 30 => "LPDDR4", + 31 => "Logical Non-volatile device", + 32 => "HBM", + 33 => "HBM2", + 34 => "DDR5", + 35 => "LPDDR5", + 36 => "HBM3", ]; var memoryOutput:Int = -1; diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index 7ee23c23..d2dcf417 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -4,6 +4,7 @@ import flixel.util.FlxColor; import flixel.util.typeLimit.OneOfTwo; import funkin.backend.FunkinSprite.XMLAnimType; import funkin.backend.FunkinSprite; +import funkin.game.Character; import funkin.backend.system.ErrorCode; import funkin.backend.system.interfaces.IOffsetCompatible; import haxe.xml.Access; @@ -175,8 +176,7 @@ final class XMLUtil { animType: spr.spriteAnimType, x: 0, y: 0, - indices: [for(i in 0...spr.frames.frames.length) i], - forced: (node.has.forced && node.att.forced == "true") || (!node.has.forced && spr.spriteAnimType == BEAT) + indices: [for(i in 0...spr.frames.frames.length) i] }); } } @@ -202,8 +202,7 @@ final class XMLUtil { animType: animType, x: 0, y: 0, - indices: [], - forced: false, + indices: [] }; if (anim.has.name) animData.name = anim.att.name; @@ -259,24 +258,28 @@ final class XMLUtil { if (sprite is FunkinSprite) { var xmlSpr = cast(sprite, FunkinSprite); + var name = animData.name; switch(animData.animType) { case BEAT: xmlSpr.beatAnims.push({ - name: animData.name, - forced: animData.forced.getDefault(false) + name: name, + forced: animData.forced.getDefault(defaultForcedCheck(name, xmlSpr)) }); case LOOP: - xmlSpr.playAnim(animData.name, animData.forced.getDefault(false)); + xmlSpr.playAnim(name, animData.forced.getDefault(defaultForcedCheck(name, xmlSpr))); default: // nothing } - xmlSpr.animDatas.set(animData.name, animData); + xmlSpr.animDatas.set(name, animData); } return OK; } return MISSING_PROPERTY; } + public static inline function defaultForcedCheck(animName:String, sprite:FunkinSprite):Bool + return sprite is Character && (animName.startsWith("idle") || animName.startsWith("danceLeft") || animName.startsWith("danceRight")) ? false : sprite.spriteAnimType == BEAT; + public static inline function fixXMLText(text:String) { var v:String; return [for(l in text.split("\n")) if ((v = l.trim()) != "") v].join("\n"); diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx index 7b98f0e6..f1279b9b 100644 --- a/source/funkin/editors/EditorPicker.hx +++ b/source/funkin/editors/EditorPicker.hx @@ -22,11 +22,21 @@ class EditorPicker extends MusicBeatSubstate { iconID: 2, state: funkin.editors.stage.StageSelection }, + #if debug { name: "UI Debug State", iconID: 3, state: UIDebugState }, + #end + { + name: "Wiki", + iconID: 5, + state: null, + onClick: function() { + CoolUtil.openURL("https://fnf-cne-devs.github.io/"); + } + }, { name: "Debug Options", iconID: 4, @@ -94,7 +104,9 @@ class EditorPicker extends MusicBeatSubstate { } if (controls.ACCEPT || FlxG.mouse.justReleased) { - if (options[curSelected].state != null) { + if(options[curSelected].onClick != null) + options[curSelected].onClick(); + else if (options[curSelected].state != null) { selected = true; CoolUtil.playMenuSFX(CONFIRM); @@ -145,6 +157,7 @@ typedef Editor = { var name:String; var iconID:Int; var state:Class; + var ?onClick:Void->Void; } class EditorPickerOption extends FlxTypedSpriteGroup { @@ -161,7 +174,6 @@ class EditorPickerOption extends FlxTypedSpriteGroup { public function new(name:String, iconID:Int, height:Float) { super(); - FlxG.mouse.visible = true; iconSpr = new FlxSprite(); iconSpr.loadGraphic(Paths.image('editors/icons'), true, 128, 128); diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx index 5c52b1b9..c4c05ef1 100644 --- a/source/funkin/editors/character/CharacterAnimButton.hx +++ b/source/funkin/editors/character/CharacterAnimButton.hx @@ -1,12 +1,12 @@ package funkin.editors.character; +import flxanimate.animate.FlxAnim.FlxSymbolAnimation; import flxanimate.animate.FlxSymbol; import flxanimate.animate.FlxElement; import flxanimate.animate.SymbolParameters; import flxanimate.animate.FlxKeyFrame; import flxanimate.animate.FlxTimeline; import flixel.graphics.frames.FlxFrame; -import flxanimate.animate.FlxAnim.SymbolStuff; import flixel.animation.FlxAnimation; import openfl.geom.Rectangle; import funkin.backend.utils.XMLUtil.AnimData; @@ -215,7 +215,7 @@ class CharacterAnimButton extends UIButton { } if (parent.character.animateAtlas != null) { - var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim]; + var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim]; parent.character.animateAtlas.anim.animsMap.remove(anim); parent.character.animateAtlas.anim.animsMap.set(newName, animSymbol); @@ -251,7 +251,7 @@ class CharacterAnimButton extends UIButton { animData.anim = newAnim; if (parent.character.animateAtlas != null) { - var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim]; + var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim]; refreshSymbolKeyFrames(animSymbol, animData); } else { var flxAnimation:FlxAnimation = parent.character.animation._animations[anim]; @@ -287,7 +287,7 @@ class CharacterAnimButton extends UIButton { animData.fps = newFPS; if (parent.character.animateAtlas != null) { - var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim]; + var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim]; animSymbol.frameRate = newFPS; } else { var flxAnimation:FlxAnimation = parent.character.animation._animations[anim]; @@ -303,7 +303,7 @@ class CharacterAnimButton extends UIButton { animData.loop = newLooping; if (parent.character.animateAtlas != null) { - var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim]; + var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim]; animSymbol.instance.symbol.loop = animData.loop ? Loop : PlayOnce; } else { var flxAnimation:FlxAnimation = parent.character.animation._animations[anim]; @@ -319,7 +319,7 @@ class CharacterAnimButton extends UIButton { animData.indices = indicies; if (parent.character.animateAtlas != null) { - var animSymbol:SymbolStuff = parent.character.animateAtlas.anim.animsMap[anim]; + var animSymbol:FlxSymbolAnimation = parent.character.animateAtlas.anim.animsMap[anim]; refreshSymbolKeyFrames(animSymbol, animData); } else { var flxAnimation:FlxAnimation = parent.character.animation._animations[anim]; @@ -351,7 +351,7 @@ class CharacterAnimButton extends UIButton { } } - public inline function refreshSymbolKeyFrames(symbol:SymbolStuff, animData:AnimData) @:privateAccess { + public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess { if (animData.indices.length > 0) { // keeps on crashing, look at flxanimate FlxAnim.hx for refrence } else { diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 23c3b892..0f6ab63b 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -111,10 +111,10 @@ class Charter extends UIState { { label: "File", childs: [ - { + /*{ label: "New" }, - null, + null,*/ { label: "Save", keybind: [CONTROL, S], @@ -378,9 +378,9 @@ class Charter extends UIState { onSelect: _playback_metronome, icon: Options.charterMetronomeEnabled ? 1 : 0 }, - { + /*{ label: "Visual metronome" - }, + },*/ ] } ]; @@ -436,7 +436,7 @@ class Charter extends UIState { }; uiGroup.add(scrollBar); - songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4"); + songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00 / 00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4"); songPosInfo.alignment = RIGHT; uiGroup.add(songPosInfo); @@ -1184,7 +1184,6 @@ class Charter extends UIState { var snapButton:UITopMenuButton = cast topMenuSpr.members[snapIndex]; var lastButtonX = playBackButton.x-10; - var buttonI:Int = 0; for (button in quantButtons) { button.visible = ((button.quant == quant) || (button.quant == quants[FlxMath.wrap(quants.indexOf(quant)-1, 0, quants.length-1)]) || diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx index 6416cca9..13948728 100644 --- a/source/funkin/editors/charter/CharterNote.hx +++ b/source/funkin/editors/charter/CharterNote.hx @@ -41,7 +41,7 @@ class CharterNote extends UISprite implements ICharterSelectable { this.setUnstretchedGraphicSize(40, 40, false); sustainSpr = new UISprite(10, 20); - sustainSpr.makeSolid(1, 1, -1); + sustainSpr.makeSolid(1, 1, -1, false, "note-sustain"); sustainSpr.scale.set(10, 0); members.push(sustainSpr); @@ -166,8 +166,9 @@ class CharterNote extends UISprite implements ICharterSelectable { } if (strumLine != null) { - sustainSpr.alpha = alpha = !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1); - typeAlpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1); + var isVisible = strumLine.strumLine.visible; + sustainSpr.alpha = alpha = !isVisible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1); + typeAlpha = !isVisible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1); } colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1; diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx index 4c9a1d87..586907a3 100644 --- a/source/funkin/editors/charter/CharterNoteGroup.hx +++ b/source/funkin/editors/charter/CharterNoteGroup.hx @@ -60,14 +60,14 @@ class CharterNoteGroup extends FlxTypedGroup { public override function update(elapsed:Float) @:privateAccess { var oldDefaultCameras = FlxCamera._defaultCameras; - if (cameras != null) FlxCamera._defaultCameras = cameras; + if (_cameras != null) FlxCamera._defaultCameras = _cameras; if (autoSort && members.length != __lastSort) sortNotes(); forEach((n) -> { if(n.exists && n.active) { - n.cameras = n.__lastDrawCameras = cameras; + n._cameras = n.__lastDrawCameras = cameras; n.update(elapsed); } }); diff --git a/source/funkin/editors/charter/CharterStrumline.hx b/source/funkin/editors/charter/CharterStrumline.hx index 58dcd9be..22caa289 100644 --- a/source/funkin/editors/charter/CharterStrumline.hx +++ b/source/funkin/editors/charter/CharterStrumline.hx @@ -46,7 +46,7 @@ class CharterStrumline extends UISprite { } return selectedWaveform = value; } - public var waveformShader:CustomShader; + public var waveformShader:CustomShader; public function new(strumLine:ChartStrumLine) { super(); diff --git a/source/funkin/editors/ui/UIState.hx b/source/funkin/editors/ui/UIState.hx index 47324848..34be9f9e 100644 --- a/source/funkin/editors/ui/UIState.hx +++ b/source/funkin/editors/ui/UIState.hx @@ -75,8 +75,8 @@ class UIState extends MusicBeatState { var pos = FlxG.mouse.getScreenPosition(camera, __point); __rect.copyFrom(rect); - __rect.x -= camera.scroll.x * spr.scrollFactor.x; - __rect.y -= camera.scroll.y * spr.scrollFactor.y; + __rect.x = __rect.x - camera.scroll.x * spr.scrollFactor.x; + __rect.y = __rect.y - camera.scroll.y * spr.scrollFactor.y; if (((pos.x > __rect.x) && (pos.x < __rect.x + __rect.width)) && ((pos.y > __rect.y) && (pos.y < __rect.y + __rect.height))) { return true; diff --git a/source/funkin/editors/ui/UIUtil.hx b/source/funkin/editors/ui/UIUtil.hx index 4a5a0ee4..4036184f 100644 --- a/source/funkin/editors/ui/UIUtil.hx +++ b/source/funkin/editors/ui/UIUtil.hx @@ -4,10 +4,12 @@ import flixel.input.FlxInput.FlxInputState; import flixel.input.keyboard.FlxKey; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; +@:access(flixel.FlxSprite) class UIUtil { public static function follow(spr:FlxSprite, target:FlxSprite, x:Float = 0, y:Float = 0) { - spr.cameras = target is UISprite ? cast(target, UISprite).__lastDrawCameras : target.cameras; - spr.setPosition(target.x + x, target.y + y); + spr._cameras = target is UISprite ? cast(target, UISprite).__lastDrawCameras : target.cameras; + spr.x = target.x + x; + spr.y = target.y + y; spr.scrollFactor.set(target.scrollFactor.x, target.scrollFactor.y); } diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index a8cbb285..8e4361c5 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -291,7 +291,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset /** * Like `playSingAnimUnsafe` but checks if the character has the animation with the suffix part, otherwise it plays the animation without the suffix part. */ - public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0) + 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(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force); script.call("onPlaySingAnim", [event]); @@ -308,7 +308,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset playAnim(event.animName, event.force, event.context, event.reversed, event.frame); } - public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) { + 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]); if (event.cancelled) return; @@ -380,6 +380,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public inline function buildCharacter(xml:Access) { this.xml = xml; // Modders wassup :D sprite = curCharacter; + spriteAnimType = BEAT; if (xml.x.exists("isPlayer")) playerOffsets = (xml.x.get("isPlayer") == "true"); if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x")); diff --git a/source/funkin/game/NoteGroup.hx b/source/funkin/game/NoteGroup.hx index 98b6477e..8c611581 100644 --- a/source/funkin/game/NoteGroup.hx +++ b/source/funkin/game/NoteGroup.hx @@ -34,10 +34,16 @@ class NoteGroup extends FlxTypedGroup { return FlxSort.byValues(FlxSort.DESCENDING, n1.strumTime, n2.strumTime); }); } + + @:dox(hide) public var __forcedSongPos:Null = null; + + @:dox(hide) private inline function __getSongPos() + return __forcedSongPos == null ? Conductor.songPosition : __forcedSongPos; + public override function update(elapsed:Float) { i = length-1; __loopSprite = null; - __time = Conductor.songPosition; + __time = __getSongPos(); while(i >= 0) { __loopSprite = members[i--]; if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.active) { @@ -49,16 +55,16 @@ class NoteGroup extends FlxTypedGroup { } } - public override function draw() { - @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras; - @:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras; + public override function draw() @:privateAccess { + var oldDefaultCameras = FlxCamera._defaultCameras; + if (_cameras != null) FlxCamera._defaultCameras = _cameras; var oldCur = __currentlyLooping; __currentlyLooping = true; i = length-1; __loopSprite = null; - __time = Conductor.songPosition; + __time = __getSongPos(); while(i >= 0) { __loopSprite = members[i--]; if (__loopSprite == null || !__loopSprite.exists || !__loopSprite.visible) @@ -68,7 +74,7 @@ class NoteGroup extends FlxTypedGroup { } __currentlyLooping = oldCur; - @:privateAccess FlxCamera._defaultCameras = oldDefaultCameras; + FlxCamera._defaultCameras = oldDefaultCameras; } /** @@ -81,7 +87,7 @@ class NoteGroup extends FlxTypedGroup { public override function forEach(noteFunc:Note->Void, recursive:Bool = false) { i = length-1; __loopSprite = null; - __time = Conductor.songPosition; + __time = __getSongPos(); var oldCur = __currentlyLooping; __currentlyLooping = true; diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 53de5ce5..036f4a3a 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -242,7 +242,7 @@ class PlayState extends MusicBeatState /** * Current stage name */ - public var curStage:String = ""; + public var curStage(get, set):String; /** * Interval at which Girlfriend dances. @@ -525,6 +525,14 @@ class PlayState extends MusicBeatState return this.maxHealth = v; } + private inline function get_curStage() + return stage == null ? "" : stage.stageName; + + private inline function set_curStage(name:String) { + if (stage != null) stage.stageName = name; + return name; + } + @:dox(hide) override public function create() { Note.__customNoteTypeExists = []; @@ -1933,7 +1941,7 @@ class PlayState extends MusicBeatState } } -class ComboRating { +final class ComboRating { public var percent:Float; public var rating:String; public var color:FlxColor; diff --git a/source/funkin/game/SplashGroup.hx b/source/funkin/game/SplashGroup.hx index b3565df7..7d8f9211 100644 --- a/source/funkin/game/SplashGroup.hx +++ b/source/funkin/game/SplashGroup.hx @@ -107,8 +107,9 @@ class SplashGroup extends FlxTypedGroup { public function getSplashAnim(id:Int):String { if (animationNames.length <= 0) return null; id %= animationNames.length; - if (animationNames[id] == null || animationNames[id].length <= 0) return null; - return animationNames[id][FlxG.random.int(0, animationNames[id].length - 1)]; + var animNames = animationNames[id]; + if (animNames == null || animNames.length <= 0) return null; + return animNames[FlxG.random.int(0, animNames.length - 1)]; } var __splash:FunkinSprite; diff --git a/source/funkin/game/SplashHandler.hx b/source/funkin/game/SplashHandler.hx index 7086782a..31a06234 100644 --- a/source/funkin/game/SplashHandler.hx +++ b/source/funkin/game/SplashHandler.hx @@ -42,7 +42,7 @@ class SplashHandler extends FlxTypedGroup { var __grp:SplashGroup; public function showSplash(name:String, strum:Strum) { __grp = getSplashGroup(name); - var event = funkin.backend.scripting.EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp); + var event = EventManager.get(SplashShowEvent).recycle(name, __grp.showOnStrum(strum), strum, __grp); event = PlayState.instance.scripts.event("onSplashShown", event); if (!event.cancelled) add(event.splash); diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index eb64c169..9e5ce76c 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -3,6 +3,7 @@ package funkin.game; import flixel.util.typeLimit.OneOfTwo; import flixel.FlxState; import flixel.math.FlxPoint; +import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; import funkin.backend.scripting.events.StageNodeEvent; import funkin.backend.scripting.events.StageXMLEvent; @@ -23,6 +24,7 @@ class Stage extends FlxBasic implements IBeatReceiver { public var stageScript:Script; public var state:FlxState; public var characterPoses:Map = []; + public var xmlImportedScripts:Array = []; public var defaultZoom:Float = 1.05; public var startCam = new FlxPoint(); @@ -31,12 +33,15 @@ class Stage extends FlxBasic implements IBeatReceiver { public var onNodeLoaded:(Access, Dynamic)->Dynamic = null; public var onNodeFinished:(Access, Dynamic)->Void = null; public var onAddSprite:(FlxObject)->Void = null; + public var onXMLPostLoaded:(Access, Array)->Array = null; public var spritesParentFolder = ""; - public inline function getSprite(name:String) { + public inline function getSprite(name:String) return stageSprites[name]; - } + + public function setStagesSprites(script:Script) + for (k=>e in stageSprites) script.set(k, e); public function new(stage:String, ?state:FlxState, autoLoad:Bool = true) { super(); @@ -46,13 +51,9 @@ class Stage extends FlxBasic implements IBeatReceiver { this.state = state; stageFile = stage; - stagePath = Paths.xml('stages/$stageFile'); - try { - if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement()); - } catch(e) { - Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR); - } + try if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement()) + catch(e) Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR); if (autoLoad) loadXml(stageXML); } @@ -66,6 +67,8 @@ class Stage extends FlxBasic implements IBeatReceiver { stageScript.load(); } + var event = null; + var elems:Array = []; if (xml != null) { var parsed:Null; if((parsed = Std.parseFloat(xml.getAtt("startCamPosX"))).isNotNull()) startCam.x = parsed; @@ -78,7 +81,6 @@ class Stage extends FlxBasic implements IBeatReceiver { if(xml.has.startCamPosX) PlayState.instance.camFollow.x = startCam.x; if(xml.has.startCamPosY) PlayState.instance.camFollow.y = startCam.y; if(xml.has.zoom) PlayState.instance.defaultCamZoom = defaultZoom; - PlayState.instance.curStage = stageName; } if (xml.has.folder) { spritesParentFolder = xml.att.folder; @@ -90,22 +92,16 @@ class Stage extends FlxBasic implements IBeatReceiver { if(!DEFAULT_ATTRIBUTES.contains(att)) extra.set(att, xml.x.get(att)); - var elems:Array = []; // some way to tag that the sprites are from the group for(node in xml.elements) { - if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll)) - for(e in node.elements) - elems.push(e); - else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll)) - for(e in node.elements) - elems.push(e); - else - elems.push(node); + if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e); + else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e); + else __pushNcheckNode(elems, node); } if (PlayState.instance == state) { - var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, xml, elems)); - elems = event.elems; + event = EventManager.get(StageXMLEvent).recycle(this, xml, elems); + elems = PlayState.instance.scripts.event("onStageXMLParsed", event).elems; } if(onXMLLoaded != null) { elems = onXMLLoaded(xml, elems); @@ -121,7 +117,7 @@ class Stage extends FlxBasic implements IBeatReceiver { stageSprites.set(spr.name, spr); addSprite(spr); case "box" | "solid": - if ( !node.has.name || !node.has.width || !node.has.height) continue; + if (!node.has.name || !node.has.width || !node.has.height) continue; var spr = new FunkinSprite( (node.has.x) ? Std.parseFloat(node.att.x).getDefault(0) : 0, @@ -154,6 +150,9 @@ 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; + null; default: null; } @@ -184,10 +183,31 @@ class Stage extends FlxBasic implements IBeatReceiver { if (characterPoses["boyfriend"] == null) addCharPos("boyfriend", null, getDefaultPos("boyfriend")); - if (PlayState.instance != state) return; - for(k=>e in stageSprites) { - stageScript.set(k, e); + if (PlayState.instance == state) { + setStagesSprites(stageScript); + + // i know this for gets run twice under, but its better like this in case a script modifies the short lived ones, i dont wanna save them in an array; more dynamic like this - Nex + for (info in xmlImportedScripts) if (info.importStageSprites) { + var script = info.getScript(); + if (script != null) setStagesSprites(script); + } + + // idk lemme check anyways just in case scripts did smth - Nex + if (event != null) PlayState.instance.scripts.event("onPostStageCreation", event); + + // shortlived scripts destroy when the stage finishes setting up - Nex + for (info in xmlImportedScripts) if (info.shortLived) { + var script = info.getScript(); + if (script == null) continue; + + PlayState.instance.scripts.remove(script); + script.destroy(); + } } + if(onXMLPostLoaded != null) { + elems = onXMLPostLoaded(xml, elems); + } + } public static function getDefaultPos(name:String):StageCharPosInfo { @@ -219,6 +239,15 @@ 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); + } + + @: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; @@ -410,4 +439,45 @@ typedef OldCharInfo = { var alpha:Float; var zoomFactor:Float; var angle: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 cb8477a8..4019b40a 100644 --- a/source/funkin/game/Strum.hx +++ b/source/funkin/game/Strum.hx @@ -80,7 +80,7 @@ class Strum extends FlxSprite { super.draw(); } - @:noCompletion public static final PIX180:Float = 565.4866776461628; // 180 * Math.PI + @:noCompletion public static inline final PIX180:Float = 565.4866776461628; // 180 * Math.PI @:noCompletion public static final N_WIDTHDIV2:Float = Note.swagWidth / 2; public function updateNotePosition(daNote:Note) { diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 509b33e2..1ddc8999 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -152,8 +152,10 @@ class StrumLine extends FlxTypedGroup { var scrollSpeed = strumLine.scrollSpeed; if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed; if(scrollSpeed == null) scrollSpeed = 1; - var limit = FlxG.height / 0.45; - notes.limit = limit / scrollSpeed; + + // TODOD: Make this work by accounting zoom and scrollspeed changes - Nex + /*var limit = FlxG.height / 0.45; + notes.limit = limit / scrollSpeed;*/ } public override function update(elapsed:Float) { diff --git a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx index 8885f56a..a948344d 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx @@ -94,7 +94,7 @@ class DialogueBox extends FunkinSprite { dialogueBoxScript.call("postCreate"); } - public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) { + 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); dialogueBoxScript.call("onPlayAnim", [event]); if(event.cancelled) return; diff --git a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx index 9fe4c44e..d53bcbd9 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx @@ -61,7 +61,7 @@ class DialogueCharacter extends FunkinSprite { dialogueCharScript.call("postCreate"); } - public override function playAnim(AnimName:String, Force:Bool = false, Context:PlayAnimContext = NONE, Reversed:Bool = false, Frame:Int = 0) { + 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); dialogueCharScript.call("playAnim", [event]); if(event.cancelled) return; diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index 3c122494..c1670dfd 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -281,12 +281,13 @@ class FreeplayState extends MusicBeatState function updateCoopModes() { __opponentMode = false; __coopMode = false; - if (songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed) { + var curSong = songs[curSelected]; + if (curSong.coopAllowed && curSong.opponentModeAllowed) { __opponentMode = curCoopMode % 2 == 1; __coopMode = curCoopMode >= 2; - } else if (songs[curSelected].coopAllowed) { + } else if (curSong.coopAllowed) { __coopMode = curCoopMode == 1; - } else if (songs[curSelected].opponentModeAllowed) { + } else if (curSong.opponentModeAllowed) { __opponentMode = curCoopMode == 1; } } @@ -315,9 +316,10 @@ class FreeplayState extends MusicBeatState } public function convertChart() { - trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...'); - var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]); - Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty].toLowerCase()); + var curSong = songs[curSelected]; + trace('Converting ${curSong.name} (${curSong.difficulties[curDifficulty]}) to Codename format...'); + var chart = Chart.parse(curSong.name, curSong.difficulties[curDifficulty]); + Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSong.difficulties[curDifficulty].toLowerCase()); } /** diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index ff45f20a..7101486c 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -8,6 +8,7 @@ import funkin.backend.scripting.Script; import funkin.backend.scripting.events.MenuChangeEvent; import funkin.backend.scripting.events.NameEvent; import funkin.backend.scripting.events.PauseCreationEvent; +import funkin.backend.system.Conductor; import funkin.backend.utils.FunkinParentDisabler; import funkin.editors.charter.Charter; import funkin.menus.StoryMenuState; @@ -162,6 +163,9 @@ class PauseSubState extends MusicBeatSubstate PlayState.resetSongInfos(); if (Charter.instance != null) Charter.instance.__clearStatics(); + // prevents certain notes to disappear early when exiting - Nex + game.strumLines.forEachAlive(function(grp) grp.notes.__forcedSongPos = Conductor.songPosition); + CoolUtil.playMenuSong(); FlxG.switchState(PlayState.isStoryMode ? new StoryMenuState() : new FreeplayState()); } diff --git a/source/haxe/Timer.hx b/source/haxe/Timer.hx new file mode 100644 index 00000000..5092af8c --- /dev/null +++ b/source/haxe/Timer.hx @@ -0,0 +1,297 @@ +package haxe; + +#if !lime_cffi +// Original haxe.Timer class + +/* + * Copyright (C)2005-2018 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + The Timer class allows you to create asynchronous timers on platforms that + support events. + + The intended usage is to create an instance of the Timer class with a given + interval, set its run() method to a custom function to be invoked and + eventually call stop() to stop the Timer. + + Note that a running Timer may or may not prevent the program to exit + automatically when main() returns. + + It is also possible to extend this class and override its run() method in + the child class. +**/ +class Timer +{ + #if (flash || js) + private var id:Null; + #elseif java + private var timer:java.util.Timer; + private var task:java.util.TimerTask; + #elseif (haxe_ver >= "3.4.0") + private var event:MainLoop.MainEvent; + #end + + /** + Creates a new timer that will run every `time_ms` milliseconds. + + After creating the Timer instance, it calls `this.run` repeatedly, + with delays of `time_ms` milliseconds, until `this.stop` is called. + + The first invocation occurs after `time_ms` milliseconds, not + immediately. + + The accuracy of this may be platform-dependent. + **/ + public function new(time_ms:Int) + { + #if flash + var me = this; + id = untyped __global__["flash.utils.setInterval"](function() + { + me.run(); + }, time_ms); + #elseif js + var me = this; + id = untyped setInterval(function() me.run(), time_ms); + #elseif java + timer = new java.util.Timer(); + timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms)); + #elseif (haxe_ver >= "3.4.0") + var dt = time_ms / 1000; + event = MainLoop.add(function() + { + @:privateAccess event.nextRun += dt; + run(); + }); + event.delay(dt); + #end + } + + /** + Stops `this` Timer. + + After calling this method, no additional invocations of `this.run` + will occur. + + It is not possible to restart `this` Timer once stopped. + **/ + public function stop() + { + #if (flash || js) + if (id == null) return; + #if flash + untyped __global__["flash.utils.clearInterval"](id); + #elseif js + untyped clearInterval(id); + #end + id = null; + #elseif java + if (timer != null) + { + timer.cancel(); + timer = null; + } + task = null; + #elseif (haxe_ver >= "3.4.0") + if (event != null) + { + event.stop(); + event = null; + } + #end + } + + /** + This method is invoked repeatedly on `this` Timer. + + It can be overridden in a subclass, or rebound directly to a custom + function: + var timer = new haxe.Timer(1000); // 1000ms delay + timer.run = function() { ... } + + Once bound, it can still be rebound to different functions until `this` + Timer is stopped through a call to `this.stop`. + **/ + public dynamic function run() {} + + /** + Invokes `f` after `time_ms` milliseconds. + + This is a convenience function for creating a new Timer instance with + `time_ms` as argument, binding its run() method to `f` and then stopping + `this` Timer upon the first invocation. + + If `f` is null, the result is unspecified. + **/ + public static function delay(f:Void->Void, time_ms:Int) + { + var t = new haxe.Timer(time_ms); + t.run = function() + { + t.stop(); + f(); + }; + return t; + } + + /** + Measures the time it takes to execute `f`, in seconds with fractions. + + This is a convenience function for calculating the difference between + Timer.stamp() before and after the invocation of `f`. + + The difference is passed as argument to Log.trace(), with "s" appended + to denote the unit. The optional `pos` argument is passed through. + + If `f` is null, the result is unspecified. + **/ + public static function measure(f:Void->T, ?pos:PosInfos):T + { + var t0 = stamp(); + var r = f(); + Log.trace((stamp() - t0) + "s", pos); + return r; + } + + /** + Returns a timestamp, in seconds with fractions. + + The value itself might differ depending on platforms, only differences + between two values make sense. + **/ + public static inline function stamp():Float + { + #if flash + return flash.Lib.getTimer() / 1000; + #elseif (neko || php) + return Sys.time(); + #elseif js + return Date.now().getTime() / 1000; + #elseif cpp + return untyped __global__.__time_stamp(); + #elseif python + return Sys.cpuTime(); + #elseif sys + return Sys.time(); + #else + return 0; + #end + } +} + +#if java +@:nativeGen +private class TimerTask extends java.util.TimerTask +{ + var timer:Timer; + + public function new(timer:Timer):Void + { + super(); + this.timer = timer; + } + + @:overload override public function run():Void + { + timer.run(); + } +} +#end +#else +import lime.system.System; + +class Timer +{ + private static var sRunningTimers:Array = []; + + private var mTime:Float; + private var mFireAt:Float; + private var mRunning:Bool; + + public function new(time:Float) + { + mTime = time; + sRunningTimers.push(this); + mFireAt = getMS() + mTime; + mRunning = true; + } + + public static function delay(f:Void->Void, time:Int) + { + var t = new Timer(time); + + t.run = function() + { + t.stop(); + f(); + }; + + return t; + } + + private static function getMS():Float + { + return System.getTimer(); + } + + public static function measure(f:Void->T, ?pos:PosInfos):T + { + var t0 = stamp(); + var r = f(); + Log.trace((stamp() - t0) + "s", pos); + return r; + } + + dynamic public function run() {} + + public static inline function stamp():Float + { + var timer = System.getTimer(); + return (timer > 0 ? timer / 1000 : 0); + } + + public function stop():Void + { + /*if (mRunning) + { + + for (i in 0...sRunningTimers.length) + { + if (sRunningTimers[i] == this) + { + sRunningTimers[i] = null; + break; + } + } + }*/ + mRunning = false; + } + + @:noCompletion private function __check(inTime:Float) + { + if (inTime >= mFireAt) + { + mFireAt += mTime; + run(); + } + } +} +#end diff --git a/source/hscript/Config.hx b/source/hscript/Config.hx index dcfcc10e..98fc7f7a 100644 --- a/source/hscript/Config.hx +++ b/source/hscript/Config.hx @@ -3,19 +3,23 @@ package hscript; class Config { // Runs support for custom classes in these public static final ALLOWED_CUSTOM_CLASSES = [ + #if !DOCUMENTATION "flixel", "funkin", + #end ]; // Runs support for abstract support in these public static final ALLOWED_ABSTRACT_AND_ENUM = [ + #if !DOCUMENTATION "flixel", "openfl", "haxe.xml", "haxe.CallStack", "funkin", + #end ]; // Incase any of your files fail diff --git a/source/lime/_internal/backend/native/NativeApplication.hx b/source/lime/_internal/backend/native/NativeApplication.hx new file mode 100644 index 00000000..63466a42 --- /dev/null +++ b/source/lime/_internal/backend/native/NativeApplication.hx @@ -0,0 +1,983 @@ +package lime._internal.backend.native; + +import haxe.Timer; +import lime._internal.backend.native.NativeCFFI; +import lime.app.Application; +import lime.graphics.opengl.GL; +import lime.graphics.OpenGLRenderContext; +import lime.graphics.RenderContext; +import lime.math.Rectangle; +import lime.media.AudioManager; +import lime.system.Clipboard; +import lime.system.Display; +import lime.system.DisplayMode; +import lime.system.JNI; +import lime.system.Sensor; +import lime.system.SensorType; +import lime.system.System; +import lime.ui.Gamepad; +import lime.ui.Joystick; +import lime.ui.JoystickHatPosition; +import lime.ui.KeyCode; +import lime.ui.KeyModifier; +import lime.ui.Touch; +import lime.ui.Window; + +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(haxe.Timer) +@:access(lime._internal.backend.native.NativeCFFI) +@:access(lime._internal.backend.native.NativeOpenGLRenderContext) +@:access(lime._internal.backend.native.NativeWindow) +@:access(lime.app.Application) +@:access(lime.graphics.opengl.GL) +@:access(lime.graphics.OpenGLRenderContext) +@:access(lime.graphics.Renderer) +@:access(lime.system.Clipboard) +@:access(lime.system.Sensor) +@:access(lime.ui.Gamepad) +@:access(lime.ui.Joystick) +@:access(lime.ui.Window) +class NativeApplication +{ + private var applicationEventInfo = new ApplicationEventInfo(UPDATE); + private var clipboardEventInfo = new ClipboardEventInfo(); + private var currentTouches = new Map(); + private var dropEventInfo = new DropEventInfo(); + private var gamepadEventInfo = new GamepadEventInfo(); + private var joystickEventInfo = new JoystickEventInfo(); + private var keyEventInfo = new KeyEventInfo(); + private var mouseEventInfo = new MouseEventInfo(); + private var renderEventInfo = new RenderEventInfo(RENDER); + private var sensorEventInfo = new SensorEventInfo(); + private var textEventInfo = new TextEventInfo(); + private var touchEventInfo = new TouchEventInfo(); + private var unusedTouchesPool = new List(); + private var windowEventInfo = new WindowEventInfo(); + + public var handle:Dynamic; + + private var pauseTimer:Int; + private var parent:Application; + private var toggleFullscreen:Bool; + + private static function __init__() + { + #if (lime_cffi && !macro) + var init = NativeCFFI; + #end + } + + public function new(parent:Application):Void + { + this.parent = parent; + pauseTimer = -1; + toggleFullscreen = true; + + AudioManager.init(); + + #if (ios || android || tvos) + Sensor.registerSensor(SensorType.ACCELEROMETER, 0); + #end + + #if (!macro && lime_cffi) + handle = NativeCFFI.lime_application_create(); + #end + } + + private function advanceTimer():Void + { + #if lime_cffi + if (pauseTimer > -1) + { + var offset = System.getTimer() - pauseTimer; + for(timer in Timer.sRunningTimers) { + if(timer != null && timer.mRunning) timer.mFireAt += offset; + } + pauseTimer = -1; + } + #end + } + + public function exec():Int + { + #if !macro + #if lime_cffi + NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo); + NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo); + NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo); + NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo); + NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo); + NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo); + NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo); + NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo); + NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo); + NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo); + NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo); + #if (ios || android || tvos) + NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo); + #end + #end + + #if (nodejs && lime_cffi) + NativeCFFI.lime_application_init(handle); + + var eventLoop = function() + { + var active = NativeCFFI.lime_application_update(handle); + + if (!active) + { + untyped process.exitCode = NativeCFFI.lime_application_quit(handle); + parent.onExit.dispatch(untyped process.exitCode); + } + else + { + untyped setImmediate(eventLoop); + } + } + + untyped setImmediate(eventLoop); + return 0; + #elseif lime_cffi + var result = NativeCFFI.lime_application_exec(handle); + + #if (!webassembly && !ios && !nodejs) + parent.onExit.dispatch(result); + #end + + return result; + #end + #end + + return 0; + } + + public function exit():Void + { + AudioManager.shutdown(); + + #if (!macro && lime_cffi) + NativeCFFI.lime_application_quit(handle); + #end + } + + private function handleApplicationEvent():Void + { + switch (applicationEventInfo.type) + { + case UPDATE: + updateTimer(); + + parent.onUpdate.dispatch(applicationEventInfo.deltaTime); + + default: + } + } + + private function handleClipboardEvent():Void + { + Clipboard.__update(); + } + + private function handleDropEvent():Void + { + for (window in parent.windows) + { + window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end); + } + } + + private function handleGamepadEvent():Void + { + switch (gamepadEventInfo.type) + { + case AXIS_MOVE: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue); + + case BUTTON_DOWN: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button); + + case BUTTON_UP: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button); + + case CONNECT: + Gamepad.__connect(gamepadEventInfo.id); + + case DISCONNECT: + Gamepad.__disconnect(gamepadEventInfo.id); + } + } + + private function handleJoystickEvent():Void + { + switch (joystickEventInfo.type) + { + case AXIS_MOVE: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x); + + case HAT_MOVE: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue); + + case TRACKBALL_MOVE: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onTrackballMove.dispatch(joystickEventInfo.index, joystickEventInfo.x, joystickEventInfo.y); + + case BUTTON_DOWN: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index); + + case BUTTON_UP: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index); + + case CONNECT: + Joystick.__connect(joystickEventInfo.id); + + case DISCONNECT: + Joystick.__disconnect(joystickEventInfo.id); + } + } + + private function handleKeyEvent():Void + { + var window = parent.__windowByID.get(keyEventInfo.windowID); + + if (window != null) + { + var type:KeyEventType = keyEventInfo.type; + var int32:Float = keyEventInfo.keyCode; + var keyCode:KeyCode = Std.int(int32); + var modifier:KeyModifier = keyEventInfo.modifier; + + switch (type) + { + case KEY_DOWN: + window.onKeyDown.dispatch(keyCode, modifier); + + case KEY_UP: + window.onKeyUp.dispatch(keyCode, modifier); + } + + #if (windows || linux) + if (keyCode == RETURN) + { + if (type == KEY_DOWN) + { + if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey)) + { + toggleFullscreen = false; + + if (!window.onKeyDown.canceled) + { + window.fullscreen = !window.fullscreen; + } + } + } + else + { + toggleFullscreen = true; + } + } + + #if rpi + if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled) + { + System.exit(0); + } + #end + #elseif mac + if (keyCode == F) + { + if (type == KEY_DOWN) + { + if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey)) + { + toggleFullscreen = false; + + if (!window.onKeyDown.canceled) + { + window.fullscreen = !window.fullscreen; + } + } + } + else + { + toggleFullscreen = true; + } + } + #elseif android + if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled) + { + var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;"); + var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z"); + + moveTaskToBack(mainActivity.get(), true); + } + #end + } + } + + private function handleMouseEvent():Void + { + var window = parent.__windowByID.get(mouseEventInfo.windowID); + + if (window != null) + { + switch (mouseEventInfo.type) + { + case MOUSE_DOWN: + window.clickCount = mouseEventInfo.clickCount; + window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button); + window.clickCount = 0; + + case MOUSE_UP: + window.clickCount = mouseEventInfo.clickCount; + window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button); + window.clickCount = 0; + + case MOUSE_MOVE: + window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y); + window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY); + + case MOUSE_WHEEL: + window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN); + + default: + } + } + } + + private function handleRenderEvent():Void + { + // TODO: Allow windows to render independently + + for (window in parent.__windows) + { + if (window == null) continue; + + // parent.renderer = renderer; + + switch (renderEventInfo.type) + { + case RENDER: + if (window.context != null) + { + window.__backend.render(); + window.onRender.dispatch(window.context); + + if (!window.onRender.canceled) + { + window.__backend.contextFlip(); + } + } + + case RENDER_CONTEXT_LOST: + if (window.__backend.useHardware && window.context != null) + { + switch (window.context.type) + { + case OPENGL, OPENGLES, WEBGL: + #if (lime_cffi && (lime_opengl || lime_opengles) && !display) + var gl = window.context.gl; + (gl : NativeOpenGLRenderContext).__contextLost(); + if (GL.context == gl) GL.context = null; + #end + + default: + } + + window.context = null; + window.onRenderContextLost.dispatch(); + } + + case RENDER_CONTEXT_RESTORED: + if (window.__backend.useHardware) + { + // GL.context = new OpenGLRenderContext (); + // window.context.gl = GL.context; + + window.onRenderContextRestored.dispatch(window.context); + } + } + } + } + + private function handleSensorEvent():Void + { + var sensor = Sensor.sensorByID.get(sensorEventInfo.id); + + if (sensor != null) + { + sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z); + } + } + + private function handleTextEvent():Void + { + var window = parent.__windowByID.get(textEventInfo.windowID); + + if (window != null) + { + switch (textEventInfo.type) + { + case TEXT_INPUT: + window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end); + + case TEXT_EDIT: + window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start, + textEventInfo.length); + + default: + } + } + } + + private function handleTouchEvent():Void + { + switch (touchEventInfo.type) + { + case TOUCH_START: + var touch = unusedTouchesPool.pop(); + + if (touch == null) + { + touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure, + touchEventInfo.device); + } + else + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.id = touchEventInfo.id; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + touch.device = touchEventInfo.device; + } + + currentTouches.set(touch.id, touch); + + Touch.onStart.dispatch(touch); + + case TOUCH_END: + var touch = currentTouches.get(touchEventInfo.id); + + if (touch != null) + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + + Touch.onEnd.dispatch(touch); + + currentTouches.remove(touchEventInfo.id); + unusedTouchesPool.add(touch); + } + + case TOUCH_MOVE: + var touch = currentTouches.get(touchEventInfo.id); + + if (touch != null) + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + + Touch.onMove.dispatch(touch); + } + + default: + } + } + + private function handleWindowEvent():Void + { + var window = parent.__windowByID.get(windowEventInfo.windowID); + + if (window != null) + { + switch (windowEventInfo.type) + { + case WINDOW_ACTIVATE: + advanceTimer(); + window.onActivate.dispatch(); + AudioManager.resume(); + + case WINDOW_CLOSE: + window.close(); + + case WINDOW_DEACTIVATE: + window.onDeactivate.dispatch(); + AudioManager.suspend(); + pauseTimer = System.getTimer(); + + case WINDOW_ENTER: + window.onEnter.dispatch(); + + case WINDOW_EXPOSE: + window.onExpose.dispatch(); + + case WINDOW_FOCUS_IN: + window.onFocusIn.dispatch(); + + case WINDOW_FOCUS_OUT: + window.onFocusOut.dispatch(); + + case WINDOW_LEAVE: + window.onLeave.dispatch(); + + case WINDOW_MAXIMIZE: + window.__maximized = true; + window.__fullscreen = false; + window.__minimized = false; + window.onMaximize.dispatch(); + + case WINDOW_MINIMIZE: + window.__minimized = true; + window.__maximized = false; + window.__fullscreen = false; + window.onMinimize.dispatch(); + + case WINDOW_MOVE: + window.__x = windowEventInfo.x; + window.__y = windowEventInfo.y; + window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y); + + case WINDOW_RESIZE: + window.__width = windowEventInfo.width; + window.__height = windowEventInfo.height; + window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height); + + case WINDOW_RESTORE: + window.__fullscreen = false; + window.__minimized = false; + window.onRestore.dispatch(); + + case WINDOW_SHOW: + window.onShow.dispatch(); + + case WINDOW_HIDE: + window.onHide.dispatch(); + } + } + } + + private function updateTimer():Void + { + #if lime_cffi + if (Timer.sRunningTimers.length > 0) + { + var currentTime = System.getTimer(); + var foundNull = false; + var timer; + + for (i in 0...Timer.sRunningTimers.length) + { + timer = Timer.sRunningTimers[i]; + + if (timer != null && timer.mRunning) + { + if (currentTime >= timer.mFireAt) + { + timer.mFireAt += timer.mTime; + timer.run(); + } + } + else + { + foundNull = true; + } + } + + if (foundNull) + { + Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val) + { + return val != null && val.mRunning; + }); + } + } + + #if (haxe_ver >= 4.2) + #if target.threaded + sys.thread.Thread.current().events.progress(); + #else + // Duplicate code required because Haxe 3 can't handle + // #if (haxe_ver >= 4.2 && target.threaded) + @:privateAccess haxe.EntryPoint.processEvents(); + #end + #else + @:privateAccess haxe.EntryPoint.processEvents(); + #end + #end + } +} + +@:keep /*private*/ class ApplicationEventInfo +{ + public var deltaTime:Int; + public var type:ApplicationEventType; + + public function new(type:ApplicationEventType = null, deltaTime:Int = 0) + { + this.type = type; + this.deltaTime = deltaTime; + } + + public function clone():ApplicationEventInfo + { + return new ApplicationEventInfo(type, deltaTime); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int) +{ + var UPDATE = 0; + var EXIT = 1; +} + +@:keep /*private*/ class ClipboardEventInfo +{ + public var type:ClipboardEventType; + + public function new(type:ClipboardEventType = null) + { + this.type = type; + } + + public function clone():ClipboardEventInfo + { + return new ClipboardEventInfo(type); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int) +{ + var UPDATE = 0; +} + +@:keep /*private*/ class DropEventInfo +{ + public var file:#if hl hl.Bytes #else String #end; + public var type:DropEventType; + + public function new(type:DropEventType = null, file = null) + { + this.type = type; + this.file = file; + } + + public function clone():DropEventInfo + { + return new DropEventInfo(type, file); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int) +{ + var DROP_FILE = 0; +} + +@:keep /*private*/ class GamepadEventInfo +{ + public var axis:Int; + public var button:Int; + public var id:Int; + public var type:GamepadEventType; + public var axisValue:Float; + + public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0) + { + this.type = type; + this.id = id; + this.button = button; + this.axis = axis; + this.axisValue = value; + } + + public function clone():GamepadEventInfo + { + return new GamepadEventInfo(type, id, button, axis, axisValue); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int) +{ + var AXIS_MOVE = 0; + var BUTTON_DOWN = 1; + var BUTTON_UP = 2; + var CONNECT = 3; + var DISCONNECT = 4; +} + +@:keep /*private*/ class JoystickEventInfo +{ + public var id:Int; + public var index:Int; + public var type:JoystickEventType; + public var eventValue:Int; + public var x:Float; + public var y:Float; + + public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0) + { + this.type = type; + this.id = id; + this.index = index; + this.eventValue = value; + this.x = x; + this.y = y; + } + + public function clone():JoystickEventInfo + { + return new JoystickEventInfo(type, id, index, eventValue, x, y); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int) +{ + var AXIS_MOVE = 0; + var HAT_MOVE = 1; + var TRACKBALL_MOVE = 2; + var BUTTON_DOWN = 3; + var BUTTON_UP = 4; + var CONNECT = 5; + var DISCONNECT = 6; +} + +@:keep /*private*/ class KeyEventInfo +{ + public var keyCode: Float; + public var modifier:Int; + public var type:KeyEventType; + public var windowID:Int; + + public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.keyCode = keyCode; + this.modifier = modifier; + } + + public function clone():KeyEventInfo + { + return new KeyEventInfo(type, windowID, keyCode, modifier); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int) +{ + var KEY_DOWN = 0; + var KEY_UP = 1; +} + +@:keep /*private*/ class MouseEventInfo +{ + public var button:Int; + public var movementX:Float; + public var movementY:Float; + public var type:MouseEventType; + public var windowID:Int; + public var x:Float; + public var y:Float; + public var clickCount:Int; + + public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0) + { + this.type = type; + this.windowID = 0; + this.x = x; + this.y = y; + this.button = button; + this.movementX = movementX; + this.movementY = movementY; + this.clickCount = clickCount; + } + + public function clone():MouseEventInfo + { + return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int) +{ + var MOUSE_DOWN = 0; + var MOUSE_UP = 1; + var MOUSE_MOVE = 2; + var MOUSE_WHEEL = 3; +} + +@:keep /*private*/ class RenderEventInfo +{ + public var type:RenderEventType; + + public function new(type:RenderEventType = null) + { + this.type = type; + } + + public function clone():RenderEventInfo + { + return new RenderEventInfo(type); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int) +{ + var RENDER = 0; + var RENDER_CONTEXT_LOST = 1; + var RENDER_CONTEXT_RESTORED = 2; +} + +@:keep /*private*/ class SensorEventInfo +{ + public var id:Int; + public var x:Float; + public var y:Float; + public var z:Float; + public var type:SensorEventType; + + public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0) + { + this.type = type; + this.id = id; + this.x = x; + this.y = y; + this.z = z; + } + + public function clone():SensorEventInfo + { + return new SensorEventInfo(type, id, x, y, z); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int) +{ + var ACCELEROMETER = 0; +} + +@:keep /*private*/ class TextEventInfo +{ + public var id:Int; + public var length:Int; + public var start:Int; + public var text:#if hl hl.Bytes #else String #end; + public var type:TextEventType; + public var windowID:Int; + + public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.text = text; + this.start = start; + this.length = length; + } + + public function clone():TextEventInfo + { + return new TextEventInfo(type, windowID, text, start, length); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int) +{ + var TEXT_INPUT = 0; + var TEXT_EDIT = 1; +} + +@:keep /*private*/ class TouchEventInfo +{ + public var device:Int; + public var dx:Float; + public var dy:Float; + public var id:Int; + public var pressure:Float; + public var type:TouchEventType; + public var x:Float; + public var y:Float; + + public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0) + { + this.type = type; + this.x = x; + this.y = y; + this.id = id; + this.dx = dx; + this.dy = dy; + this.pressure = pressure; + this.device = device; + } + + public function clone():TouchEventInfo + { + return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int) +{ + var TOUCH_START = 0; + var TOUCH_END = 1; + var TOUCH_MOVE = 2; +} + +@:keep /*private*/ class WindowEventInfo +{ + public var height:Int; + public var type:WindowEventType; + public var width:Int; + public var windowID:Int; + public var x:Int; + public var y:Int; + + public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.width = width; + this.height = height; + this.x = x; + this.y = y; + } + + public function clone():WindowEventInfo + { + return new WindowEventInfo(type, windowID, width, height, x, y); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int) +{ + var WINDOW_ACTIVATE = 0; + var WINDOW_CLOSE = 1; + var WINDOW_DEACTIVATE = 2; + var WINDOW_ENTER = 3; + var WINDOW_EXPOSE = 4; + var WINDOW_FOCUS_IN = 5; + var WINDOW_FOCUS_OUT = 6; + var WINDOW_LEAVE = 7; + var WINDOW_MAXIMIZE = 8; + var WINDOW_MINIMIZE = 9; + var WINDOW_MOVE = 10; + var WINDOW_RESIZE = 11; + var WINDOW_RESTORE = 12; + var WINDOW_SHOW = 13; + var WINDOW_HIDE = 14; +}