Merge branch 'main' into new-editors
This commit is contained in:
+3
-3
@@ -13,7 +13,7 @@ _**QOL = Quality of Life**_
|
||||
- Downscroll
|
||||
- Ghost Tapping
|
||||
- Opponent & Co-op modes
|
||||
- Memory optimisation (most of the game runs < 500mb)
|
||||
- Memory optimization (most of the game runs < 500mb)
|
||||
- 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)
|
||||
@@ -36,7 +36,7 @@ _**QOL = Quality of Life**_
|
||||
- Support for `hscript-improved`, a fork made to allow HScript to push modding even further
|
||||
- Allows for imports
|
||||
- Allows for public variables (variables shared between every script in a ScriptPack such as a song scripts)
|
||||
- Allows for static variables (variabels shared between every single script ran in this mod)
|
||||
- Allows for static variables (variables shared between every single script ran in this mod)
|
||||
- Allows you to use for example `boyfriend` instead of `PlayState.boyfriend` or `game.boyfriend`, for smaller and easier to comprehend code.
|
||||
- Allows you to use `@:bypassAccessor`
|
||||
- Allows you to use maps
|
||||
@@ -59,7 +59,7 @@ _**QOL = Quality of Life**_
|
||||
- Position them
|
||||
- Add animations
|
||||
- Change their scale
|
||||
- Change their scrollfactor
|
||||
- Change their scroll factor
|
||||
- Change their zoomfactor
|
||||
- Turn on/off their antialiasing (on by default)
|
||||
- Change additional properties via child nodes.
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
- New SEXY editor assets by paige and nex!!!! (and lunar, does 16x16 even count??? 😭)
|
||||
- NEW EVENTS (Camera events, play animation, scroll speed change)
|
||||
- Events window is now scrollable (more events, more room)
|
||||
- Custom Properities in Meta AND Character (Editable in Charter and Character Editor :D) (accessible with SONG.meta.customValues.[property] and [character].extras.[property])
|
||||
- Custom Properties in Meta AND Character (Editable in Charter and Character Editor :D) (accessible with SONG.meta.customValues.[property] and [character].extras.[property])
|
||||
- SMALLER BUT STILL COOL
|
||||
- Multiple Icons now show up on strumlines with multiple characters in the charter
|
||||
- Note hoverer to show where your note is going to be placed
|
||||
@@ -41,9 +41,9 @@
|
||||
- Reworked Lagless Credits Codename Engine's Contributors Menu
|
||||
- New Credit Method for GitHub accounts (also lagless) in Mod Credits (First gets the data normally, if fails uses the ratelimited api)
|
||||
- MANY OG GAME WEEK STUFF!! (still wip and softcoded and also many reworks by nex!)
|
||||
- Senpai Cutscene got reworked! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/3b3f32fc-78d1-40fa-8398-776554cab1d6)
|
||||
- Thorns got reworked with a cool chromatic aberration effect! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/73d9f7c5-1c9c-407e-baf0-e8e2c381ecbb)
|
||||
- Winter Horrorland got reworked with a bloody vignette effect! (https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/1e10c3e2-32dc-4de2-b31a-d9d577d516da - https://github.com/FNF-CNE-Devs/CodenameEngine/assets/87421482/cad852c3-1ee2-409e-a70e-b3571ab5cf7b)
|
||||
- Senpai Cutscene got reworked! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/3b3f32fc-78d1-40fa-8398-776554cab1d6)
|
||||
- Thorns got reworked with a cool chromatic aberration effect! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/73d9f7c5-1c9c-407e-baf0-e8e2c381ecbb)
|
||||
- Winter Horrorland got reworked with a bloody vignette effect! (https://github.com/CodenameCrew/CodenameEngine/assets/87421482/1e10c3e2-32dc-4de2-b31a-d9d577d516da - https://github.com/CodenameCrew/CodenameEngine/assets/87421482/cad852c3-1ee2-409e-a70e-b3571ab5cf7b)
|
||||
- SMALLER BUT STILL COOL
|
||||
- Ability to set Controls.hx variables (Being able to block inputs or even trigger them (needs to be in an if statement))
|
||||
- You can now easily check if a cutscene was played (by default is disabled and makes it auto play or not; Check startCutscene() code for more details in PlayState)
|
||||
@@ -63,7 +63,7 @@
|
||||
- Fix for pressing F5 in an editor breaking it
|
||||
- Fix when getting GitHub data and a redirect happens
|
||||
- Fix for healthbar incorrectly colored in opponent mode
|
||||
- Video cutscenes are ALOT more stable (thanks majigsaw :D)
|
||||
- Video cutscenes are A LOT more stable (thanks majigsaw :D)
|
||||
- 3D now works again (so cool i love away3d in 2024)
|
||||
- Lots and lots of grammar and wording fixes (so true bro -lunar)
|
||||
- Lots and lots of other optimizations made
|
||||
@@ -76,5 +76,5 @@
|
||||
- Some other shit idk ill add it whenever someone tells me backward compat is broke 💔
|
||||
</details>
|
||||
|
||||
thanks so much for the support and always being there, it really does mean alot to us < 3
|
||||
thanks so much for the support and always being there, it really does mean a lot to us < 3
|
||||
we are one step closer to turning beta so we really can't wait !
|
||||
@@ -12,7 +12,7 @@ Also, the command `.\cne test` uses the source assets folder instead of the expo
|
||||
## Codename Engine
|
||||
|
||||
Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying modding, along with extensiblity and ease of use.<br>
|
||||
### Before making issues or need help with something, check our website [HERE](https://fnf-cne-devs.github.io/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!!
|
||||
### Before making issues or need help with something, check our website [HERE](https://codename-engine.com/) (it contains a wiki of how to mod with EXAMPLES, an api, lists of mods made with Codename Engine and more)!!!
|
||||
#### The Engine includes many new features, as seen [here](FEATURES.md)<br>
|
||||
#### Wanna see the new features added in the most recent update? Click [here](PATCHNOTES.md)<br>
|
||||
|
||||
@@ -69,7 +69,7 @@ If you don't have a github account please go onto https://fnf-cne-devs.github.io
|
||||
- Download and play the engine with its mods and modpacks
|
||||
- Mod and fork the engine (without using it for illicit purposes)
|
||||
- Contribute to the engine (for example through *Pull Requests*, *Issues*, etc)
|
||||
- Create a sub engine with Codename Engine as **TEMPLATE** with **CREDITS** (for example leaving the *credits menu submenu with the GitHub contributors* and putting the *[main devs](https://github.com/FNF-CNE-Devs)* in a *README* specifying that it's a *sub engine from Codename Engine*)
|
||||
- Create a sub engine with Codename Engine as **TEMPLATE** with **CREDITS** (for example leaving the *credits menu submenu with the GitHub contributors* and putting the *[main devs](https://github.com/CodenameCrew)* in a *README* specifying that it's a *sub engine from Codename Engine*)
|
||||
- Release excutable mods that use Codename Engine as source (Specifing that uses Codename Engine by for example the same way written above this)
|
||||
- Release modpacks
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
- [ ] BPM Changes Events
|
||||
- [ ] Character Editor
|
||||
- [x] Animation Editing
|
||||
- [x] Animation Deletion/Creationg
|
||||
- [x] Animation Deletion/Creation
|
||||
- [x] Character Info Editing
|
||||
- [x] Character Saving
|
||||
- [x] Add Undos to Actions
|
||||
@@ -49,7 +49,7 @@
|
||||
- [ ] Fix bugs
|
||||
|
||||
## `v1.1.0`
|
||||
- [ ] Charter Waveforms (maybe in v1.1.0, dont wanna slow shit down)
|
||||
- [ ] Charter Waveforms (maybe in v1.1.0, don't wanna slow shit down)
|
||||
- [ ] Custom classes
|
||||
- [ ] Proper super overrides (FlxText_HSX doesn't allow overrides for update because of this)
|
||||
- [ ] Custom Class extending Custom Classes
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
@ECHO OFF
|
||||
cd ..
|
||||
echo Building Game...
|
||||
lime build windows --haxeflag="--macro include('scripting')" --haxeflag="-xml docs/doc.xml" -D DOCUMENTATION --no-output
|
||||
lime build windows --haxeflag="-xml docs/doc.xml" -D doc-gen -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
|
||||
echo Please put this in codename-website/api-generator/api/doc.xml
|
||||
@@ -7,7 +7,7 @@ function postCreate() {
|
||||
}
|
||||
|
||||
var toAdd:Bool = true; // Using this just to make sure
|
||||
function update(elpased) {
|
||||
function update(elapsed) {
|
||||
if(toAdd) {
|
||||
toAdd = false;
|
||||
PlayState.instance.insert(PlayState.instance.members.indexOf(self), trail);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var loopedTimah:FlxTimer; // Hey! a man has fallen into the river in lego city! and if youre asking why this is here, you shall disappear! - Nex
|
||||
var loopedTimer:FlxTimer; // Hey! a man has fallen into the river in lego city! and if youre asking why this is here, you shall disappear! - Nex
|
||||
var bgFade:FlxSprite;
|
||||
var hand:FlxSprite;
|
||||
|
||||
@@ -9,7 +9,7 @@ function postCreate() {
|
||||
bgFade.alpha = 0;
|
||||
cutscene.insert(0, bgFade);
|
||||
|
||||
loopedTimah = new FlxTimer().start(0.83, function(tmr:FlxTimer)
|
||||
loopedTimer = new FlxTimer().start(0.83, function(tmr:FlxTimer)
|
||||
{
|
||||
bgFade.alpha += (1 / 5) * 0.7;
|
||||
if (bgFade.alpha > 0.7)
|
||||
@@ -30,8 +30,8 @@ function close(event) {
|
||||
cutscene.curMusic?.fadeOut(1, 0);
|
||||
for(c in cutscene.charMap) c.visible = false;
|
||||
|
||||
loopedTimah.cancel();
|
||||
loopedTimah = new FlxTimer().start(0.2, function(tmr:FlxTimer)
|
||||
loopedTimer.cancel();
|
||||
loopedTimer = new FlxTimer().start(0.2, function(tmr:FlxTimer)
|
||||
{
|
||||
if (tmr.elapsedLoops <= 5) {
|
||||
cutscene.dialogueBox.alpha -= 1 / 5;
|
||||
|
||||
@@ -11,7 +11,7 @@ function create() {
|
||||
}
|
||||
|
||||
var adder:Int = 0;
|
||||
function postUpdate(elapsed:Float) { // Doing it like this so at least the red bump doesnt look too much instant
|
||||
function postUpdate(elapsed:Float) { // Doing it like this so at least the red bump doesn't look too much instant
|
||||
var toAdd:Int = lerp(adder, 0, elapsed);
|
||||
idk.amount = camGame.zoom * camHUD.zoom + toAdd;
|
||||
idk.strength = (camGame.zoom - camHUD.zoom + toAdd) * 5;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
antialiasing="false" scale="4.797" scroll="0.9" updateHitbox="true" type="loop"/>
|
||||
|
||||
<sprite name="bgTrees" x="-590" y="-800" sprite="weebTrees" antialiasing="false" scale="5.102" scroll="0.85" updateHitbox="true" type="loop">
|
||||
<!-- Frame 19 isnt used in og -->
|
||||
<!-- Frame 19 isn't used in og -->
|
||||
<anim name="trees" anim="trees" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19" fps="12" loop="true"/>
|
||||
</sprite>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public function lightningStrikeShit():Void
|
||||
lightningStrikeBeat = curBeat;
|
||||
lightningOffset = FlxG.random.int(8, 24);
|
||||
|
||||
boyfriend.playAnim('scared', true, "SING"); // SING so that they dont get indefinitely looped
|
||||
boyfriend.playAnim('scared', true, "SING"); // SING so that they don't get indefinitely looped
|
||||
gf.playAnim('scared', true, "SING");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<dad x="20" y="160"/>
|
||||
<boyfriend x="810"/>
|
||||
|
||||
<!-- FOREGROUND TANKMANS -->
|
||||
<!-- FOREGROUND TANKMENs -->
|
||||
|
||||
<high-memory>
|
||||
<sprite name="tank0" x="-500" y="650" sprite="tank0" scrollx="1.7" scrolly="1.5" type="onbeat" zoomfactor="1.25"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# THIS FILE ISNT NECESSARY,
|
||||
# THIS FILE ISN'T NECESSARY,
|
||||
# BUT YOU CAN USE IT TO SORT WEEKS
|
||||
|
||||
tutorial
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Here are guides on common mistakes and advices for writting shaders for flixel and openfl.
|
||||
## Here are guides on common mistakes and advices for writing shaders for flixel and openfl.
|
||||
|
||||
It's important to follow this guide as most shader coders make these mistakes a lot and end up with shaders that only work on certain devices.
|
||||
|
||||
@@ -51,7 +51,7 @@ We will show each step to converting this shader into an usable openfl/flixel sh
|
||||
|
||||
1. replace ``void mainImage( out vec4 fragColor, in vec2 fragCoord )`` with a simple ``void main()`` (flixel and openfl do not have any parameters since the coords are defined outside the function)
|
||||
2. replace ``vec2 uv = fragCoord/iResolution.xy;`` with ``vec2 uv = openfl_TextureCoordv`` (or in some cases, add ``vec2 fragCoord = openfl_TextureCoordv*openfl_TextureSize.xy`` before it and replace `iResolution.xy` with ``openfl_TextureSize.xy`` (if the uv value is different than usual))
|
||||
3. replace ``vec4 col = texture(iChannel0, uv);`` with ``vec4 col = texture2D(bitmap, uv);`` or ``vec4 col = flixel_texture2D(bitmap, uv);`` (keep in mind bitmap is the pixels of the camera/sprite the shader is aplied to)
|
||||
3. replace ``vec4 col = texture(iChannel0, uv);`` with ``vec4 col = texture2D(bitmap, uv);`` or ``vec4 col = flixel_texture2D(bitmap, uv);`` (keep in mind bitmap is the pixels of the camera/sprite the shader is applied to)
|
||||
4. finally, replace ``fragColor = col;`` with ``gl_FragColor = col;``
|
||||
|
||||
###### Keep in mind that this covers the most basic on how to convert a shader from shadertoy.com, complex shaders will need more expertise with handling shaders before converting those.
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0);
|
||||
const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0);
|
||||
|
||||
const vec4 outlinecolor = vec4(0.1);
|
||||
const vec3 lowdetailcol = vec3(0.0, 0.0, 1.0);
|
||||
const vec4 outlineColor = vec4(0.1);
|
||||
const vec3 lowDetailCol = vec3(0.0, 0.0, 1.0);
|
||||
|
||||
uniform vec2 textureRes;
|
||||
uniform float pixelOffset;
|
||||
@@ -42,28 +42,28 @@ bool inWaveForm(vec2 pixel, float width) {
|
||||
return pixel.x > widthdiv2 && pixel.x < textureRes.x-widthdiv2;
|
||||
}
|
||||
|
||||
float getHightlight(float ampwidth) {
|
||||
float remapCoord = (ampwidth/textureRes.x)/2.0;
|
||||
float getHighlight(float ampWidth) {
|
||||
float remapCoord = (ampWidth/textureRes.x)/2.0;
|
||||
float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.0-remapCoord, 0.0, 1.0);
|
||||
return 1.0-(abs(mappedCoord-0.5)*2.0);
|
||||
}
|
||||
|
||||
void outlineWaveform(vec2 pixel, vec2 offset) {
|
||||
float amplitude = getAmplitude(pixel - offset);
|
||||
float ampwidth = (1.0-amplitude) * textureRes.x;
|
||||
float ampWidth = (1.0-amplitude) * textureRes.x;
|
||||
|
||||
if (!inWaveForm(pixel, ampwidth))
|
||||
gl_FragColor -= outlinecolor*mix(0.5, 0.8, amplitude);
|
||||
if (!inWaveForm(pixel, ampWidth))
|
||||
gl_FragColor -= outlineColor*mix(0.5, 0.8, amplitude);
|
||||
}
|
||||
|
||||
void highDetailWaveform(vec2 pixel, float amplitude, float ampwidth) {
|
||||
void highDetailWaveform(vec2 pixel, float amplitude, float ampWidth) {
|
||||
vec3 gradientColor = mix(gradient1, gradient2, openfl_TextureCoordv.x);
|
||||
gradientColor *= pixel.y+pixelOffset>playerPosition ? 0.7 : 1.0;
|
||||
gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(getHightlight(amplitude), 0.0, 1.0, 0.5, 1.0));
|
||||
gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(getHighlight(amplitude), 0.0, 1.0, 0.5, 1.0));
|
||||
|
||||
float ampwidthHighlight = getAmpWidth(amplitude/3.0);
|
||||
if (inWaveForm(pixel, ampwidthHighlight))
|
||||
gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(getHightlight(ampwidthHighlight), 0.0, 1.0, 0.5, 1.0));
|
||||
float ampWidthHighlight = getAmpWidth(amplitude/3.0);
|
||||
if (inWaveForm(pixel, ampWidthHighlight))
|
||||
gradientColor = mix(gradientColor, gradientColor * vec3(1.8), map(getHighlight(ampWidthHighlight), 0.0, 1.0, 0.5, 1.0));
|
||||
|
||||
gl_FragColor = vec4(vec3(gradientColor * mix(0.5, 0.8, amplitude))*openfl_Alphav, openfl_Alphav);
|
||||
|
||||
@@ -71,19 +71,19 @@ void highDetailWaveform(vec2 pixel, float amplitude, float ampwidth) {
|
||||
outlineWaveform(pixel, vec2(0.0, 1.0));
|
||||
}
|
||||
|
||||
void lowDetailWaveform(vec2 pixel, float amplitude, float ampwidth) {
|
||||
gl_FragColor = vec4(lowdetailcol * (pixel.y+pixelOffset>playerPosition ? 0.7 : 1.0), 1.0);
|
||||
void lowDetailWaveform(vec2 pixel, float amplitude, float ampWidth) {
|
||||
gl_FragColor = vec4(lowDetailCol * (pixel.y+pixelOffset>playerPosition ? 0.7 : 1.0), 1.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pixel = openfl_TextureCoordv * textureRes;
|
||||
float amplitude = getAmplitude(pixel);
|
||||
float ampwidth = getAmpWidth(amplitude);
|
||||
float ampWidth = getAmpWidth(amplitude);
|
||||
|
||||
gl_FragColor = vec4(0.0);
|
||||
if (!inWaveForm(pixel, ampwidth)) return;
|
||||
if (!inWaveForm(pixel, ampWidth)) return;
|
||||
|
||||
if (lowDetail) lowDetailWaveform(pixel, amplitude, ampwidth);
|
||||
else highDetailWaveform(pixel, amplitude, ampwidth);
|
||||
if (lowDetail) lowDetailWaveform(pixel, amplitude, ampWidth);
|
||||
else highDetailWaveform(pixel, amplitude, ampWidth);
|
||||
}
|
||||
+3
-3
@@ -41,7 +41,7 @@ class Main {
|
||||
dDoc: [
|
||||
"Usage: test <optional args>",
|
||||
"",
|
||||
"This will create a quick debug build binded to the source then run it, which means:",
|
||||
"This will create a quick debug build connected to the source then run it, which means:",
|
||||
"- The assets WON'T be copied over - Assets will be read from the game's source.",
|
||||
"- This build WON'T be ready for release - Running anywhere else than in the bin folder will result in a crash from missing assets",
|
||||
"- This build will also use the mods folder from the source directory.",
|
||||
@@ -60,7 +60,7 @@ class Main {
|
||||
dDoc: [
|
||||
"Usage: build <optional arguments>",
|
||||
"",
|
||||
"This will create a quick debug build binded to the source then run it, which means:",
|
||||
"This will create a quick debug build connected to the source then run it, which means:",
|
||||
"- The assets WON'T be copied over - Assets will be read from the game's source.",
|
||||
"- This build WON'T be ready for release - Running anywhere else than in the bin folder will result in a crash from missing assets",
|
||||
"- This build will also use the mods folder from the source directory.",
|
||||
@@ -121,7 +121,7 @@ class Main {
|
||||
"",
|
||||
"This will optimize a JSON file, which means it will remove all unnecessary spacing from the file.",
|
||||
"WARNING: Order might be lost.",
|
||||
"WARNING: Comments arent supported.",
|
||||
"WARNING: Comments aren't supported.",
|
||||
"",
|
||||
"-O | --no-old : No Old file will be created.",
|
||||
].join("\n")
|
||||
|
||||
@@ -271,13 +271,13 @@ class Setup {
|
||||
prettyPrint("Installing Microsoft Visual Studio Community (Dependency)");
|
||||
|
||||
// thanks to @crowplexus for these two lines! - Nex
|
||||
Sys.command("curl -# -O https://download.visualstudio.microsoft.com/download/pr/3105fcfe-e771-41d6-9a1c-fc971e7d03a7/8eb13958dc429a6e6f7e0d6704d43a55f18d02a253608351b6bf6723ffdaf24e/vs_Community.exe");
|
||||
Sys.command("vs_Community.exe --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.19041 -p");
|
||||
command("curl -# -O https://download.visualstudio.microsoft.com/download/pr/3105fcfe-e771-41d6-9a1c-fc971e7d03a7/8eb13958dc429a6e6f7e0d6704d43a55f18d02a253608351b6bf6723ffdaf24e/vs_Community.exe");
|
||||
command("vs_Community.exe --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.19041 -p");
|
||||
|
||||
FileSystem.deleteFile("vs_Community.exe");
|
||||
prettyPrint('If it didn\'t say it before: Because of this component if you want to compile you have to restart the device.');
|
||||
Sys.print("Do you wish to do it now [y/n]? ");
|
||||
if(Sys.stdin().readLine().toLowerCase() == "y") Sys.command("shutdown /r /t 0 /f");
|
||||
if(Sys.stdin().readLine().toLowerCase() == "y") command("shutdown /r /t 0 /f");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
<print pretty="true">Preparing installation...</print>
|
||||
|
||||
<!-- OpenFL & Lime (Required for Flixel) -->
|
||||
<git name="openfl" url="https://github.com/FNF-CNE-Devs/openfl" />
|
||||
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" />
|
||||
<lib name="lime" version="8.1.2" />
|
||||
<!-- <git name="lime" url="https://github.com/FNF-CNE-Devs/lime" /> disabled for now until fixed -->
|
||||
<!-- <git name="lime" url="https://github.com/CodenameCrew/cne-lime" /> disabled for now until fixed -->
|
||||
|
||||
<!-- Flixel -->
|
||||
<git name="flixel" url="https://github.com/FNF-CNE-Devs/flixel" />
|
||||
<git name="flixel-addons" url="https://github.com/FNF-CNE-Devs/flixel-addons" />
|
||||
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" />
|
||||
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" />
|
||||
|
||||
<!-- Other Libraries -->
|
||||
<git name="hscript-improved" url="https://github.com/FNF-CNE-Devs/hscript-improved" ref="codename-dev" />
|
||||
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/FNF-CNE-Devs/hxdiscord_rpc" skipDeps="true" />
|
||||
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" />
|
||||
<git name="flxanimate" url="https://github.com/CodenameCrew/cne-flxanimate" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
|
||||
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
|
||||
|
||||
<!-- Documentation and other features -->
|
||||
<git name="away3d" url="https://github.com/FNF-CNE-Devs/away3d" />
|
||||
<git name="funkin.vis" url="https://github.com/FNF-CNE-Devs/funkVis" />
|
||||
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
|
||||
<git name="funkin.vis" url="https://github.com/CodenameCrew/cne-funkVis" />
|
||||
<git name="grig.audio" url="https://gitlab.com/haxe-grig/grig.audio.git" ref="57f5d47f2533fd0c3dcd025a86cb86c0dfa0b6d2" />
|
||||
<!-- <lib name="dox" /> -->
|
||||
<lib name="markdown" />
|
||||
@@ -29,10 +29,7 @@
|
||||
<lib global="true" name="hxp" /> <lib name="hxp" />
|
||||
<lib global="true" name="format" /> <lib name="format" />
|
||||
|
||||
<!-- Extras -->
|
||||
<!-- TODO: Remove this when hxcpp is updated -->
|
||||
<!-- Adds a progress counter to compilation -->
|
||||
<git name="hxcpp" url="https://github.com/NeeEoo/hxcpp" ref="codename" />
|
||||
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" />
|
||||
</if>
|
||||
|
||||
<!-- Hxcpp Building -->
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@
|
||||
<!-- Comment this out to disable support for custom ndlls. !-->
|
||||
<haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl" />
|
||||
|
||||
<!-- Comment this out to disable support for Away3D Flixel Intergration (reduces compile times) !-->
|
||||
<!-- Comment this out to disable support for Away3D Flixel Integration (reduces compile times) !-->
|
||||
<define name="THREE_D_SUPPORT" />
|
||||
|
||||
<!-- Comment this out to disable support for Funkin Audio Visualizer (reduces compile times) !-->
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ class Xml {
|
||||
|
||||
/**
|
||||
Removes a child from the Document or Element.
|
||||
Returns true if the child was successfuly removed.
|
||||
Returns true if the child was successfully removed.
|
||||
**/
|
||||
public function removeChild(x:Xml):Bool {
|
||||
ensureElementType();
|
||||
|
||||
@@ -7,7 +7,7 @@ import flixel.FlxG;
|
||||
#end
|
||||
|
||||
/**
|
||||
* @author lunarclient
|
||||
* @author lunarcleint
|
||||
* @see https://twitter.com/lunarcleint
|
||||
*
|
||||
* Help from Ne_Eo
|
||||
|
||||
@@ -3,15 +3,20 @@ package funkin.backend;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxMatrix;
|
||||
import openfl.display.BlendMode;
|
||||
import openfl.geom.ColorTransform;
|
||||
|
||||
class FlxAnimate extends flxanimate.FlxAnimate {
|
||||
static var rMatrix = new FlxMatrix();
|
||||
|
||||
override function drawLimb(limb:FlxFrame, _rMatrix:FlxMatrix, ?colorTransform:ColorTransform)
|
||||
override function drawLimb(limb:FlxFrame, _rMatrix:FlxMatrix, ?colorTransform:ColorTransform, ?blendMode:BlendMode)
|
||||
{
|
||||
if (alpha == 0 || colorTransform != null && (colorTransform.alphaMultiplier == 0 || colorTransform.alphaOffset == -255) || limb == null || limb.type == EMPTY)
|
||||
return;
|
||||
|
||||
if (blendMode == null)
|
||||
blendMode = BlendMode.NORMAL;
|
||||
|
||||
for (camera in cameras)
|
||||
{
|
||||
rMatrix.identity();
|
||||
@@ -28,7 +33,7 @@ class FlxAnimate extends flxanimate.FlxAnimate {
|
||||
var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
|
||||
var cos = Math.cos(angleOff);
|
||||
var sin = Math.sin(angleOff);
|
||||
// cos doesnt need to be negated
|
||||
// cos doesn't need to be negated
|
||||
rMatrix.rotateWithTrig(cos, -sin);
|
||||
rMatrix.translate(-frameOffset.x, -frameOffset.y);
|
||||
rMatrix.rotateWithTrig(cos, sin);
|
||||
@@ -66,13 +71,13 @@ class FlxAnimate extends flxanimate.FlxAnimate {
|
||||
}
|
||||
|
||||
rMatrix.translate(_point.x, _point.y);
|
||||
camera.drawPixels(limb, null, rMatrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
|
||||
camera.drawPixels(limb, null, rMatrix, colorTransform, blendMode, antialiasing, shaderEnabled ? shader : null);
|
||||
#if FLX_DEBUG
|
||||
FlxBasic.visibleCount++;
|
||||
#end
|
||||
}
|
||||
|
||||
// doesnt work, needs to be remade
|
||||
// doesn't work, needs to be remade
|
||||
// #if FLX_DEBUG
|
||||
// if (FlxG.debugger.drawDebug)
|
||||
// drawDebug();
|
||||
@@ -81,7 +86,7 @@ class FlxAnimate extends flxanimate.FlxAnimate {
|
||||
|
||||
override function limbOnScreen(limb:FlxFrame, m:FlxMatrix, ?Camera:FlxCamera)
|
||||
{
|
||||
// TODO: ACTUAL OPTIMISATION
|
||||
// TODO: ACTUAL OPTIMIZATION
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public var globalCurFrame(get, set):Int;
|
||||
|
||||
/**
|
||||
* ODD interval -> asynced; EVEN interval -> synced
|
||||
* ODD interval -> not aligned to beats
|
||||
* EVEN interval -> aligned to beats
|
||||
*/
|
||||
public var beatInterval(default, set):Int = 2;
|
||||
public var beatOffset:Int = 0;
|
||||
@@ -326,7 +327,31 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
lastAnimContext = Context;
|
||||
}
|
||||
|
||||
public function getAnim(name:String):OneOfTwo<FlxAnimation, FlxSymbolAnimation> {
|
||||
public inline function addAnim(name:String, prefix:String, frameRate:Float = 24, ?looped:Bool, ?forced:Bool, ?indices:Array<Int>, x:Float = 0, y:Float = 0, animType:XMLAnimType = NONE)
|
||||
{
|
||||
return XMLUtil.addAnimToSprite(this, {
|
||||
name: name,
|
||||
anim: prefix,
|
||||
fps: frameRate,
|
||||
loop: looped == null ? animType == LOOP : looped,
|
||||
animType: animType,
|
||||
x: x,
|
||||
y: y,
|
||||
indices: indices,
|
||||
forced: forced
|
||||
});
|
||||
}
|
||||
|
||||
public inline function removeAnim(name:String)
|
||||
{
|
||||
if (animateAtlas != null)
|
||||
@:privateAccess animateAtlas.anim.animsMap.remove(name);
|
||||
else
|
||||
animation.remove(name);
|
||||
}
|
||||
|
||||
public function getAnim(name:String):OneOfTwo<FlxAnimation, FlxSymbolAnimation>
|
||||
{
|
||||
if(animateAtlas != null)
|
||||
return animateAtlas.anim.getByName(name);
|
||||
return animation.getByName(name);
|
||||
@@ -339,7 +364,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
return FlxPoint.weak(0, 0);
|
||||
}
|
||||
|
||||
public inline function hasAnimation(AnimName:String):Bool @:privateAccess
|
||||
public inline function hasAnim(AnimName:String):Bool @:privateAccess
|
||||
return animateAtlas != null ? (animateAtlas.anim.animsMap.exists(AnimName)
|
||||
|| animateAtlas.anim.symbolDictionary.exists(AnimName)) : animation.exists(AnimName);
|
||||
|
||||
@@ -351,13 +376,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
return animateAtlas != null ? animateAtlas.anim.reversed : animation.curAnim != null ? animation.curAnim.reversed : false;
|
||||
}
|
||||
|
||||
public inline function removeAnimation(name:String) {
|
||||
if (animateAtlas != null)
|
||||
@:privateAccess animateAtlas.anim.animsMap.remove(name);
|
||||
else
|
||||
animation.remove(name);
|
||||
}
|
||||
|
||||
public inline function getNameList():Array<String> {
|
||||
if (animateAtlas != null)
|
||||
return [for (name in @:privateAccess animateAtlas.anim.animsMap.keys()) name];
|
||||
@@ -365,7 +383,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
return animation.getNameList();
|
||||
}
|
||||
|
||||
public inline function stopAnimation() {
|
||||
public inline function stopAnim()
|
||||
{
|
||||
if (animateAtlas != null)
|
||||
animateAtlas.anim.pause();
|
||||
else
|
||||
@@ -384,6 +403,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
if (animEnabled)
|
||||
super.updateAnimation(elapsed);
|
||||
}
|
||||
|
||||
// Backwards compat (the names used to be all different and it sucked, please lets use the same format in the future) - Nex
|
||||
public inline function hasAnimation(AnimName:String) return hasAnim(AnimName);
|
||||
public inline function removeAnimation(name:String) return removeAnim(name);
|
||||
public inline function stopAnimation() return stopAnim();
|
||||
#end
|
||||
|
||||
// Getter / Setters
|
||||
|
||||
@@ -2,6 +2,9 @@ package funkin.backend;
|
||||
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
|
||||
/**
|
||||
* Group of FlxSprites, if the sprite implements `IBeatReceiver`, then beatHit, stepHit and measureHit will be called.
|
||||
**/
|
||||
class MusicBeatGroup extends FlxTypedSpriteGroup<FlxSprite> implements IBeatReceiver {
|
||||
public function beatHit(curBeat:Int) {
|
||||
for(e in members) if (e is IBeatReceiver) ({var _:IBeatReceiver = cast e;_;}).beatHit(curBeat);
|
||||
|
||||
@@ -13,6 +13,10 @@ import funkin.backend.system.framerate.Framerate;
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
import funkin.options.PlayerSettings;
|
||||
|
||||
/**
|
||||
* Base class for all the states.
|
||||
* Handles the scripts, the transitions, and the beat and step events.
|
||||
**/
|
||||
class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
{
|
||||
private var lastBeat:Float = 0;
|
||||
@@ -93,6 +97,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
|
||||
public var scriptsAllowed:Bool = true;
|
||||
|
||||
public static var lastScriptName:String = null;
|
||||
public static var lastStateName:String = null;
|
||||
|
||||
public var scriptName:String = null;
|
||||
|
||||
public static var skipTransOut:Bool = false;
|
||||
@@ -108,7 +115,12 @@ class MusicBeatState extends FlxState implements IBeatReceiver
|
||||
public function new(scriptsAllowed:Bool = true, ?scriptName:String) {
|
||||
super();
|
||||
this.scriptsAllowed = #if SOFTCODED_STATES scriptsAllowed #else false #end;
|
||||
this.scriptName = scriptName;
|
||||
|
||||
if(lastStateName != (lastStateName = Type.getClassName(Type.getClass(this)))) {
|
||||
lastScriptName = null;
|
||||
}
|
||||
this.scriptName = scriptName != null ? scriptName : lastScriptName;
|
||||
lastScriptName = this.scriptName;
|
||||
}
|
||||
|
||||
function loadScript() {
|
||||
|
||||
@@ -11,13 +11,17 @@ import funkin.backend.system.Controls;
|
||||
import funkin.backend.system.interfaces.IBeatReceiver;
|
||||
import funkin.options.PlayerSettings;
|
||||
|
||||
/**
|
||||
* Base class for all the sub states.
|
||||
* Handles the scripts, the transitions, and the beat and step events.
|
||||
**/
|
||||
class MusicBeatSubstate extends FlxSubState implements IBeatReceiver
|
||||
{
|
||||
private var lastBeat:Float = 0;
|
||||
private var lastStep:Float = 0;
|
||||
|
||||
/**
|
||||
* Whether this specifc substate can open custom transitions
|
||||
* Whether this specific substate can open custom transitions
|
||||
*/
|
||||
public var canOpenCustomTransition:Bool = false;
|
||||
/**
|
||||
|
||||
@@ -2,14 +2,23 @@ package funkin.backend;
|
||||
|
||||
import flixel.FlxState;
|
||||
import flixel.tweens.FlxTween;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
import funkin.backend.scripting.events.TransitionCreationEvent;
|
||||
import funkin.backend.utils.FunkinParentDisabler;
|
||||
|
||||
class MusicBeatTransition extends MusicBeatSubstate {
|
||||
public static var script:String = Flags.DEFAULT_TRANSITION_SCRIPT;
|
||||
public var transitionScript:Script;
|
||||
|
||||
var nextFrameSkip:Bool = false;
|
||||
|
||||
public var transitionTween:FlxTween = null;
|
||||
public var transitionCamera:FlxCamera;
|
||||
public var newState:FlxState;
|
||||
public var transOut:Bool = false;
|
||||
|
||||
public var allowSkip:Bool = true;
|
||||
|
||||
public var blackSpr:FlxSprite;
|
||||
public var transitionSprite:FunkinSprite;
|
||||
@@ -20,16 +29,30 @@ class MusicBeatTransition extends MusicBeatSubstate {
|
||||
|
||||
public override function create() {
|
||||
if (newState != null)
|
||||
add(new FunkinParentDisabler(true));
|
||||
add(new FunkinParentDisabler(true, false));
|
||||
|
||||
transitionCamera = new FlxCamera();
|
||||
transitionCamera.bgColor = 0;
|
||||
FlxG.cameras.add(transitionCamera, false);
|
||||
|
||||
cameras = [transitionCamera];
|
||||
var out = newState != null;
|
||||
|
||||
blackSpr = new FlxSprite(0, out ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
|
||||
transitionScript = Script.create(Paths.script(script));
|
||||
transitionScript.setParent(this);
|
||||
transitionScript.load();
|
||||
|
||||
var event = EventManager.get(TransitionCreationEvent).recycle(newState != null, newState);
|
||||
transitionScript.call('create', [event]);
|
||||
|
||||
transOut = event.transOut;
|
||||
newState = event.newState;
|
||||
|
||||
if (event.cancelled) {
|
||||
super.create();
|
||||
return;
|
||||
}
|
||||
|
||||
blackSpr = new FlxSprite(0, transOut ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1);
|
||||
blackSpr.scale.set(transitionCamera.width, transitionCamera.height);
|
||||
blackSpr.color = 0xFF000000;
|
||||
blackSpr.updateHitbox();
|
||||
@@ -43,7 +66,7 @@ class MusicBeatTransition extends MusicBeatSubstate {
|
||||
} else {
|
||||
transitionSprite.screenCenter();
|
||||
}
|
||||
transitionCamera.flipY = !out;
|
||||
transitionCamera.flipY = !transOut;
|
||||
add(transitionSprite);
|
||||
|
||||
transitionCamera.scroll.y = transitionCamera.height;
|
||||
@@ -55,41 +78,59 @@ class MusicBeatTransition extends MusicBeatSubstate {
|
||||
});
|
||||
|
||||
super.create();
|
||||
transitionScript.call('postCreate', [event]);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
transitionScript.call('update', [elapsed]);
|
||||
super.update(elapsed);
|
||||
|
||||
if (nextFrameSkip) {
|
||||
finish();
|
||||
return;
|
||||
var event = new CancellableEvent();
|
||||
transitionScript.call('onSkip', [event]);
|
||||
if (!event.cancelled) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parent.persistentUpdate && FlxG.keys.pressed.SHIFT) {
|
||||
if (allowSkip && !parent.persistentUpdate && FlxG.keys.pressed.SHIFT) {
|
||||
// skip
|
||||
if (newState != null) {
|
||||
nextFrameSkip = true;
|
||||
parent.persistentDraw = false;
|
||||
} else {
|
||||
finish();
|
||||
var event = new CancellableEvent();
|
||||
transitionScript.call('onSkip', [event]);
|
||||
if (!event.cancelled) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
transitionScript.call('postUpdate', [elapsed]);
|
||||
}
|
||||
|
||||
public function finish() {
|
||||
var event = new CancellableEvent();
|
||||
transitionScript.call('onFinish', [event]);
|
||||
if (event.cancelled) return;
|
||||
|
||||
if (newState != null)
|
||||
FlxG.switchState(newState);
|
||||
close();
|
||||
|
||||
transitionScript.call('onPostFinish');
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
if (transitionTween != null)
|
||||
transitionTween.cancel();
|
||||
transitionScript.call('destroy');
|
||||
|
||||
if (transitionTween != null) transitionTween.cancel();
|
||||
transitionTween = FlxDestroyUtil.destroy(transitionTween);
|
||||
if (newState == null && FlxG.cameras.list.contains(transitionCamera))
|
||||
FlxG.cameras.remove(transitionCamera);
|
||||
else
|
||||
transitionCamera.bgColor = 0xFF000000;
|
||||
if (newState == null && FlxG.cameras.list.contains(transitionCamera)) FlxG.cameras.remove(transitionCamera);
|
||||
else transitionCamera.bgColor = 0xFF000000;
|
||||
|
||||
transitionScript.destroy();
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,9 @@ class ModsFolder {
|
||||
* Whenever its the first time mods has been reloaded.
|
||||
*/
|
||||
private static var __firstTime:Bool = true;
|
||||
|
||||
/**
|
||||
* Initialises `mods` folder.
|
||||
* Initializes `mods` folder.
|
||||
*/
|
||||
public static function init() {
|
||||
if(!getModsList().contains(Options.lastLoadedMod))
|
||||
@@ -85,7 +86,12 @@ class ModsFolder {
|
||||
public static function getModsList():Array<String> {
|
||||
var mods:Array<String> = [];
|
||||
#if MOD_SUPPORT
|
||||
for(modFolder in FileSystem.readDirectory(modsPath)) {
|
||||
final modsList:Array<String> = FileSystem.readDirectory(modsPath);
|
||||
|
||||
if (modsList == null || modsList.length <= 0)
|
||||
return mods;
|
||||
|
||||
for (modFolder in modsList) {
|
||||
if (FileSystem.isDirectory(modsPath + modFolder)) {
|
||||
mods.push(modFolder);
|
||||
} else {
|
||||
@@ -151,4 +157,4 @@ class ModsFolder {
|
||||
return prepareModLibrary(libName, new ZipFolderLibrary(zipPath, libName, modName), force);
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ class BaseGameParser {
|
||||
|
||||
result.customValues = {};
|
||||
result.customValues.timeChanges = data.timeChanges;
|
||||
var firstTimeChng:SwagTimeChange = data.timeChanges[0];
|
||||
result.bpm = firstTimeChng.bpm;
|
||||
result.beatsPerMeasure = firstTimeChng.b != null ? firstTimeChng.b : Flags.DEFAULT_BEATS_PER_MEASURE;
|
||||
var firstTimeChange:SwagTimeChange = data.timeChanges[0];
|
||||
result.bpm = firstTimeChange.bpm;
|
||||
result.beatsPerMeasure = firstTimeChange.b != null ? firstTimeChange.b : Flags.DEFAULT_BEATS_PER_MEASURE;
|
||||
|
||||
result.difficulties = data.playData.difficulties.concat(data.playData.songVariations);
|
||||
Reflect.deleteField(data.playData, "difficulties");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.backend.chart;
|
||||
|
||||
import funkin.backend.chart.ChartData;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import haxe.Json;
|
||||
@@ -11,9 +12,65 @@ import sys.FileSystem;
|
||||
|
||||
using StringTools;
|
||||
|
||||
final class Chart {
|
||||
enum abstract ChartFormat(Int) {
|
||||
var CODENAME = 0;
|
||||
var LEGACY = 1; // also used by many other engines (old Psych, Kade and more) - Nex
|
||||
var VSLICE = 2;
|
||||
var PSYCH_NEW = 3;
|
||||
|
||||
@:to public function toString():String {
|
||||
return switch(cast (this, ChartFormat)) {
|
||||
case CODENAME: "CODENAME";
|
||||
case LEGACY: "LEGACY";
|
||||
case VSLICE: "VSLICE";
|
||||
case PSYCH_NEW: "PSYCH_NEW";
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromString(str:String, def:ChartFormat = ChartFormat.LEGACY) {
|
||||
str = str.toLowerCase();
|
||||
str = StringTools.replace(str, " ", "");
|
||||
str = StringTools.replace(str, "_", "");
|
||||
str = StringTools.replace(str, ".", "");
|
||||
|
||||
if(StringTools.startsWith(str, "psychv1") || StringTools.startsWith(str, "psych1"))
|
||||
return PSYCH_NEW;
|
||||
|
||||
return switch(str) {
|
||||
case "codename" | "codenameengine": CODENAME;
|
||||
case "newpsych" | "psychnew": PSYCH_NEW;
|
||||
default: def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Chart {
|
||||
public static final version:String = "1.6.0";
|
||||
|
||||
public static function cleanSongData(data:Dynamic):Dynamic {
|
||||
if (Reflect.hasField(data, "song")) {
|
||||
var field:Dynamic = Reflect.field(data, "song");
|
||||
if (field != null && Type.typeof(field) == TObject) // Cant use Reflect.isObject, because it detects strings for some reason
|
||||
return field;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function detectChartFormat(data:Dynamic):ChartFormat {
|
||||
var __temp:Dynamic; // imma reuse this var so the program doesn't have to get values multiple times - Nex
|
||||
|
||||
if ((__temp = data.codenameChart) == true || __temp == "true")
|
||||
return CODENAME;
|
||||
|
||||
if (Reflect.hasField(data, "version") && Reflect.hasField(data, "scrollSpeed"))
|
||||
return VSLICE;
|
||||
|
||||
if ((__temp = cleanSongData(data).format) != null && __temp is String && StringTools.startsWith(__temp, "psych_v1"))
|
||||
return PSYCH_NEW;
|
||||
|
||||
return LEGACY;
|
||||
}
|
||||
|
||||
public static function loadEventsJson(songName:String) {
|
||||
var path = Paths.file('songs/${songName.toLowerCase()}/events.json');
|
||||
var data:Array<ChartEvent> = null;
|
||||
@@ -30,8 +87,9 @@ final class Chart {
|
||||
public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var metaPath = Paths.file('songs/${songName.toLowerCase()}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName.toLowerCase()}/meta-${difficulty.toLowerCase()}.json');
|
||||
var songNameLower = songName.toLowerCase();
|
||||
var metaPath = Paths.file('songs/${songNameLower}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json');
|
||||
|
||||
var data:ChartMetaData = null;
|
||||
var fromMods:Bool = fromMods;
|
||||
@@ -64,7 +122,7 @@ final class Chart {
|
||||
data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(Flags.DEFAULT_COLOR));
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName.toLowerCase()}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songNameLower}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
if (data.difficulties.length == 3) {
|
||||
var hasHard = false, hasNormal = false, hasEasy = false;
|
||||
for(d in data.difficulties) {
|
||||
@@ -115,12 +173,12 @@ final class Chart {
|
||||
Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
/**
|
||||
* CHART CONVERSION
|
||||
*/
|
||||
#if REGION
|
||||
if (Reflect.hasField(data, "codenameChart") && Reflect.field(data, "codenameChart") == true) {
|
||||
/**
|
||||
* CHART CONVERSION
|
||||
*/
|
||||
#if REGION
|
||||
if (data != null) switch (detectChartFormat(data)) {
|
||||
case CODENAME:
|
||||
// backward compat on events since its caused problems
|
||||
var eventTypesToString:Map<Int, String> = [
|
||||
-1 => "HScript Call",
|
||||
@@ -131,28 +189,23 @@ final class Chart {
|
||||
];
|
||||
|
||||
if (data.events == null) data.events = [];
|
||||
for (event in cast(data.events, Array<Dynamic>)) {
|
||||
if (Reflect.hasField(event, "type")) {
|
||||
if(event.type != null)
|
||||
event.name = eventTypesToString[event.type];
|
||||
Reflect.deleteField(event, "type");
|
||||
}
|
||||
for (event in cast(data.events, Array<Dynamic>)) if (Reflect.hasField(event, "type")) {
|
||||
if (event.type != null)
|
||||
event.name = eventTypesToString[event.type];
|
||||
Reflect.deleteField(event, "type");
|
||||
}
|
||||
|
||||
// codename chart
|
||||
base = data;
|
||||
} else {
|
||||
// base game chart
|
||||
FNFLegacyParser.parse(data, base);
|
||||
}
|
||||
#end
|
||||
case PSYCH_NEW: PsychParser.parse(data, base);
|
||||
case VSLICE: // TODO
|
||||
case LEGACY: FNFLegacyParser.parse(data, base);
|
||||
}
|
||||
#end
|
||||
|
||||
if (base.meta == null)
|
||||
base.meta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
if (base.meta == null) base.meta = loadedMeta;
|
||||
else {
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
for(field in Reflect.fields(base.meta)) {
|
||||
for (field in Reflect.fields(base.meta)) {
|
||||
var f = Reflect.field(base.meta, field);
|
||||
if (f != null)
|
||||
Reflect.setField(loadedMeta, field, f);
|
||||
@@ -257,4 +310,4 @@ typedef ChartSaveSettings = {
|
||||
var ?saveEventsInChart:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
var ?folder:String;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ typedef ChartStrumLine = {
|
||||
var ?vocalsSuffix:String;
|
||||
var ?keyCount:Int; // default=4
|
||||
|
||||
var ?strumLinePos:Float; // Backwards compatability
|
||||
var ?strumLinePos:Float; // Backwards compatibility
|
||||
}
|
||||
|
||||
typedef ChartNote = {
|
||||
|
||||
@@ -3,15 +3,14 @@ package funkin.backend.chart;
|
||||
import funkin.backend.chart.ChartData.ChartEvent;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
/**
|
||||
* Legacy FNF chart parser.
|
||||
* This is for charts that that were made before v0.5.0.
|
||||
**/
|
||||
class FNFLegacyParser {
|
||||
public static function parse(data:Dynamic, result:ChartData) {
|
||||
// base fnf chart parsing
|
||||
var data:SwagSong = data;
|
||||
if (Reflect.hasField(data, "song")) {
|
||||
var field:Dynamic = Reflect.field(data, "song");
|
||||
if (!(field is String))
|
||||
data = field;
|
||||
}
|
||||
var data:SwagSong = Chart.cleanSongData(data);
|
||||
|
||||
result.scrollSpeed = data.speed;
|
||||
result.stage = data.stage;
|
||||
@@ -29,7 +28,7 @@ class FNFLegacyParser {
|
||||
position: "boyfriend",
|
||||
notes: []
|
||||
});
|
||||
var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : "gf");
|
||||
var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : (data.player3 != null ? data.player3 : "gf"));
|
||||
if (!p2isGF && gfName != "none") {
|
||||
result.strumLines.push({
|
||||
characters: [gfName],
|
||||
@@ -214,6 +213,7 @@ typedef SwagSong =
|
||||
|
||||
var player1:String;
|
||||
var player2:String;
|
||||
var ?player3:String; // alt for gf but idr if it was just used in psych or what but eh whatever maybe its better here anyways - Nex
|
||||
var ?gf:String;
|
||||
var ?gfVersion:String;
|
||||
var validScore:Bool;
|
||||
|
||||
@@ -5,16 +5,38 @@ import funkin.backend.chart.FNFLegacyParser.SwagSection;
|
||||
import funkin.backend.chart.FNFLegacyParser.SwagSong;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
/**
|
||||
* Parser for Psych Charts.
|
||||
* Both for pre-v1.0.0 and v1.0.0 charts.
|
||||
**/
|
||||
class PsychParser {
|
||||
// Alreadly parsed in sections
|
||||
// Already parsed in sections
|
||||
public static var ignoreEvents:Array<String> = [
|
||||
"Camera Movement",
|
||||
"Alt Animation Toggle",
|
||||
"BPM Change"
|
||||
];
|
||||
|
||||
public static function parse(data:Dynamic, result:ChartData)
|
||||
/**
|
||||
* Converts 1.0 Psych charts to older ones
|
||||
*/
|
||||
public static function standardize(data:Dynamic, result:ChartData) {
|
||||
var sectionsData:Array<Dynamic> = Chart.cleanSongData(data).notes;
|
||||
if (sectionsData == null) return;
|
||||
|
||||
for (section in sectionsData) for (note in cast(section.sectionNotes, Array<Dynamic>)) {
|
||||
var gottaHitNote:Bool = section.mustHitSection == (note[1] >= 4);
|
||||
note[1] = (note[1] % 4) + (gottaHitNote ? 4 : 0);
|
||||
|
||||
if (note[3] != null && Std.isOfType(note[3], String))
|
||||
note[3] = Chart.addNoteType(result, note[3]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function parse(data:Dynamic, result:ChartData) {
|
||||
if (Chart.detectChartFormat(data) == PSYCH_NEW) standardize(data, result);
|
||||
FNFLegacyParser.parse(data, result);
|
||||
}
|
||||
|
||||
public static function encode(chart:ChartData):Dynamic {
|
||||
var base:SwagSong = FNFLegacyParser.__convertToSwagSong(chart);
|
||||
@@ -44,7 +66,7 @@ class PsychParser {
|
||||
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
|
||||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
|
||||
sectionNote[1] += 4;
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
swagSection.sectionNotes.push(sectionNote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,10 +110,10 @@ class PsychParser {
|
||||
var eventStep:Float = Conductor.getStepForTime(event.time);
|
||||
psychEvents.push([
|
||||
"Change Scroll Speed",
|
||||
FlxMath.roundDecimal(event.params[1]/chart.scrollSpeed, 2), // SCROLL SPEED MULTIPLER
|
||||
FlxMath.roundDecimal(event.params[1]/chart.scrollSpeed, 2), // SCROLL SPEED MULTIPLIER
|
||||
FlxMath.roundDecimal( // TIME
|
||||
event.params[0] ? // IS TWEENED?
|
||||
(Conductor.getTimeForStep(eventStep+event.params[2]) - Conductor.getTimeForStep(eventStep))/1000
|
||||
(Conductor.getTimeForStep(eventStep+event.params[2]) - Conductor.getTimeForStep(eventStep))/1000
|
||||
: 0, 2)
|
||||
]);
|
||||
default:
|
||||
|
||||
@@ -4,7 +4,7 @@ import flixel.FlxState;
|
||||
import funkin.backend.scripting.events.*;
|
||||
|
||||
final class EventManager {
|
||||
// map doesnt work for that
|
||||
// map doesn't work for that
|
||||
public static var eventValues:Array<CancellableEvent> = [];
|
||||
public static var eventKeys:Array<Class<CancellableEvent>> = [];
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import funkin.backend.system.Conductor;
|
||||
class GlobalScript {
|
||||
public static var scripts:ScriptPack;
|
||||
|
||||
private static var initalized:Bool = false;
|
||||
private static var initialized:Bool = false;
|
||||
|
||||
public static function init() {
|
||||
if(initalized) return;
|
||||
initalized = true;
|
||||
if(initialized) return;
|
||||
initialized = true;
|
||||
#if MOD_SUPPORT
|
||||
ModsFolder.onModSwitch.add(onModSwitch);
|
||||
#end
|
||||
|
||||
@@ -16,7 +16,7 @@ class HScript extends Script {
|
||||
public static function initParser() {
|
||||
var parser = new Parser();
|
||||
parser.allowJSON = parser.allowMetadata = parser.allowTypes = true;
|
||||
parser.preprocesorValues = Script.getDefaultPreprocessors();
|
||||
parser.preprocessorValues = Script.getDefaultPreprocessors();
|
||||
return parser;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import lime.app.Application;
|
||||
@:allow(funkin.backend.scripting.ScriptPack)
|
||||
/**
|
||||
* Class used for scripting.
|
||||
* Use `Script.create` to create a script.
|
||||
*/
|
||||
class Script extends FlxBasic implements IFlxDestroyable {
|
||||
/**
|
||||
@@ -16,6 +17,9 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
*/
|
||||
public static var staticVariables:Map<String, Dynamic> = [];
|
||||
|
||||
/**
|
||||
* Gets the default variables for a script.
|
||||
*/
|
||||
public static function getDefaultVariables(?script:Script):Map<String, Dynamic> {
|
||||
return [
|
||||
// Haxe related stuff
|
||||
@@ -104,7 +108,6 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
**/
|
||||
public static function getDefaultImportRedirects():Map<String, String> {
|
||||
var redirects:Map<String, String> = [
|
||||
"funkin.savedata.FunkinSave" => "funkin.backend.utils.FunkinSave"
|
||||
];
|
||||
|
||||
final events = "funkin.backend.scripting.events.";
|
||||
@@ -141,6 +144,10 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
return redirects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default defines for a script.
|
||||
* Includes all of the defines that the build was compiled with.
|
||||
*/
|
||||
public static function getDefaultPreprocessors():Map<String, Dynamic> {
|
||||
var defines = funkin.backend.system.macros.DefinesMacro.defines;
|
||||
defines.set("CODENAME_ENGINE", true);
|
||||
@@ -182,6 +189,10 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
|
||||
private var didLoad:Bool = false;
|
||||
|
||||
/**
|
||||
* Remapped filenames.
|
||||
* Used for trace messages, to show what mod the script is from.
|
||||
*/
|
||||
public var remappedNames:Map<String, String> = [];
|
||||
|
||||
/**
|
||||
@@ -307,7 +318,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the code from a string, doesnt really work after the script has been loaded
|
||||
* Loads the code from a string, doesn't really work after the script has been loaded
|
||||
* @param code The code.
|
||||
*/
|
||||
public function loadFromString(code:String) {
|
||||
@@ -366,7 +377,14 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
private function onCall(func:String, parameters:Array<Dynamic>):Dynamic {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Called when the script is created.
|
||||
* @param path Path to the script
|
||||
*/
|
||||
public function onCreate(path:String) {}
|
||||
|
||||
/**
|
||||
* Called when the script is loaded.
|
||||
*/
|
||||
public function onLoad() {}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package funkin.backend.scripting;
|
||||
import flixel.util.FlxStringUtil;
|
||||
import funkin.backend.scripting.events.CancellableEvent;
|
||||
|
||||
/**
|
||||
* Used to group multiple scripts together, and easily be able to call them.
|
||||
**/
|
||||
@:access(CancellableEvent)
|
||||
class ScriptPack extends Script {
|
||||
public var scripts:Array<Script> = [];
|
||||
@@ -10,12 +13,19 @@ class ScriptPack extends Script {
|
||||
public var publicVariables:Map<String, Dynamic> = [];
|
||||
public var parent:Dynamic = null;
|
||||
|
||||
/**
|
||||
* Loads all scripts in the pack.
|
||||
**/
|
||||
public override function load() {
|
||||
for(e in scripts) {
|
||||
e.load();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the script pack contains a script with a specific path.
|
||||
* @param path Path to check
|
||||
*/
|
||||
public function contains(path:String) {
|
||||
for(e in scripts)
|
||||
if (e.path == path)
|
||||
@@ -27,6 +37,10 @@ class ScriptPack extends Script {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a script by path.
|
||||
* @param name Path to the script
|
||||
**/
|
||||
public function getByPath(name:String) {
|
||||
for(s in scripts)
|
||||
if (s.path == name)
|
||||
@@ -34,12 +48,22 @@ class ScriptPack extends Script {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a script by name.
|
||||
* @param name Name of the script
|
||||
**/
|
||||
public function getByName(name:String) {
|
||||
for(s in scripts)
|
||||
if (s.fileName == name)
|
||||
return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a script by path.
|
||||
* @param path Path to the script
|
||||
* @throws Error if the script does not exist
|
||||
**/
|
||||
public function importScript(path:String):Script {
|
||||
var script = Script.create(Paths.script(path));
|
||||
if (script is DummyScript) {
|
||||
@@ -51,6 +75,12 @@ class ScriptPack extends Script {
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a function on every single script.
|
||||
* Only calls on scripts that are active.
|
||||
* @param func Function to call
|
||||
* @param parameters Parameters to pass to the function
|
||||
**/
|
||||
public override function call(func:String, ?parameters:Array<Dynamic>):Dynamic {
|
||||
for(e in scripts)
|
||||
if(e.active)
|
||||
@@ -74,6 +104,10 @@ class ScriptPack extends Script {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first script that has a variable with a specific name.
|
||||
* @param val Name of the variable
|
||||
**/
|
||||
public override function get(val:String):Dynamic {
|
||||
for(e in scripts) {
|
||||
var v = e.get(val);
|
||||
@@ -82,40 +116,66 @@ class ScriptPack extends Script {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads all scripts in the pack.
|
||||
**/
|
||||
public override function reload() {
|
||||
for(e in scripts) e.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable in every script.
|
||||
**/
|
||||
public override function set(val:String, value:Dynamic) {
|
||||
for(e in scripts) e.set(val, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent/this of every script in the pack.
|
||||
*/
|
||||
public override function setParent(parent:Dynamic) {
|
||||
this.parent = parent;
|
||||
for(e in scripts) e.setParent(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all scripts in the pack.
|
||||
**/
|
||||
public override function destroy() {
|
||||
super.destroy();
|
||||
for(e in scripts) e.destroy();
|
||||
}
|
||||
|
||||
public override function onCreate(path:String) {}
|
||||
@:dox(hide) public override function onCreate(path:String) {}
|
||||
|
||||
/**
|
||||
* Adds a script to the pack, and sets the parent/this of the script.
|
||||
**/
|
||||
public function add(script:Script) {
|
||||
scripts.push(script);
|
||||
__configureNewScript(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a script from the pack.
|
||||
* Does not reset the parent/this.
|
||||
**/
|
||||
public function remove(script:Script) {
|
||||
scripts.remove(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a script into the pack, and sets the parent/this of the script.
|
||||
**/
|
||||
public function insert(pos:Int, script:Script) {
|
||||
scripts.insert(pos, script);
|
||||
__configureNewScript(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a new script.
|
||||
* @param script Script to configure
|
||||
**/
|
||||
private function __configureNewScript(script:Script) {
|
||||
if (parent != null) script.setParent(parent);
|
||||
script.setPublicMap(publicVariables);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package funkin.backend.scripting.events;
|
||||
|
||||
import flixel.FlxState;
|
||||
|
||||
/**
|
||||
* CANCEL this event to prevent default behaviour!
|
||||
*/
|
||||
final class TransitionCreationEvent extends CancellableEvent {
|
||||
/**
|
||||
* If the transition is going out into another state
|
||||
*/
|
||||
public var transOut:Bool;
|
||||
|
||||
/**
|
||||
* The state that is about to be loaded (only on trans out)
|
||||
*/
|
||||
public var newState:FlxState;
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package funkin.backend.scripting.events.menu.freeplay;
|
||||
|
||||
final class FreeplayAlphaUpdateEvent extends CancellableEvent {
|
||||
/**
|
||||
* The alpha when nothing is playing and isnt selected
|
||||
* The alpha when nothing is playing and isn't selected
|
||||
*/
|
||||
public var idleAlpha:Float;
|
||||
/**
|
||||
* The alpha when something is playing and isnt selected
|
||||
* The alpha when something is playing and isn't selected
|
||||
*/
|
||||
public var idlePlayingAlpha:Float;
|
||||
/**
|
||||
|
||||
@@ -86,7 +86,7 @@ final class NoteHitEvent extends CancellableEvent {
|
||||
*/
|
||||
public var healthGain:Float;
|
||||
/**
|
||||
* Rating name. Defaults to "sick", "good", "bad" and "shit". Customisable.
|
||||
* Rating name. Defaults to "sick", "good", "bad" and "shit". Customizable.
|
||||
*/
|
||||
public var rating:String = "sick";
|
||||
/**
|
||||
|
||||
@@ -3,11 +3,13 @@ package funkin.backend.shaders;
|
||||
import flixel.util.FlxColor;
|
||||
import openfl.display.ShaderParameter;
|
||||
|
||||
@:dox(hide)
|
||||
typedef BlendModeShader =
|
||||
{
|
||||
var uBlendColor:ShaderParameter<Float>;
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
class BlendModeEffect
|
||||
{
|
||||
public var shader(default, null):BlendModeShader;
|
||||
|
||||
@@ -215,8 +215,8 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultFragmentSource;
|
||||
glRawFragmentSource = value;
|
||||
value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
|
||||
value = processImports(value, FRAGMENT_SHADER);
|
||||
value = value.replace("#pragma header", ShaderTemplates.fragHeader).replace("#pragma body", ShaderTemplates.fragBody);
|
||||
if (value != __glFragmentSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
@@ -230,8 +230,19 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
if(value == null)
|
||||
value = ShaderTemplates.defaultVertexSource;
|
||||
glRawVertexSource = value;
|
||||
value = value.replace("#pragma header", ShaderTemplates.vertHeader).replace("#pragma body", ShaderTemplates.vertBody);
|
||||
value = processImports(value, VERTEX_SHADER);
|
||||
|
||||
var useBackCompat:Bool = true;
|
||||
for (regex in ShaderTemplates.vertBackCompatVarList) if (!regex.match(value)) {
|
||||
useBackCompat = false;
|
||||
break;
|
||||
}
|
||||
|
||||
var header = useBackCompat ? ShaderTemplates.vertHeaderBackCompat : ShaderTemplates.vertHeader;
|
||||
var body = useBackCompat ? ShaderTemplates.vertBodyBackCompat : ShaderTemplates.vertBody;
|
||||
|
||||
value = value.replace("#pragma header", header).replace("#pragma body", body);
|
||||
|
||||
if (value != __glVertexSource)
|
||||
{
|
||||
__glSourceDirty = true;
|
||||
@@ -521,7 +532,7 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
|
||||
|
||||
// cant do "field is ShaderInput" because ShaderInput has the @:generic metadata
|
||||
// aka instead of ShaderInput<Float> it gets built as ShaderInput_Float
|
||||
// this should be fine tho because we check the class, and the fields dont vary based on the type
|
||||
// this should be fine tho because we check the class, and the fields don't vary based on the type
|
||||
|
||||
// thanks for looking in the code cne fans :D!! -lunar
|
||||
|
||||
@@ -673,6 +684,38 @@ if(hasColorTransform) {
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;";
|
||||
|
||||
// TODO: make this ignore comments
|
||||
public static final vertBackCompatVarList:Array<EReg> = [
|
||||
~/attribute float alpha/,
|
||||
~/attribute vec4 colorMultiplier/,
|
||||
~/attribute vec4 colorOffset/,
|
||||
~/uniform bool hasColorTransform/
|
||||
];
|
||||
|
||||
public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha;
|
||||
attribute vec4 openfl_ColorMultiplier;
|
||||
attribute vec4 openfl_ColorOffset;
|
||||
attribute vec4 openfl_Position;
|
||||
attribute vec2 openfl_TextureCoord;
|
||||
|
||||
varying float openfl_Alphav;
|
||||
varying vec4 openfl_ColorMultiplierv;
|
||||
varying vec4 openfl_ColorOffsetv;
|
||||
varying vec2 openfl_TextureCoordv;
|
||||
|
||||
uniform mat4 openfl_Matrix;
|
||||
uniform bool openfl_HasColorTransform;
|
||||
uniform vec2 openfl_TextureSize;";
|
||||
|
||||
public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha;
|
||||
openfl_TextureCoordv = openfl_TextureCoord;
|
||||
|
||||
if(openfl_HasColorTransform) {
|
||||
openfl_ColorMultiplierv = openfl_ColorMultiplier;
|
||||
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
|
||||
}
|
||||
|
||||
gl_Position = openfl_Matrix * openfl_Position;";
|
||||
|
||||
public static final defaultVertexSource:String = "#pragma header
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package funkin.backend.shaders;
|
||||
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
|
||||
@:dox(hide)
|
||||
class OverlayShader extends FlxShader
|
||||
{
|
||||
@:glFragmentSource('
|
||||
|
||||
@@ -3,6 +3,7 @@ package funkin.backend.shaders;
|
||||
// STOLEN FROM HAXEFLIXEL DEMO LOL
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
|
||||
@:dox(hide)
|
||||
enum WiggleEffectType
|
||||
{
|
||||
DREAMY;
|
||||
@@ -12,6 +13,7 @@ enum WiggleEffectType
|
||||
FLAG;
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
class WiggleEffect
|
||||
{
|
||||
public var shader(default, null):WiggleShader = new WiggleShader();
|
||||
@@ -59,6 +61,7 @@ class WiggleEffect
|
||||
}
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
class WiggleShader extends FlxShader
|
||||
{
|
||||
@:glFragmentSource('
|
||||
|
||||
@@ -38,7 +38,7 @@ class Conductor
|
||||
public static var stepCrochet:Float = crochet / 4; // steps in milliseconds
|
||||
|
||||
/**
|
||||
* Number of beats per mesure (top number in time signature). Defaults to 4.
|
||||
* Number of beats per measure (top number in time signature). Defaults to 4.
|
||||
*/
|
||||
public static var beatsPerMeasure:Float = 4;
|
||||
|
||||
@@ -190,7 +190,7 @@ class Conductor
|
||||
__updateSongPos(FlxG.elapsed);
|
||||
|
||||
if (bpm > 0) {
|
||||
// updates curbeat and stuff
|
||||
// updates curBeat and stuff
|
||||
__lastChange = {
|
||||
stepTime: 0,
|
||||
songTime: 0,
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package funkin.backend.system;
|
||||
|
||||
enum abstract ErrorCode(Int) {
|
||||
var OK = 0;
|
||||
var FAILED = 1;
|
||||
var MISSING_PROPERTY = 2;
|
||||
var TYPE_INCORRECT = 3;
|
||||
var VALUE_NULL = 4;
|
||||
var REFLECT_ERROR = 5;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class Flags {
|
||||
@:lazy public static var VERSION_MESSAGE:String = 'Codename Engine v${Application.current.meta.get('version')}';
|
||||
|
||||
public static var REPO_NAME:String = "CodenameEngine";
|
||||
public static var REPO_OWNER:String = "FNF-CNE-Devs";
|
||||
public static var REPO_OWNER:String = "CodenameCrew";
|
||||
public static var REPO_URL:String = 'https://github.com/$REPO_OWNER/$REPO_NAME';
|
||||
|
||||
// make this empty once you guys are done with the project.
|
||||
@@ -76,7 +76,7 @@ class Flags {
|
||||
public static var DEFAULT_OPPONENT_MODE_ALLOWED:Bool = false;
|
||||
|
||||
@:also(funkin.game.PlayState.coopMode)
|
||||
public static var DEFAULT_COOP_MODE:Bool = false; // used in playstate if it doesnt find it
|
||||
public static var DEFAULT_COOP_MODE:Bool = false; // used in playstate if it doesn't find it
|
||||
@:also(funkin.game.PlayState.opponentMode)
|
||||
public static var DEFAULT_OPPONENT_MODE:Bool = false;
|
||||
|
||||
@@ -135,6 +135,13 @@ class Flags {
|
||||
public static var DISABLE_EDITORS:Bool = false;
|
||||
public static var DISABLE_BETA_WARNING_SCREEN:Bool = false;
|
||||
public static var DISABLE_TRANSITIONS:Bool = false;
|
||||
|
||||
@:also(funkin.backend.MusicBeatTransition.script)
|
||||
public static var DEFAULT_TRANSITION_SCRIPT:String = "";
|
||||
@:also(funkin.menus.PauseSubState.script)
|
||||
public static var DEFAULT_PAUSE_SCRIPT:String = "";
|
||||
@:also(funkin.game.GameOverSubstate.script)
|
||||
public static var DEFAULT_GAMEOVER_SCRIPT:String = "";
|
||||
// -- End of Codename's Default Flags --
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ package funkin.backend.system;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
|
||||
/**
|
||||
* Dummy FlxSprite that allows you to cache FlxGraphics, and immediatly send them to GPU memory.
|
||||
* Dummy FlxSprite that allows you to cache FlxGraphics, and immediately send them to GPU memory.
|
||||
*/
|
||||
class GraphicCacheSprite extends FlxSprite {
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,7 @@ final class Logs {
|
||||
var prefix = "[FLIXEL]";
|
||||
var color:ConsoleColor = LIGHTGRAY;
|
||||
var level:Level = INFO;
|
||||
if (style == LogStyle.CONSOLE) // cant place a switch here as these arent inline values - Nex
|
||||
if (style == LogStyle.CONSOLE) // cant place a switch here as these aren't inline values - Nex
|
||||
{
|
||||
prefix = "> ";
|
||||
color = WHITE;
|
||||
|
||||
@@ -48,6 +48,10 @@ class Main extends Sprite
|
||||
|
||||
public static var game:FunkinGame;
|
||||
|
||||
/**
|
||||
* The time since the game was focused last time in seconds.
|
||||
*/
|
||||
public static var timeSinceFocus(get, never):Float;
|
||||
public static var time:Int = 0;
|
||||
|
||||
// You can pretty much ignore everything from here on - your code should go in your states.
|
||||
@@ -144,7 +148,7 @@ class Main extends Sprite
|
||||
Assets.registerLibrary('default', lib);
|
||||
|
||||
funkin.options.PlayerSettings.init();
|
||||
funkin.backend.utils.FunkinSave.init();
|
||||
funkin.savedata.FunkinSave.init();
|
||||
Options.load();
|
||||
|
||||
FlxG.fixedTimestep = false;
|
||||
@@ -154,6 +158,7 @@ class Main extends Sprite
|
||||
Conductor.init();
|
||||
AudioSwitchFix.init();
|
||||
EventManager.init();
|
||||
FlxG.signals.focusGained.add(onFocus);
|
||||
FlxG.signals.preStateSwitch.add(onStateSwitch);
|
||||
FlxG.signals.postStateSwitch.add(onStateSwitchPost);
|
||||
|
||||
@@ -202,13 +207,17 @@ class Main extends Sprite
|
||||
{asset: diamond, width: 32, height: 32}, new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
|
||||
}
|
||||
|
||||
public static function onFocus() {
|
||||
_tickFocused = FlxG.game.ticks;
|
||||
}
|
||||
|
||||
private static function onStateSwitch() {
|
||||
scaleMode.resetSize();
|
||||
}
|
||||
|
||||
private static function onStateSwitchPost() {
|
||||
// manual asset clearing since base openfl one doesnt clear lime one
|
||||
// doesnt clear bitmaps since flixel fork does it auto
|
||||
// manual asset clearing since base openfl one does'nt clear lime one
|
||||
// does'nt clear bitmaps since flixel fork does it auto
|
||||
|
||||
@:privateAccess {
|
||||
// clear uint8 pools
|
||||
@@ -235,4 +244,9 @@ class Main extends Sprite
|
||||
Sys.setCwd(Path.addTrailingSlash(openfl.filesystem.File.applicationStorageDirectory.nativePath));
|
||||
#end
|
||||
}
|
||||
|
||||
private static var _tickFocused:Float = 0;
|
||||
public static function get_timeSinceFocus():Float {
|
||||
return (FlxG.game.ticks - _tickFocused) / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ class MainState extends FlxState {
|
||||
|
||||
Flags.load();
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); // Loads global.hx
|
||||
MusicBeatTransition.script = Flags.DEFAULT_TRANSITION_SCRIPT;
|
||||
Main.refreshAssets();
|
||||
DiscordUtil.init();
|
||||
EventsData.reloadEvents();
|
||||
|
||||
@@ -126,7 +126,7 @@ class Framerate extends Sprite {
|
||||
bgSprite.scaleY = height;
|
||||
|
||||
var selectable = debugMode == 2;
|
||||
{ // idk i tried to make it more lookable:sob: - Nex
|
||||
{ // idk i tried to make it more readable:sob: - Nex
|
||||
memoryCounter.memoryText.selectable = memoryCounter.memoryPeakText.selectable =
|
||||
fpsCounter.fpsNum.selectable = fpsCounter.fpsLabel.selectable =
|
||||
#if SHOW_BUILD_ON_FPS codenameBuildField.selectable = #end selectable;
|
||||
|
||||
@@ -63,7 +63,7 @@ class SystemInfo extends FramerateCategory {
|
||||
|
||||
cpuName = process.stdout.readAll().toString().trim().split("\n")[1].trim();
|
||||
#elseif mac
|
||||
var process = new HiddenProcess("sysctl -a | grep brand_string"); // Somehow this isnt able to use the args but it still works
|
||||
var process = new HiddenProcess("sysctl -a | grep brand_string"); // Somehow this isn't able to use the args but it still works
|
||||
if (process.exitCode() != 0) throw 'Could not fetch CPU information';
|
||||
|
||||
cpuName = process.stdout.readAll().toString().trim().split(":")[1].trim();
|
||||
|
||||
@@ -7,6 +7,11 @@ import haxe.Json;
|
||||
import haxe.Exception;
|
||||
|
||||
// TODO: Document further and perhaps make this a Haxelib.
|
||||
/**
|
||||
* Set of functions to interact with GitHub.
|
||||
* Requires the `GITHUB_API` macro to be defined.
|
||||
* This has no authentication, so it's limited to unauthenticated requests, and rate limits.
|
||||
**/
|
||||
class GitHub {
|
||||
/**
|
||||
* Gets all the releases from a specific GitHub repository using the GitHub API.
|
||||
|
||||
@@ -21,4 +21,12 @@ typedef GitHubContributor = {
|
||||
var type:String;
|
||||
var site_admin:Bool;
|
||||
var contributions:Int;
|
||||
}
|
||||
|
||||
typedef CreditsGitHubContributor = {
|
||||
var login:String;
|
||||
var avatar_url:String;
|
||||
var html_url:String;
|
||||
var ?id:Int; // Not available in the avatar part
|
||||
var ?contributions:Int; // Not available in the avatar part
|
||||
}
|
||||
@@ -78,9 +78,9 @@ class ControlsMacro
|
||||
});
|
||||
|
||||
function buildBindControl(s:String, prefix:String) {
|
||||
var prefixxed = prefix + s;
|
||||
var prefixed = prefix + s;
|
||||
|
||||
return macro bindKeys(Control.$s, Options.$prefixxed);
|
||||
return macro bindKeys(Control.$s, Options.$prefixed);
|
||||
}
|
||||
|
||||
fields.push({
|
||||
|
||||
@@ -9,7 +9,7 @@ class DefinesMacro {
|
||||
/**
|
||||
* Returns the defined values
|
||||
*/
|
||||
public static var defines(get, null):Map<String, Dynamic>;
|
||||
public static var defines(get, never):Map<String, Dynamic>;
|
||||
|
||||
// GETTERS
|
||||
#if REGION
|
||||
|
||||
@@ -184,7 +184,7 @@ class FlagMacro {
|
||||
}
|
||||
|
||||
case TPath({name: "Array", pack: []}):
|
||||
Context.error("Flag " + field.name + " cannot be an Array that isnt a String or Bool or TrimmedString or Int or Float", field.pos);
|
||||
Context.error("Flag " + field.name + " cannot be an Array that isn't a String or Bool or TrimmedString or Int or Float", field.pos);
|
||||
case TPath({name: "Map", pack: []}):
|
||||
Context.error("Flag " + field.name + " cannot be a Map<K, V>", field.pos);
|
||||
default:
|
||||
|
||||
@@ -42,20 +42,9 @@ class FunkinSaveMacro {
|
||||
/**
|
||||
* SAVE FUNCTION
|
||||
*/
|
||||
var saveFuncBlocks:Array<Expr> = [for(f in fieldNames)
|
||||
macro ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(saveFieldName))
|
||||
}}.data.$f = ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(f))
|
||||
}}
|
||||
];
|
||||
var saveFuncBlocks:Array<Expr> = [for(f in fieldNames) macro $i{saveFieldName}.data.$f = $i{f}];
|
||||
|
||||
saveFuncBlocks.push(macro ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(saveFieldName))
|
||||
}}.flush());
|
||||
saveFuncBlocks.push(macro $i{saveFieldName}.flush());
|
||||
|
||||
fields.push({
|
||||
pos: Context.currentPos(),
|
||||
@@ -81,16 +70,8 @@ class FunkinSaveMacro {
|
||||
expr: {
|
||||
pos: Context.currentPos(),
|
||||
expr: EBlock([for(f in fieldNames)
|
||||
macro if (${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(saveFieldName))
|
||||
}}.data.$f != null) ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(f))
|
||||
}} = ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(saveFieldName))
|
||||
}}.data.$f
|
||||
macro if ($i{saveFieldName}.data.$f != null)
|
||||
$i{f} = $i{saveFieldName}.data.$f
|
||||
])
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -8,11 +8,27 @@ class GitCommitMacro {
|
||||
/**
|
||||
* Returns the current commit number
|
||||
*/
|
||||
public static var commitNumber(get, null):Int;
|
||||
public static var commitNumber(get, never):Int;
|
||||
/**
|
||||
* Returns the current commit hash
|
||||
*/
|
||||
public static var commitHash(get, null):String;
|
||||
public static var commitHash(get, never):String;
|
||||
/**
|
||||
* Returns the current commit hash in long format
|
||||
*/
|
||||
public static var commitHashLong(get, never):String;
|
||||
/**
|
||||
* Returns the current commit message
|
||||
*/
|
||||
public static var commitMessage(get, never):String;
|
||||
/**
|
||||
* Returns the current commit author
|
||||
*/
|
||||
public static var commitAuthor(get, never):String;
|
||||
/**
|
||||
* Returns the current commit date + time
|
||||
**/
|
||||
public static var commitDate(get, never):Date;
|
||||
|
||||
// GETTERS
|
||||
#if REGION
|
||||
@@ -21,6 +37,15 @@ class GitCommitMacro {
|
||||
|
||||
private static inline function get_commitHash()
|
||||
return __getCommitHash();
|
||||
private static inline function get_commitHashLong()
|
||||
return __getCommitHashLong();
|
||||
|
||||
private static inline function get_commitMessage()
|
||||
return __getCommitMessage();
|
||||
private static inline function get_commitAuthor()
|
||||
return __getCommitAuthor();
|
||||
private static inline function get_commitDate()
|
||||
return __getCommitDate();
|
||||
#end
|
||||
|
||||
// INTERNAL MACROS
|
||||
@@ -52,5 +77,77 @@ class GitCommitMacro {
|
||||
return macro $v{0}
|
||||
#end
|
||||
}
|
||||
|
||||
private static macro function __getCommitHashLong() {
|
||||
#if display
|
||||
return macro $v{"-"};
|
||||
#else
|
||||
try {
|
||||
var proc = new Process('git', ['rev-parse', 'HEAD'], false);
|
||||
proc.exitCode(true);
|
||||
|
||||
return macro $v{proc.stdout.readLine()};
|
||||
} catch(e) {}
|
||||
return macro $v{"-"}
|
||||
#end
|
||||
}
|
||||
|
||||
private static macro function __getCommitMessage() {
|
||||
#if display
|
||||
return macro $v{"-"};
|
||||
#else
|
||||
try {
|
||||
var proc = new Process('git', ['log', '-1', '--pretty=%B'], false);
|
||||
proc.exitCode(true);
|
||||
var all = proc.stdout.readAll().toString();
|
||||
while(StringTools.endsWith(all, "\r\n")) all = all.substr(0, all.length - 2);
|
||||
while(StringTools.endsWith(all, "\n")) all = all.substr(0, all.length - 1);
|
||||
while(StringTools.endsWith(all, "\r")) all = all.substr(0, all.length - 1);
|
||||
|
||||
return macro $v{all};
|
||||
} catch(e) {}
|
||||
return macro $v{"-"}
|
||||
#end
|
||||
}
|
||||
|
||||
private static macro function __getCommitAuthor() {
|
||||
#if display
|
||||
return macro $v{"-"};
|
||||
#else
|
||||
try {
|
||||
var proc = new Process('git', ['log', '-1', '--pretty=%an'], false);
|
||||
proc.exitCode(true);
|
||||
|
||||
return macro $v{StringTools.trim(proc.stdout.readLine())};
|
||||
} catch(e) {}
|
||||
return macro $v{"-"}
|
||||
#end
|
||||
}
|
||||
|
||||
private static macro function __getCommitDate() {
|
||||
#if display
|
||||
return macro $v{Date.now()};
|
||||
#else
|
||||
try {
|
||||
// get the time in UTC format
|
||||
var oldTz = Sys.getEnv("TZ");
|
||||
Sys.putEnv("TZ", "UTC0");
|
||||
var proc = new Process('git', ['log', "--date='format-local:%Y-%m-%dT%H:%M:%SZ'", '-1', '--pretty=%cd'], false);
|
||||
proc.exitCode(true);
|
||||
Sys.putEnv("TZ", oldTz);
|
||||
|
||||
var rawDate = StringTools.trim(proc.stdout.readLine());
|
||||
|
||||
try {
|
||||
Date.fromString(rawDate);
|
||||
} catch(e) {
|
||||
throw 'Invalid date format: $rawDate';
|
||||
}
|
||||
|
||||
return macro Date.fromString($v{rawDate});
|
||||
} catch(e) {}
|
||||
return macro Date.fromString("1970-01-01T00:00:00"); // failed to parse date
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -81,8 +81,8 @@ class HashLinkFixer {
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var printer = new haxe.macro.Printer();
|
||||
if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void
|
||||
if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they dont explicitly set :Void
|
||||
if(cl.module == "hl.Gc" && fun.ret == null) returns = false; // fix since they don't explicitly set :Void
|
||||
if(cl.module == "hl.Format" && fun.ret == null) returns = false; // fix since they don't explicitly set :Void
|
||||
|
||||
var name = 'hlf_${f.name}';
|
||||
|
||||
@@ -114,7 +114,7 @@ class HashLinkFixer {
|
||||
default:
|
||||
}
|
||||
|
||||
var fiel:Field = {
|
||||
var field:Field = {
|
||||
name: name,
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
@@ -126,7 +126,7 @@ class HashLinkFixer {
|
||||
access: f.access.copy().filter(function(a) return a != APublic && a != APrivate).concat([APrivate]),
|
||||
meta: meta
|
||||
};
|
||||
fields.push(fiel);
|
||||
fields.push(field);
|
||||
definedFields.push(f.name);
|
||||
|
||||
// Remove meta from original function
|
||||
|
||||
@@ -24,14 +24,8 @@ class OptionsMacro {
|
||||
var ptwoname = 'P2_$name';
|
||||
var newFuncExpr:Expr = macro {
|
||||
var a:Array<FlxKey> = [];
|
||||
for(e in ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(ponename))
|
||||
}}) a.push(e);
|
||||
for(e in ${{
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CIdent(ptwoname))
|
||||
}}) a.push(e);
|
||||
for(e in $i{ponename}) a.push(e);
|
||||
for(e in $i{ptwoname}) a.push(e);
|
||||
return a;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import funkin.backend.utils.NativeAPI;
|
||||
import lime.media.AudioManager;
|
||||
|
||||
/**
|
||||
* if youre stealing this keep this comment at least please lol
|
||||
* if you are stealing this keep this comment at least please lol
|
||||
*
|
||||
* hi gray itsa me yoshicrafter29 i fixed it hehe
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import flixel.sound.FlxSound;
|
||||
import lime.media.AudioBuffer;
|
||||
|
||||
// Thank you yosh :DDD -lunar
|
||||
// (https://github.com/FNF-CNE-Devs/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
|
||||
// (https://github.com/CodenameCrew/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
|
||||
class AudioAnalyzer {
|
||||
public var buffer:AudioBuffer;
|
||||
var __peakByte:Float = 0;
|
||||
|
||||
@@ -41,9 +41,15 @@ import flixel.math.FlxPoint;
|
||||
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* Various utilities, that have no specific Util class.
|
||||
**/
|
||||
@:allow(funkin.game.PlayState)
|
||||
final class CoolUtil
|
||||
{
|
||||
/**
|
||||
* Gets the last exception stack. Useful for debugging.
|
||||
*/
|
||||
public static function getLastExceptionStack():String {
|
||||
return CallStack.toString(CallStack.exceptionStack());
|
||||
}
|
||||
@@ -118,14 +124,14 @@ final class CoolUtil
|
||||
* Gets file attributes from a file or a folder adding eventual missing folders in the path
|
||||
* (WARNING: Only works on `windows` for now. On other platforms the attributes' value it's always going to be `0` -thanks to the wrapper you can also use `isNothing` for checking- but still creates eventual missing folders if the platforms allows it to).
|
||||
* @param path Path to the file or folder
|
||||
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @return The attributes through the `FileAttributeWrapper`
|
||||
*/
|
||||
@:noUsing public static inline function safeGetAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper {
|
||||
@:noUsing public static inline function safeGetAttributes(path:String, useAbsolute:Bool = true):FileAttributeWrapper {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.getFileAttributes(path, useAbsol);
|
||||
var result = NativeAPI.getFileAttributes(path, useAbsolute);
|
||||
if(result.isNothing) Logs.trace('The file where it has been tried to get the attributes from, might be corrupted or inexistent (code: ${result.getValue()})', WARNING);
|
||||
return result;
|
||||
#else
|
||||
@@ -138,15 +144,15 @@ final class CoolUtil
|
||||
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
|
||||
* @param path Path to the file or folder
|
||||
* @param attrib The attribute(s) to set (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
|
||||
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @return The result code: `0` means that it failed setting
|
||||
*/
|
||||
@:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsol:Bool = true):Int {
|
||||
@:noUsing public static inline function safeSetAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsolute:Bool = true):Int {
|
||||
// yes, i'm aware that FileAttribute is also an Int so need to include it too, but at least like this we don't have to make cast sometimes while passing the arguments - Nex
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.setFileAttributes(path, attrib, useAbsol);
|
||||
var result = NativeAPI.setFileAttributes(path, attrib, useAbsolute);
|
||||
if(result == 0) Logs.trace('Failed to set attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
@@ -159,14 +165,14 @@ final class CoolUtil
|
||||
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
|
||||
* @param path Path to the file or folder
|
||||
* @param attrib The attribute(s) to add (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
|
||||
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @return The result code: `0` means that it failed setting
|
||||
*/
|
||||
@:noUsing public static inline function safeAddAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
|
||||
@:noUsing public static inline function safeAddAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.addFileAttributes(path, attrib, useAbsol);
|
||||
var result = NativeAPI.addFileAttributes(path, attrib, useAbsolute);
|
||||
if(result == 0) Logs.trace('Failed to add attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
@@ -179,14 +185,14 @@ final class CoolUtil
|
||||
* (WARNING: Only works on `windows` for now. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to).
|
||||
* @param path Path to the file or folder
|
||||
* @param attrib The attribute(s) to remove (WARNING: There are some non settable attributes, such as the `COMPRESSED` one)
|
||||
* @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @param useAbsolute If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example)
|
||||
* @return The result code: `0` means that it failed setting
|
||||
*/
|
||||
@:noUsing public static inline function safeRemoveAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
|
||||
@:noUsing public static inline function safeRemoveAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
|
||||
#if sys
|
||||
addMissingFolders(Path.directory(path));
|
||||
|
||||
var result = NativeAPI.removeFileAttributes(path, attrib, useAbsol);
|
||||
var result = NativeAPI.removeFileAttributes(path, attrib, useAbsolute);
|
||||
if(result == 0) Logs.trace('Failed to remove attributes to $path with a code of: $result', WARNING);
|
||||
return result;
|
||||
#else
|
||||
@@ -427,7 +433,7 @@ final class CoolUtil
|
||||
var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE;
|
||||
var stepsPerBeat:Int = Flags.DEFAULT_STEPS_PER_BEAT;
|
||||
|
||||
// Check later, i dont think timeSignParsed can contain null, only nan
|
||||
// Check later, i don't think timeSignParsed can contain null, only nan
|
||||
if (timeSignParsed.length == 2 && !timeSignParsed.contains(null)) {
|
||||
beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? Flags.DEFAULT_BEATS_PER_MEASURE : cast timeSignParsed[0];
|
||||
stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? Flags.DEFAULT_STEPS_PER_BEAT : cast timeSignParsed[1];
|
||||
@@ -833,6 +839,15 @@ final class CoolUtil
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quantizes a value to a certain amount.
|
||||
* Example: `quantize(2.5543, 1)` will return `2.0`
|
||||
* Example: `quantize(2.5543, 10)` will return `2.5`
|
||||
* Example: `quantize(2.5543, 100)` will return `2.55`
|
||||
*
|
||||
* @param Value Value to quantize
|
||||
* @param Quant Quantization amount
|
||||
*/
|
||||
@:noUsing public static inline function quantize(Value:Float, Quant:Float) {
|
||||
return Math.fround(Value * Quant) / Quant;
|
||||
}
|
||||
@@ -850,6 +865,11 @@ final class CoolUtil
|
||||
frontEnd.music = music;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the FlxEase from a string.
|
||||
* @param mainEase Main ease
|
||||
* @param suffix Suffix (Ignored if `mainEase` is `linear`)
|
||||
*/
|
||||
@:noUsing public static inline function flxeaseFromString(mainEase:String, suffix:String)
|
||||
return Reflect.field(FlxEase, mainEase + (mainEase == "linear" ? "" : suffix));
|
||||
|
||||
@@ -890,6 +910,11 @@ final class CoolUtil
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array alphabetically.
|
||||
* @param array Array to sort
|
||||
* @param lowercase Whenever the array should be sorted in lowercase
|
||||
*/
|
||||
public static function sortAlphabetically(array:Array<String>, ?lowercase:Bool=false) {
|
||||
array.sort(function(a1, a2):Int {
|
||||
if(lowercase) {
|
||||
@@ -903,6 +928,11 @@ final class CoolUtil
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes an element to an array, but only if it doesn't already exist.
|
||||
* @param array Array to push to
|
||||
* @param element Element to push
|
||||
*/
|
||||
public static inline function pushOnce<T>(array:Array<T>, element:T) {
|
||||
#if (haxe >= "4.0.0")
|
||||
if (!array.contains(element))
|
||||
@@ -914,6 +944,11 @@ final class CoolUtil
|
||||
}
|
||||
|
||||
#if !(haxe >= "4.0.0")
|
||||
/**
|
||||
* Checks if an array contains an element.
|
||||
* @param array Array to check
|
||||
* @param element Element to check
|
||||
*/
|
||||
public static inline function contains<T>(array:Array<T>, element:T) {
|
||||
return array.indexOf(element) != -1;
|
||||
}
|
||||
@@ -992,9 +1027,10 @@ final class CoolUtil
|
||||
* Converts an array of numbers into a string of ranges.
|
||||
* Example: [1,2,3,5,7,8,9,8,7,6,5] -> "1..3,5,7..9,8..5"
|
||||
* @param numbers Array of numbers
|
||||
* @param separator Separator between ranges
|
||||
* @return String representing the ranges
|
||||
*/
|
||||
public static function formatNumberRange(numbers:Array<Int>, seperator:String = ","):String {
|
||||
public static function formatNumberRange(numbers:Array<Int>, separator:String = ","):String {
|
||||
if (numbers.length == 0) return "";
|
||||
|
||||
var result:Array<String> = [];
|
||||
@@ -1031,9 +1067,15 @@ final class CoolUtil
|
||||
i++;
|
||||
}
|
||||
|
||||
return result.join(seperator);
|
||||
return result.join(separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep flattens an array.
|
||||
* Example: `deepFlatten([1, [2, 3], 4])` will return `[1, 2, 3, 4]`
|
||||
* @param arr Array to flatten
|
||||
* @param result Result array
|
||||
*/
|
||||
public static function deepFlatten(arr:Array<Dynamic>, ?result:Array<Dynamic>):Array<Dynamic> {
|
||||
if(arr == null) return [];
|
||||
if(result == null) result = [];
|
||||
@@ -1049,7 +1091,7 @@ final class CoolUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* SFXs to play using `playMenuSFX`.
|
||||
* SFXs to play using `CoolUtil.playMenuSFX`.
|
||||
*/
|
||||
enum abstract CoolSfx(Int) from Int {
|
||||
var SCROLL = 0;
|
||||
|
||||
@@ -19,9 +19,11 @@ class FunkinParentDisabler extends FlxBasic {
|
||||
var __timers:Array<FlxTimer>;
|
||||
var __sounds:Array<FlxSound>;
|
||||
var __replaceUponDestroy:Bool;
|
||||
public function new(replaceUponDestroy:Bool = false) {
|
||||
var __restoreUponDestroy:Bool;
|
||||
public function new(replaceUponDestroy:Bool = false, restoreUponDestroy:Bool = true) {
|
||||
super();
|
||||
__replaceUponDestroy = replaceUponDestroy;
|
||||
__restoreUponDestroy = restoreUponDestroy;
|
||||
@:privateAccess {
|
||||
// tweens
|
||||
__tweens = FlxTween.globalManager._tweens.copy();
|
||||
@@ -53,6 +55,11 @@ class FunkinParentDisabler extends FlxBasic {
|
||||
public override function destroy() {
|
||||
super.destroy();
|
||||
@:privateAccess {
|
||||
if (!__restoreUponDestroy) {
|
||||
for(t in __tweens) { t.cancel(); t.destroy(); };
|
||||
for(t in __timers) { t.cancel(); t.destroy(); };
|
||||
return;
|
||||
}
|
||||
if (__replaceUponDestroy) {
|
||||
FlxTween.globalManager._tweens = __tweens;
|
||||
FlxTimer.globalManager._timers = __timers;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
/**
|
||||
* DOESNT SUPPORT CATEGORIES YET!!
|
||||
* DOESN'T SUPPORT CATEGORIES YET!!
|
||||
*/
|
||||
final class IniUtil {
|
||||
public static inline function parseAsset(assetPath:String, ?defaultVariables:Map<String, String>)
|
||||
|
||||
@@ -14,6 +14,12 @@ import openfl.system.System;
|
||||
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* Tools that are related to memory.
|
||||
* Including garbage collection, and memory usage, and hardware info.
|
||||
*
|
||||
* DISCLAIMER: Hardware info is only available on Native platforms.
|
||||
**/
|
||||
final class MemoryUtil {
|
||||
public static var disableCount:Int = 0;
|
||||
|
||||
@@ -34,12 +40,18 @@ final class MemoryUtil {
|
||||
|
||||
public static function init() {}
|
||||
|
||||
/**
|
||||
* Does a minor garbage collection.
|
||||
*/
|
||||
public static function clearMinor() {
|
||||
#if (cpp || java || neko)
|
||||
Gc.run(false);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a full garbage collection.
|
||||
*/
|
||||
public static function clearMajor() {
|
||||
#if cpp
|
||||
Gc.run(true);
|
||||
@@ -51,18 +63,29 @@ final class MemoryUtil {
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables garbage collection.
|
||||
*/
|
||||
public static function enable() {
|
||||
#if (cpp || hl)
|
||||
Gc.enable(true);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables garbage collection.
|
||||
* Fyi: doesn't fully disable garbage collection, but prevents it from running as much.
|
||||
*/
|
||||
public static function disable() {
|
||||
#if (cpp || hl)
|
||||
Gc.enable(false);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total memory of the system.
|
||||
* Output depends on the hardware.
|
||||
*/
|
||||
public static function getTotalMem():Float
|
||||
{
|
||||
#if windows
|
||||
@@ -76,6 +99,10 @@ final class MemoryUtil {
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current memory usage of the app.
|
||||
* DISCLAIMER: This gets the memory usage that is taken up by Haxe, not the actual memory usage of the app.
|
||||
*/
|
||||
public static inline function currentMemUsage() {
|
||||
#if cpp
|
||||
return Gc.memInfo64(Gc.MEM_INFO_USAGE);
|
||||
@@ -89,6 +116,10 @@ final class MemoryUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the memory type of the system.
|
||||
* Output depends on the platform, and hardware.
|
||||
*/
|
||||
public static function getMemType():String {
|
||||
#if windows
|
||||
var memoryMap:Map<Int, String> = [
|
||||
|
||||
@@ -38,9 +38,9 @@ class NativeAPI {
|
||||
/**
|
||||
* Gets the specified file's (or folder) attributes.
|
||||
*/
|
||||
public static function getFileAttributesRaw(path:String, useAbsol:Bool = true):Int {
|
||||
public static function getFileAttributesRaw(path:String, useAbsolute:Bool = true):Int {
|
||||
#if windows
|
||||
if(useAbsol) path = sys.FileSystem.absolutePath(path);
|
||||
if(useAbsolute) path = sys.FileSystem.absolutePath(path);
|
||||
return Windows.getFileAttributes(path);
|
||||
#else
|
||||
return -1;
|
||||
@@ -50,16 +50,16 @@ class NativeAPI {
|
||||
/**
|
||||
* Gets the specified file's (or folder) attributes and passes it to `FileAttributeWrapper`.
|
||||
*/
|
||||
public static function getFileAttributes(path:String, useAbsol:Bool = true):FileAttributeWrapper {
|
||||
return new FileAttributeWrapper(getFileAttributesRaw(path, useAbsol));
|
||||
public static function getFileAttributes(path:String, useAbsolute:Bool = true):FileAttributeWrapper {
|
||||
return new FileAttributeWrapper(getFileAttributesRaw(path, useAbsolute));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified file's (or folder) attributes. If it fails, the return value is `0`.
|
||||
*/
|
||||
public static function setFileAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsol:Bool = true):Int {
|
||||
public static function setFileAttributes(path:String, attrib:OneOfThree<NativeAPI.FileAttribute, FileAttributeWrapper, Int>, useAbsolute:Bool = true):Int {
|
||||
#if windows
|
||||
if(useAbsol) path = sys.FileSystem.absolutePath(path);
|
||||
if(useAbsolute) path = sys.FileSystem.absolutePath(path);
|
||||
return Windows.setFileAttributes(path, attrib is FileAttributeWrapper ? cast(attrib, FileAttributeWrapper).getValue() : cast(attrib, Int));
|
||||
#else
|
||||
return 0;
|
||||
@@ -69,9 +69,9 @@ class NativeAPI {
|
||||
/**
|
||||
* Removes from the specified file's (or folder) one (or more) specific attribute.
|
||||
*/
|
||||
public static function addFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
|
||||
public static function addFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
|
||||
#if windows
|
||||
return setFileAttributes(path, getFileAttributesRaw(path, useAbsol) | cast(attrib, Int), useAbsol);
|
||||
return setFileAttributes(path, getFileAttributesRaw(path, useAbsolute) | cast(attrib, Int), useAbsolute);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
@@ -80,9 +80,9 @@ class NativeAPI {
|
||||
/**
|
||||
* Removes from the specified file's (or folder) one (or more) specific attribute.
|
||||
*/
|
||||
public static function removeFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsol:Bool = true):Int {
|
||||
public static function removeFileAttributes(path:String, attrib:OneOfTwo<NativeAPI.FileAttribute, Int>, useAbsolute:Bool = true):Int {
|
||||
#if windows
|
||||
return setFileAttributes(path, getFileAttributesRaw(path, useAbsol) & ~cast(attrib, Int), useAbsol);
|
||||
return setFileAttributes(path, getFileAttributesRaw(path, useAbsolute) & ~cast(attrib, Int), useAbsolute);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
|
||||
@@ -38,7 +38,7 @@ final class NdllUtil {
|
||||
return Reflect.makeVarArgs(function(a:Array<Dynamic>) {
|
||||
// This generates horrific code
|
||||
return funkin.backend.system.macros.Utils.generateReflectionLike(25, "func", "a");
|
||||
//return Reflect.callMethod(null, func, a); // wouldnt work for some reason, maybe cause like c++ functions doesnt have reflection enabled
|
||||
//return Reflect.callMethod(null, func, a); // wouldn't work for some reason, maybe cause like c++ functions doesn't have reflection enabled
|
||||
});
|
||||
#else
|
||||
Logs.trace('NDLLs are not supported on this platform.', WARNING);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
/**
|
||||
* All types of utils only usable in a sorted array.
|
||||
* Tools to work with sorted arrays, in a more efficient way.
|
||||
*
|
||||
* DISCLAIMER: All types of utils only usable in a sorted array.
|
||||
*/
|
||||
final class SortedArrayUtil {
|
||||
/**
|
||||
* Gets the index of a possible new element of an Array of T using an efficient algorithm.
|
||||
* @param array Array of T to check in
|
||||
* @param getVal Function that returns the position value of T
|
||||
* @return Index
|
||||
* @return The index of the element
|
||||
*/
|
||||
public static inline function binarySearch<T>(array:Array<T>, val:Float, getVal:T->Float):Int {
|
||||
if (array.length <= 0) return 0; // if the array is empty, it should be equal to zero (the beginning)
|
||||
@@ -46,4 +48,15 @@ final class SortedArrayUtil {
|
||||
if (val != null)
|
||||
array.insert(binarySearch(array, getVal(val), getVal), val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from a sorted array, using binary search.
|
||||
* @param array Array to remove from
|
||||
* @param val Value to remove
|
||||
* @param getVal Function that returns the value that needs to be sorted
|
||||
*/
|
||||
public static inline function removeSorted<T>(array:Array<T>, val:T, getVal:T->Float) {
|
||||
if (val != null)
|
||||
array.splice(binarySearch(array, getVal(val), getVal), 1);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ package funkin.backend.utils;
|
||||
import openfl.Lib;
|
||||
|
||||
final class WindowUtils {
|
||||
public static var winTitle(default, set):String;
|
||||
private static function set_winTitle(value:String):String {
|
||||
winTitle = value;
|
||||
public static var title(default, set):String;
|
||||
private static function set_title(value:String):String {
|
||||
title = value;
|
||||
updateTitle();
|
||||
return value;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ final class WindowUtils {
|
||||
static var __triedClosing:Bool = false;
|
||||
public static inline function resetClosing() __triedClosing = false;
|
||||
|
||||
public static function init() {
|
||||
@:dox(hide) public static inline function init() {
|
||||
resetTitle();
|
||||
resetClosing();
|
||||
|
||||
@@ -41,18 +41,27 @@ final class WindowUtils {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the window title to the application name and resets the prefix and suffix.
|
||||
**/
|
||||
public static inline function resetTitle() {
|
||||
winTitle = Lib.application.meta["name"];
|
||||
title = Lib.application.meta["name"];
|
||||
resetAffixes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the prefix and suffix.
|
||||
**/
|
||||
public static inline function resetAffixes() {
|
||||
prefix = suffix = "";
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the window title to have the current title and prefix/suffix.
|
||||
**/
|
||||
public static inline function updateTitle()
|
||||
Lib.application.window.title = '$prefix$winTitle$suffix';
|
||||
Lib.application.window.title = '$prefix$title$suffix';
|
||||
|
||||
// backwards compat
|
||||
@:noCompletion public static var endfix(get, set):String;
|
||||
@@ -62,4 +71,12 @@ final class WindowUtils {
|
||||
@:noCompletion private static function get_endfix():String {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
@:noCompletion public static var winTitle(get, set):String;
|
||||
@:noCompletion private static function get_winTitle():String {
|
||||
return title;
|
||||
}
|
||||
@:noCompletion private static function set_winTitle(value:String):String {
|
||||
return title = value;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ 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.FunkinSprite.XMLAnimType;
|
||||
import flixel.util.FlxColor;
|
||||
import haxe.xml.Access;
|
||||
@@ -18,17 +17,53 @@ import haxe.xml.Access;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@:dox(hide)
|
||||
enum abstract ErrorCode(Int) {
|
||||
var OK = 0;
|
||||
var FAILED = 1;
|
||||
var MISSING_PROPERTY = 2;
|
||||
var TYPE_INCORRECT = 3;
|
||||
var VALUE_NULL = 4;
|
||||
var REFLECT_ERROR = 5;
|
||||
}
|
||||
|
||||
@:dox(hide)
|
||||
typedef TextFormat = { text:String, format:Dynamic }
|
||||
|
||||
/**
|
||||
* Class made to make XML parsing easier.
|
||||
* Used in Stage.hx, Character.hx, and more.
|
||||
*/
|
||||
final class XMLUtil {
|
||||
|
||||
/**
|
||||
* Applies a property XML node to an object.
|
||||
* The format for the XML is as follows:
|
||||
* ```xml
|
||||
* <!-- float -->
|
||||
* <property name="name" type="float" value="value" />
|
||||
* <property name="name" type="number" value="value" />
|
||||
* <property name="name" type="f" value="value" />
|
||||
* <!-- string -->
|
||||
* <property name="name" type="string" value="value" />
|
||||
* <property name="name" type="str" value="value" />
|
||||
* <property name="name" type="text" value="value" />
|
||||
* <property name="name" type="s" value="value" />
|
||||
* <!-- int -->
|
||||
* <property name="name" type="int" value="value" />
|
||||
* <property name="name" type="integer" value="value" />
|
||||
* <property name="name" type="i" value="value" />
|
||||
* <!-- bool -->
|
||||
* <property name="name" type="bool" value="value" />
|
||||
* <property name="name" type="boolean" value="value" />
|
||||
* <property name="name" type="b" value="value" />
|
||||
* <!-- color -->
|
||||
* <property name="name" type="color" value="value" />
|
||||
* <property name="name" type="c" value="value" />
|
||||
* ```
|
||||
*
|
||||
* @param object Object to which the xml property will be applied
|
||||
* @param property `property` node.
|
||||
* @return Error code (0 = OK, 1 = FAILED, 2 = MISSING_PROPERTY, 3 = TYPE_INCORRECT, 4 = VALUE_NULL, 5 = REFLECT_ERROR)
|
||||
*/
|
||||
public static function applyXMLProperty(object:Dynamic, property:Access):ErrorCode {
|
||||
if (!property.has.name || !property.has.type || !property.has.value) {
|
||||
@@ -47,7 +82,8 @@ final class XMLUtil {
|
||||
|
||||
var value:Dynamic = switch(property.att.type.toLowerCase()) {
|
||||
case "f" | "float" | "number": Std.parseFloat(property.att.value);
|
||||
case "i" | "int" | "integer" | "color": Std.parseInt(property.att.value);
|
||||
case "i" | "int" | "integer": Std.parseInt(property.att.value);
|
||||
case "c" | "color": FlxColor.fromString(property.att.value);
|
||||
case "s" | "string" | "str" | "text": property.att.value;
|
||||
case "b" | "bool" | "boolean": property.att.value.toLowerCase() == "true";
|
||||
default: return TYPE_INCORRECT;
|
||||
@@ -72,7 +108,11 @@ final class XMLUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a sprite based on a XML node.
|
||||
* Sets the properties of a sprite based on a XML node.
|
||||
* @param spr The sprite
|
||||
* @param node The XML node
|
||||
* @param parentFolder The parent folder
|
||||
* @param defaultAnimType The default animation type
|
||||
*/
|
||||
public static function loadSpriteFromXML(spr:FunkinSprite, node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT):FunkinSprite {
|
||||
if (parentFolder == null) parentFolder = "";
|
||||
@@ -147,8 +187,8 @@ final class XMLUtil {
|
||||
var graphicSizey:Null<Int> = Std.parseInt(node.att.graphicSizey);
|
||||
if (graphicSizey.isNotNull()) spr.setGraphicSize(0, graphicSizey);
|
||||
}
|
||||
|
||||
if (node.has.flipX && node.att.flipX == "true") spr.flipX = true;
|
||||
if (node.has.flipX) spr.flipX = node.att.flipX == "true";
|
||||
if (node.has.flipY) spr.flipY = node.att.flipY == "true";
|
||||
if (node.has.updateHitbox && node.att.updateHitbox == "true") spr.updateHitbox();
|
||||
|
||||
if (node.has.zoomfactor)
|
||||
@@ -175,19 +215,17 @@ final class XMLUtil {
|
||||
if(node.hasNode.anim) {
|
||||
for(anim in node.nodes.anim)
|
||||
addXMLAnimation(spr, anim);
|
||||
} else {
|
||||
if (spr.frames != null && spr.frames.frames != null) {
|
||||
addAnimToSprite(spr, {
|
||||
name: "idle",
|
||||
anim: null,
|
||||
fps: 24,
|
||||
loop: spr.spriteAnimType == LOOP,
|
||||
animType: spr.spriteAnimType,
|
||||
x: 0,
|
||||
y: 0,
|
||||
indices: [for(i in 0...spr.frames.frames.length) i]
|
||||
});
|
||||
}
|
||||
} else if (spr.frames != null && spr.frames.frames != null) {
|
||||
addAnimToSprite(spr, {
|
||||
name: "idle",
|
||||
anim: null,
|
||||
fps: 24,
|
||||
loop: spr.spriteAnimType == LOOP,
|
||||
animType: spr.spriteAnimType,
|
||||
x: 0,
|
||||
y: 0,
|
||||
indices: [for(i in 0...spr.frames.frames.length) i]
|
||||
});
|
||||
}
|
||||
|
||||
return spr;
|
||||
@@ -195,6 +233,11 @@ final class XMLUtil {
|
||||
|
||||
/**
|
||||
* Creates a new sprite based on a XML node.
|
||||
* @param node The XML node
|
||||
* @param parentFolder The parent folder
|
||||
* @param defaultAnimType The default animation type
|
||||
* @param cl The class to create (advanced)
|
||||
* @param args The arguments to pass to the class (advanced)
|
||||
*/
|
||||
public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class<FunkinSprite>, ?args:Array<Dynamic>):FunkinSprite {
|
||||
if(cl == null) cl = FunkinSprite;
|
||||
@@ -202,6 +245,12 @@ final class XMLUtil {
|
||||
return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an animation from an XML node.
|
||||
* @param anim The animation node
|
||||
* @param animType The animation type
|
||||
* @param loop Whether the animation should loop
|
||||
**/
|
||||
public static function extractAnimFromXML(anim:Access, animType:XMLAnimType = NONE, loop:Bool = false):AnimData {
|
||||
var animData:AnimData = {
|
||||
name: null,
|
||||
@@ -240,6 +289,11 @@ final class XMLUtil {
|
||||
return addAnimToSprite(sprite, extractAnimFromXML(anim, animType, loop));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an animation to a sprite.
|
||||
* @param sprite The sprite
|
||||
* @param animData The animation data (gotten from `extractAnimFromXML`)
|
||||
**/
|
||||
public static function addAnimToSprite(sprite:FlxSprite, animData:AnimData):ErrorCode {
|
||||
if (animData.name != null) {
|
||||
if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;
|
||||
@@ -249,16 +303,17 @@ final class XMLUtil {
|
||||
if(animData.anim == null)
|
||||
return MISSING_PROPERTY;
|
||||
|
||||
if (animData.indices.length > 0)
|
||||
if (animData.indices != null && animData.indices.length > 0)
|
||||
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
|
||||
else
|
||||
animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
|
||||
} else {
|
||||
if (animData.anim == null && animData.indices.length > 0)
|
||||
sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
|
||||
else if (animData.indices.length > 0)
|
||||
sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
|
||||
else
|
||||
if (animData.indices != null && animData.indices.length > 0) {
|
||||
if (animData.anim == null)
|
||||
sprite.animation.add(animData.name, animData.indices, animData.fps, animData.loop);
|
||||
else
|
||||
sprite.animation.addByIndices(animData.name, animData.anim, animData.indices, "", animData.fps, animData.loop);
|
||||
} else
|
||||
sprite.animation.addByPrefix(animData.name, animData.anim, animData.fps, animData.loop);
|
||||
}
|
||||
|
||||
@@ -314,6 +369,17 @@ final class XMLUtil {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text formats from an XML node.
|
||||
* The format for the XML is as follows:
|
||||
* ```xml
|
||||
* <text>Hello <format color="#FF0000">World</format></text>
|
||||
* ```
|
||||
*
|
||||
* @param _node The XML node
|
||||
* @param currentFormat The current format
|
||||
* @param parsedSegments The parsed segments
|
||||
**/
|
||||
public static function getTextFormats(_node:OneOfTwo<Xml, Access>, currentFormat:Dynamic = null, parsedSegments:Array<TextFormat> = null):Array<TextFormat> {
|
||||
var node:Xml = cast _node;
|
||||
if (currentFormat == null)
|
||||
@@ -406,4 +472,4 @@ typedef BeatAnim = {
|
||||
|
||||
interface IXMLEvents {
|
||||
public function onPropertySet(property:String, value:Dynamic):Void;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ import sys.thread.Thread;
|
||||
using StringTools;
|
||||
|
||||
// import ZipUtils; ZipUtils.uncompressZip(ZipUtils.openZip("E:\\Desktop\\test\\termination lua.ycemod"), "E:\\Desktop\\test\\uncompressed\\");
|
||||
// import ZipUtils; var e = ZipUtils.createZipFile("gjnsdghs.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
|
||||
// import ZipUtils; var e = ZipUtils.createZipFile("file.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
|
||||
|
||||
final class ZipUtil {
|
||||
public static var bannedNames:Array<String> = [".git", ".gitignore", ".github", ".vscode", ".gitattributes", "readme.txt"];
|
||||
|
||||
/**
|
||||
* [Description] Uncompresses `zip` into the `destFolder` folder
|
||||
* [Description] Decompresses a `zip` into the `destFolder` folder
|
||||
* @param zip
|
||||
* @param destFolder
|
||||
*/
|
||||
@@ -218,7 +218,7 @@ class ZipProgress {
|
||||
public var curFile:Int = 0;
|
||||
public var fileCount:Int = 0;
|
||||
public var done:Bool = false;
|
||||
public var percentage(get, null):Float;
|
||||
public var percentage(get, never):Float;
|
||||
|
||||
private function get_percentage() {
|
||||
return fileCount <= 0 ? 0 : curFile / fileCount;
|
||||
|
||||
@@ -13,7 +13,7 @@ import funkin.backend.utils.NativeAPI.MessageBoxIcon;
|
||||
</target>
|
||||
')
|
||||
|
||||
// majority is taken from microsofts doc
|
||||
// majority is taken from Microsoft's doc
|
||||
@:cppFileCode('
|
||||
#include "mmdeviceapi.h"
|
||||
#include "combaseapi.h"
|
||||
@@ -132,7 +132,7 @@ final class Windows {
|
||||
int darkMode = enable ? 1 : 0;
|
||||
|
||||
HWND window = FindWindowA(NULL, title.c_str());
|
||||
// Look for child windows if top level aint found
|
||||
// Look for child windows if top level is not found
|
||||
if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str());
|
||||
// If still not found, try to get the active window
|
||||
if (window == NULL) window = GetActiveWindow();
|
||||
|
||||
@@ -41,11 +41,11 @@ class DebugOptionsScreen extends OptionsScreen {
|
||||
"editorsResizable"));
|
||||
add(new Checkbox(
|
||||
"Editor SFXs",
|
||||
"If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)",
|
||||
"If checked, will play sound effects when working on editors (ex: will play SFXs when checking checkboxes...)",
|
||||
"editorSFX"));
|
||||
add(new Checkbox(
|
||||
"Pretty Print",
|
||||
"If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to xmls...)",
|
||||
"If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to XMLs...)",
|
||||
"editorPrettyPrint"));
|
||||
add(new Checkbox(
|
||||
"Intensive Blur",
|
||||
@@ -69,7 +69,7 @@ class DebugOptionsScreen extends OptionsScreen {
|
||||
));
|
||||
add(new Checkbox(
|
||||
"Autosaves Folder",
|
||||
"If checked, this will autosave your file in a seperate folder with a time stamp instead of overriding your current file. (song/autosaves/)",
|
||||
"charterAutoSavesSeperateFolder"));
|
||||
"If checked, this will autosave your file in a separate folder with a time stamp instead of overriding your current file. (song/autosaves/)",
|
||||
"charterAutoSavesSeparateFolder"));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class CharacterEditor extends UIState {
|
||||
static var __character:String;
|
||||
public var character:CharacterGhost;
|
||||
|
||||
public static var instance(get, null):CharacterEditor;
|
||||
public static var instance(get, never):CharacterEditor;
|
||||
|
||||
private static inline function get_instance()
|
||||
return FlxG.state is CharacterEditor ? cast FlxG.state : null;
|
||||
|
||||
@@ -44,7 +44,7 @@ class CharacterSelection extends EditorTreeMenu
|
||||
}
|
||||
|
||||
list.insert(0, new NewOption("New Character", "New Character", function() {
|
||||
openSubState(new UIWarningSubstate("New Character: Feature Not Implemented!", "This feature isnt implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
|
||||
openSubState(new UIWarningSubstate("New Character: Feature Not Implemented!", "This feature isn't implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
}));
|
||||
|
||||
@@ -61,7 +61,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
scrollSpeedTextBox.onChange = function (text:String) {
|
||||
@:privateAccess scrollSpeedTextBox.__onChange(text);
|
||||
for (button in strumLineList.buttons.members)
|
||||
if (button.usesChartscrollSpeed.checked)
|
||||
if (button.usesChartScrollSpeed.checked)
|
||||
button.scrollSpeedStepper.value = scrollSpeedTextBox.value;
|
||||
}
|
||||
add(scrollSpeedTextBox);
|
||||
@@ -148,7 +148,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
@:privateAccess stepper.__onChange(stepper.label.text);
|
||||
|
||||
strumLines.push({
|
||||
characters: [for (charb in strline.charactersList.buttons.members) charb.textBox.label.text],
|
||||
characters: [for (char in strline.charactersList.buttons.members) char.textBox.label.text],
|
||||
type: strline.typeDropdown.index,
|
||||
notes: [],
|
||||
position: strline.stagePositionDropdown.label.text.toLowerCase(),
|
||||
@@ -156,7 +156,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
strumPos: [0, strline.hudYStepper.value],
|
||||
strumLinePos: strline.hudXStepper.value,
|
||||
strumScale: strline.hudScaleStepper.value,
|
||||
scrollSpeed: strline.usesChartscrollSpeed.checked ? strline.scrollSpeedStepper.value : null
|
||||
scrollSpeed: strline.usesChartScrollSpeed.checked ? strline.scrollSpeedStepper.value : null
|
||||
});
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ class StrumLineButton extends UIButton {
|
||||
public var hudYStepper:UINumericStepper;
|
||||
public var visibleCheckbox:UICheckbox;
|
||||
public var scrollSpeedStepper:UINumericStepper;
|
||||
public var usesChartscrollSpeed:UICheckbox;
|
||||
public var usesChartScrollSpeed:UICheckbox;
|
||||
|
||||
public var deleteButton:UIButton;
|
||||
public var deleteIcon:FlxSprite;
|
||||
@@ -260,15 +260,15 @@ class StrumLineButton extends UIButton {
|
||||
members.push(scrollSpeedStepper);
|
||||
addLabelOn(scrollSpeedStepper, "Scroll Speed");
|
||||
|
||||
usesChartscrollSpeed = new UICheckbox(scrollSpeedStepper.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartscrollSpeed.onChecked = function(b) {
|
||||
usesChartScrollSpeed = new UICheckbox(scrollSpeedStepper.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed.onChecked = function(b) {
|
||||
if(b) {
|
||||
scrollSpeedStepper.value = subState.scrollSpeedTextBox.value;
|
||||
scrollSpeedStepper.selectable = false;
|
||||
} else
|
||||
scrollSpeedStepper.selectable = true;
|
||||
}
|
||||
members.push(usesChartscrollSpeed);
|
||||
members.push(usesChartScrollSpeed);
|
||||
|
||||
deleteButton = new UIButton(16, 246-32-11, null, function () {
|
||||
parent.remove(this);
|
||||
@@ -293,7 +293,7 @@ class StrumLineButton extends UIButton {
|
||||
hudYStepper.follow(this, hudXStepper.x + 84 - 32 + 26, 8+26 + 64);
|
||||
visibleCheckbox.follow(this, hudYStepper.x + hudYStepper.bWidth + 42, 8+26 + 64 + 9);
|
||||
scrollSpeedStepper.follow(this, typeDropdown.x, 8+26 + 128);
|
||||
usesChartscrollSpeed.follow(this, scrollSpeedStepper.x + 104, 8+26 + 135);
|
||||
usesChartScrollSpeed.follow(this, scrollSpeedStepper.x + 104, 8+26 + 135);
|
||||
|
||||
deleteButton.follow(this, 16, 246-32-11);
|
||||
deleteIcon.follow(this, 16 + ((deleteButton.bWidth/2)-(15/2)), (246-32-11) + ((deleteButton.bHeight/2)-(16/2)));
|
||||
|
||||
@@ -24,11 +24,11 @@ class Charter extends UIState {
|
||||
static var __diff:String;
|
||||
static var __reload:Bool;
|
||||
|
||||
var chart(get, null):ChartData;
|
||||
var chart(get, never):ChartData;
|
||||
private function get_chart()
|
||||
return PlayState.SONG;
|
||||
|
||||
public static var instance(get, null):Charter;
|
||||
public static var instance(get, never):Charter;
|
||||
|
||||
private static inline function get_instance()
|
||||
return FlxG.state is Charter ? cast FlxG.state : null;
|
||||
@@ -756,10 +756,10 @@ class Charter extends UIState {
|
||||
autoSaveTimer -= elapsed;
|
||||
|
||||
if (autoSaveTimer < Options.charterAutoSaveWarningTime && !autoSaveNotif.cancelled && !autoSaveNotif.showedAnimation) {
|
||||
if (Options.charterAutoSavesSeperateFolder)
|
||||
if (Options.charterAutoSavesSeparateFolder)
|
||||
__autoSaveLocation = __diff.toLowerCase() + DateTools.format(Date.now(), "%m-%d_%H-%M");
|
||||
autoSaveNotif.startAutoSave(autoSaveTimer,
|
||||
!Options.charterAutoSavesSeperateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' :
|
||||
!Options.charterAutoSavesSeparateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' :
|
||||
'Saved chart at $__autoSaveLocation.json!'
|
||||
);
|
||||
}
|
||||
@@ -769,7 +769,7 @@ class Charter extends UIState {
|
||||
buildChart();
|
||||
var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}';
|
||||
|
||||
if (Options.charterAutoSavesSeperateFolder)
|
||||
if (Options.charterAutoSavesSeparateFolder)
|
||||
Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: false, folder: "autosaves", prettyPrint: Options.editorPrettyPrint});
|
||||
else
|
||||
Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, prettyPrint: Options.editorPrettyPrint});
|
||||
@@ -865,7 +865,7 @@ class Charter extends UIState {
|
||||
|
||||
var boundedChange:FlxPoint = changePoint.clone();
|
||||
|
||||
// Some maths, so cool bro -lunar (i dont know why i quopte my self here)
|
||||
// Some maths, so cool bro -lunar (i don't know why i quote my self here)
|
||||
if (s.step + changePoint.x < 0) boundedChange.x += Math.abs(s.step + changePoint.x);
|
||||
if (s.step + changePoint.x > __endStep-1) boundedChange.x -= (s.step + changePoint.x) - (__endStep-1);
|
||||
|
||||
@@ -1794,7 +1794,7 @@ class Charter extends UIState {
|
||||
inline function _snap_resetsnap(_) setquant(16);
|
||||
|
||||
inline function changequant(change:Int) {quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
|
||||
inline function setquant(newquant:Int) {quant = newquant; buildSnapsUI();}
|
||||
inline function setquant(newQuant:Int) {quant = newQuant; buildSnapsUI();}
|
||||
|
||||
function buildSnapsUI():Array<UIContextMenuOption> {
|
||||
var snapsTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[snapIndex];
|
||||
@@ -2001,7 +2001,7 @@ class Charter extends UIState {
|
||||
return newSelection.filter((s:ICharterSelectable) -> {return s != null;});
|
||||
}
|
||||
|
||||
// UH OH!!! DANGER ZONE APPOARCHING !!!! LUNARS SHITTY CODE !!!! -lunar
|
||||
// UH OH!!! DANGER ZONE APPROACHING !!!! LUNARS SHITTY CODE !!!! -lunar
|
||||
|
||||
@:noCompletion public function __relinkSingleSelection(selectable:ICharterSelectable):ICharterSelectable {
|
||||
if (selectable is CharterNote)
|
||||
|
||||
@@ -63,7 +63,7 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
var __timer:FlxTimer;
|
||||
var __tween:FlxTween;
|
||||
|
||||
public function startAutoSave(time:Float, sucessText:String) {
|
||||
public function startAutoSave(time:Float, successText:String) {
|
||||
appearAnimation();
|
||||
|
||||
__tween = FlxTween.num(0, 1, time, null, (v:Float) -> {
|
||||
@@ -74,7 +74,7 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
});
|
||||
__timer = new FlxTimer();
|
||||
__timer.start(time, (_) -> {
|
||||
autosavingText.text = sucessText; cancelButton.visible = false;
|
||||
autosavingText.text = successText; cancelButton.visible = false;
|
||||
|
||||
icon.animation.curAnim.curFrame = 1;
|
||||
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
|
||||
|
||||
@@ -23,7 +23,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficulitesTextBox:UITextBox;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
|
||||
public function new(metadata:ChartMetaData) {
|
||||
super();
|
||||
@@ -88,9 +88,9 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
add(colorWheel);
|
||||
addLabelOn(colorWheel, "Color");
|
||||
|
||||
difficulitesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", "));
|
||||
add(difficulitesTextBox);
|
||||
addLabelOn(difficulitesTextBox, "Difficulties");
|
||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", "));
|
||||
add(difficultiesTextBox);
|
||||
addLabelOn(difficultiesTextBox, "Difficulties");
|
||||
|
||||
customPropertiesButtonList = new UIButtonList<PropertyButton>(stepsPerBeatStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5);
|
||||
customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
@@ -162,7 +162,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
parsedColor: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficulitesTextBox.label.text.split(",")) diff.trim()],
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
customValues: customVals,
|
||||
};
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
public function updatePos(step:Float, id:Int, susLength:Float = 0, ?type:Int = 0, ?strumLine:CharterStrumline = null) {
|
||||
this.step = step;
|
||||
this.id = id;
|
||||
this.susLength = susLength;
|
||||
this.susLength = Math.max(susLength, 0);
|
||||
this.type = type;
|
||||
if (strumLine != null) this.strumLine = strumLine;
|
||||
|
||||
|
||||
@@ -89,10 +89,10 @@ class CharterSelection extends EditorTreeMenu {
|
||||
}
|
||||
|
||||
public function saveSong(creation:SongCreationData) {
|
||||
var songAlreadlyExsits:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase());
|
||||
var songAlreadyExists:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase());
|
||||
|
||||
if (songAlreadlyExsits) {
|
||||
openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create alreadly exists, if you would like to override it delete the song first!", [
|
||||
if (songAlreadyExists) {
|
||||
openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create Already exists, if you would like to override it delete the song first!", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
return;
|
||||
@@ -134,10 +134,10 @@ class CharterSelection extends EditorTreeMenu {
|
||||
}
|
||||
|
||||
public function saveChart(name:String, data:ChartData) {
|
||||
var difficultyAlreadlyExsits:Bool = curSong.difficulties.contains(name);
|
||||
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
|
||||
|
||||
if (difficultyAlreadlyExsits) {
|
||||
openSubState(new UIWarningSubstate("Creating Chart: Error!", "The chart you are trying to create alreadly exists, if you would like to override it delete the chart first!", [
|
||||
if (difficultyAlreadyExists) {
|
||||
openSubState(new UIWarningSubstate("Creating Chart: Error!", "The chart you are trying to create already exists, if you would like to override it delete the chart first!", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
public var hudYStepper:UINumericStepper;
|
||||
public var visibleCheckbox:UICheckbox;
|
||||
public var scrollSpeedStepper:UINumericStepper;
|
||||
public var usesChartscrollSpeed:UICheckbox;
|
||||
public var usesChartScrollSpeed:UICheckbox;
|
||||
public var keyCountStepper:UINumericStepper;
|
||||
public var hudSpacingStepper:UINumericStepper;
|
||||
|
||||
@@ -88,8 +88,8 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
add(typeDropdown);
|
||||
addLabelOn(typeDropdown, "Type");
|
||||
|
||||
usesChartscrollSpeed = new UICheckbox(typeDropdown.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartscrollSpeed.onChecked = function(b) {
|
||||
usesChartScrollSpeed = new UICheckbox(typeDropdown.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed.onChecked = function(b) {
|
||||
if(b)
|
||||
{
|
||||
scrollSpeedStepper.value = PlayState.SONG.scrollSpeed;
|
||||
@@ -98,10 +98,10 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
scrollSpeedStepper.selectable = true;
|
||||
}
|
||||
}
|
||||
add(usesChartscrollSpeed);
|
||||
add(usesChartScrollSpeed);
|
||||
|
||||
scrollSpeedStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 128, usesChartscrollSpeed.checked ? PlayState.SONG.scrollSpeed : strumLine.scrollSpeed, 0.1, 2, 0, 10, 82);
|
||||
if(usesChartscrollSpeed.checked)
|
||||
scrollSpeedStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 128, usesChartScrollSpeed.checked ? PlayState.SONG.scrollSpeed : strumLine.scrollSpeed, 0.1, 2, 0, 10, 82);
|
||||
if(usesChartScrollSpeed.checked)
|
||||
{
|
||||
scrollSpeedStepper.selectable = false;
|
||||
} else {
|
||||
@@ -155,20 +155,20 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
add(closeButton);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
|
||||
var suffixlist = ["NONE"];
|
||||
var suffixList = ["NONE"];
|
||||
for (i in Paths.getFolderContent('songs/${Charter.__song.toLowerCase()}/song'))
|
||||
if (i.startsWith("Voices")) {
|
||||
i = haxe.io.Path.withoutExtension(i.substr("Voices".length));
|
||||
if (i == "") continue;
|
||||
for (j in PlayState.SONG.meta.difficulties) {
|
||||
if (i.endsWith('-${j.toLowerCase()}'))
|
||||
suffixlist.pushOnce(i.substring(0, i.length - j.length));
|
||||
suffixList.pushOnce(i.substring(0, i.length - j.length));
|
||||
else
|
||||
suffixlist.pushOnce(i);
|
||||
suffixList.pushOnce(i);
|
||||
}
|
||||
}
|
||||
|
||||
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixlist, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixlist.indexOf(strumLine.vocalsSuffix) : 0);
|
||||
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
|
||||
add(vocalsSuffixDropDown);
|
||||
addLabelOn(vocalsSuffixDropDown, "Vocal Suffix");
|
||||
|
||||
@@ -209,13 +209,13 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
vocalsSuffix: vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] != "NONE" ? vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] : "",
|
||||
keyCount: Std.int(keyCountStepper.value)
|
||||
};
|
||||
if(!usesChartscrollSpeed.checked) newStrumLine.scrollSpeed = scrollSpeedStepper.value;
|
||||
if(!usesChartScrollSpeed.checked) newStrumLine.scrollSpeed = scrollSpeedStepper.value;
|
||||
if (onSave != null) onSave(newStrumLine);
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float) {
|
||||
var scrollSpeed:Float = 0.0;
|
||||
if (usesChartscrollSpeed.hovered || scrollSpeedStepper.hovered || scrollSpeedStepper.focused)
|
||||
if (usesChartScrollSpeed.hovered || scrollSpeedStepper.hovered || scrollSpeedStepper.focused)
|
||||
scrollSpeed = scrollSpeedStepper.value;
|
||||
|
||||
previewStrumLine.visible = visibleCheckbox.checked;
|
||||
|
||||
@@ -28,7 +28,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficulitesTextBox:UITextBox;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
|
||||
public var backButton:UIButton;
|
||||
public var saveButton:UIButton;
|
||||
@@ -125,9 +125,9 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
menuDataGroup.add(colorWheel);
|
||||
addLabelOn(colorWheel, "Color");
|
||||
|
||||
difficulitesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, "");
|
||||
menuDataGroup.add(difficulitesTextBox);
|
||||
addLabelOn(difficulitesTextBox, "Difficulties");
|
||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, "");
|
||||
menuDataGroup.add(difficultiesTextBox);
|
||||
addLabelOn(difficultiesTextBox, "Difficulties");
|
||||
|
||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
||||
{checkbox.y += 6; checkbox.x += 4;}
|
||||
@@ -240,7 +240,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
parsedColor: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficulitesTextBox.label.text.split(",")) diff.trim()],
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
};
|
||||
|
||||
if (onSave != null) onSave({
|
||||
|
||||
@@ -44,10 +44,10 @@ class UIColorwheel extends UISliceSprite {
|
||||
|
||||
// Im too lazy to use a shader for this if its not even gonna change
|
||||
colorSlider.pixels.lock();
|
||||
for (pixely in 0...Std.int(colorSlider.height)) {
|
||||
var color:Int = FlxColor.fromHSB(pixely / (colorSlider.height-1) * 360, 1, 1);
|
||||
for (pixelx in 0...Std.int(colorSlider.width))
|
||||
if (colorSlider.pixels.getPixel32(pixelx, pixely) != FlxColor.TRANSPARENT) colorSlider.pixels.setPixel32(pixelx, pixely, color);
|
||||
for (pixelY in 0...Std.int(colorSlider.height)) {
|
||||
var color:Int = FlxColor.fromHSB(pixelY / (colorSlider.height-1) * 360, 1, 1);
|
||||
for (pixelX in 0...Std.int(colorSlider.width))
|
||||
if (colorSlider.pixels.getPixel32(pixelX, pixelY) != FlxColor.TRANSPARENT) colorSlider.pixels.setPixel32(pixelX, pixelY, color);
|
||||
}
|
||||
colorSlider.pixels.unlock();
|
||||
|
||||
@@ -124,7 +124,7 @@ class UIColorwheel extends UISliceSprite {
|
||||
public var colorChanged:Bool = false;
|
||||
|
||||
// Make the colorwheel feel better
|
||||
static inline var hitBoxExtenstion:Float = 8;
|
||||
static inline var hitBoxExtension:Float = 8;
|
||||
|
||||
// Skibidi
|
||||
var selectedSprite = null;
|
||||
@@ -133,7 +133,7 @@ class UIColorwheel extends UISliceSprite {
|
||||
if (hovered && FlxG.mouse.justPressed) {
|
||||
for (sprite in [colorPicker, colorSlider]) {
|
||||
var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
|
||||
if (FlxMath.inBounds(mousePos.x, spritePos.x - (hitBoxExtenstion/2), spritePos.x - (hitBoxExtenstion/2) + (sprite.width + hitBoxExtenstion)) && FlxMath.inBounds(mousePos.y, spritePos.y - (hitBoxExtenstion/2), spritePos.y - (hitBoxExtenstion/2) + (sprite.height + hitBoxExtenstion))) {
|
||||
if (FlxMath.inBounds(mousePos.x, spritePos.x - (hitBoxExtension/2), spritePos.x - (hitBoxExtension/2) + (sprite.width + hitBoxExtension)) && FlxMath.inBounds(mousePos.y, spritePos.y - (hitBoxExtension/2), spritePos.y - (hitBoxExtension/2) + (sprite.height + hitBoxExtension))) {
|
||||
selectedSprite = sprite;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import flixel.util.FlxColor;
|
||||
using StringTools;
|
||||
using flixel.util.FlxSpriteUtil;
|
||||
|
||||
typedef SliderSegement = {
|
||||
typedef SliderSegment = {
|
||||
var start:Float;
|
||||
var end:Float;
|
||||
var size:Float;
|
||||
}
|
||||
|
||||
class UISlider extends UISprite {
|
||||
public var segments:Array<SliderSegement> = [];
|
||||
public var segments:Array<SliderSegment> = [];
|
||||
public var barWidth:Int = 120;
|
||||
|
||||
public var progressbar:UISprite;
|
||||
@@ -36,7 +36,7 @@ class UISlider extends UISprite {
|
||||
public var valueStepper:UINumericStepper;
|
||||
public var onChange:Float->Void;
|
||||
|
||||
public function new(x:Float, y:Float, width:Int = 120, value:Float, segments:Array<SliderSegement>, centered:Bool) {
|
||||
public function new(x:Float, y:Float, width:Int = 120, value:Float, segments:Array<SliderSegment>, centered:Bool) {
|
||||
this.segments = segments;
|
||||
this.barWidth = width;
|
||||
this.progressCentered = centered;
|
||||
|
||||
@@ -17,7 +17,10 @@ import openfl.ui.MouseCursor;
|
||||
class UIState extends MusicBeatState {
|
||||
public var curContextMenu:UIContextMenu = null;
|
||||
|
||||
public static var state(get, null):UIState;
|
||||
public static var state(get, never):UIState;
|
||||
|
||||
private inline static function get_state()
|
||||
return FlxG.state is UIState ? cast FlxG.state : null;
|
||||
|
||||
public var buttonHandler:Void->Void = null;
|
||||
public var hoveredSprite:UISprite = null;
|
||||
@@ -32,9 +35,6 @@ class UIState extends MusicBeatState {
|
||||
|
||||
static var __point:FlxPoint = new FlxPoint();
|
||||
|
||||
private inline static function get_state()
|
||||
return FlxG.state is UIState ? cast FlxG.state : null;
|
||||
|
||||
public override function create() {
|
||||
__rect = new FlxRect();
|
||||
__mousePos = FlxPoint.get();
|
||||
|
||||
@@ -119,12 +119,21 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
case END:
|
||||
position = label.text.length;
|
||||
case V:
|
||||
if (modifier == KeyModifier.LEFT_CTRL || modifier == KeyModifier.RIGHT_CTRL) {
|
||||
// paste
|
||||
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
|
||||
if (data != null)
|
||||
onTextInput(data);
|
||||
}
|
||||
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
|
||||
// but somehow it gave a int outside of the KeyModifier's range :sob:
|
||||
// apparently there is a boolean that just checks for you. yw :D
|
||||
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey) return;
|
||||
// we pasting
|
||||
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
|
||||
if (data != null) onTextInput(data);
|
||||
case C:
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey) return;
|
||||
|
||||
// copying
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class UIUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all options with shortcuts present in a `Array<UIContextMenuOption>`. Also checks childrens.
|
||||
* Process all options with shortcuts present in a `Array<UIContextMenuOption>`. Also checks children.
|
||||
* @param topMenuOptions
|
||||
*/
|
||||
public static function processShortcuts(topMenuOptions:Array<UIContextMenuOption>) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class GameOverSubstate extends MusicBeatSubstate
|
||||
|
||||
var camFollow:FlxObject;
|
||||
|
||||
public static var script:String = "";
|
||||
public static var script:String = Flags.DEFAULT_GAMEOVER_SCRIPT;
|
||||
|
||||
public var gameoverScript:Script;
|
||||
public var game:PlayState = PlayState.instance; // shortcut
|
||||
|
||||
@@ -2,7 +2,14 @@ package funkin.game;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
/**
|
||||
* Camera meant for PlayState hud, allows for flipping the camera.
|
||||
**/
|
||||
class HudCamera extends FlxCamera {
|
||||
/**
|
||||
* Whenever the camera should flip the y axis.
|
||||
* Keeps the sprites not flipped, but the positions are flipped.
|
||||
*/
|
||||
public var downscroll:Bool = false;
|
||||
//public override function update(elapsed:Float) {
|
||||
// super.update(elapsed);
|
||||
|
||||
@@ -75,7 +75,7 @@ class Note extends FlxSprite
|
||||
public var scrollSpeed:Null<Float> = null;
|
||||
public var noteAngle:Null<Float> = null;
|
||||
|
||||
public var noteType(get, null):String;
|
||||
public var noteType(get, never):String;
|
||||
|
||||
@:dox(hide) public var __strumCameras:Array<FlxCamera> = null;
|
||||
@:dox(hide) public var __strum:Strum = null;
|
||||
|
||||
@@ -3,6 +3,10 @@ package funkin.game;
|
||||
import flixel.util.FlxSort;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
/**
|
||||
* Group of notes, that handles updating and rendering only the visible notes.
|
||||
* To only get the visible notes you gotta do `group.forEach()` or `group.forEachAlive()` instead of `group.members`.
|
||||
**/
|
||||
class NoteGroup extends FlxTypedGroup<Note> {
|
||||
var __loopSprite:Note;
|
||||
var i:Int = 0;
|
||||
@@ -10,7 +14,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
|
||||
var __time:Float = -1.0;
|
||||
|
||||
/**
|
||||
* How many ms it should show a note before it should be hit
|
||||
* How many milliseconds it should show a note before it should be hit
|
||||
**/
|
||||
public var limit:Float = Flags.DEFAULT_NOTE_MS_LIMIT;
|
||||
|
||||
@@ -22,11 +26,17 @@ class NoteGroup extends FlxTypedGroup<Note> {
|
||||
length = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of notes to the group, and sorts them.
|
||||
**/
|
||||
public inline function addNotes(notes:Array<Note>) {
|
||||
for(e in notes) add(e);
|
||||
sortNotes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the notes in the group.
|
||||
**/
|
||||
public inline function sortNotes() {
|
||||
sort(function(i, n1, n2) {
|
||||
if (n1.strumTime == n2.strumTime)
|
||||
@@ -117,7 +127,7 @@ class NoteGroup extends FlxTypedGroup<Note> {
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
// doesnt prevent looping from breaking
|
||||
// doesn't prevent looping from breaking
|
||||
if (Splice && __currentlyLooping && i >= index)
|
||||
i++;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import funkin.game.SplashHandler;
|
||||
import funkin.game.cutscenes.*;
|
||||
import funkin.menus.*;
|
||||
import funkin.menus.StoryMenuState.WeekData;
|
||||
import funkin.backend.utils.FunkinSave;
|
||||
import funkin.savedata.FunkinSave;
|
||||
import haxe.io.Path;
|
||||
|
||||
using StringTools;
|
||||
@@ -189,11 +189,11 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Player strums.
|
||||
*/
|
||||
public var playerStrums(get, null):StrumLine;
|
||||
public var playerStrums(get, set):StrumLine;
|
||||
/**
|
||||
* CPU strums.
|
||||
*/
|
||||
public var cpuStrums(get, null):StrumLine;
|
||||
public var cpuStrums(get, set):StrumLine;
|
||||
/**
|
||||
* Shortcut to `playerStrums`.
|
||||
*/
|
||||
@@ -360,7 +360,7 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* Accuracy for the current week
|
||||
*/
|
||||
public static var campaignAccuracy(get, null):Float;
|
||||
public static var campaignAccuracy(get, never):Float;
|
||||
|
||||
public static var campaignAccuracyTotal:Float = 0;
|
||||
public static var campaignAccuracyCount:Float = 0;
|
||||
@@ -423,7 +423,7 @@ class PlayState extends MusicBeatState
|
||||
*/
|
||||
public var events:Array<ChartEvent> = [];
|
||||
/**
|
||||
* Current camera target. -1 means no automatic camera targetting.
|
||||
* Current camera target. -1 means no automatic camera targeting.
|
||||
*/
|
||||
public var curCameraTarget:Int = 0;
|
||||
/**
|
||||
@@ -586,8 +586,8 @@ class PlayState extends MusicBeatState
|
||||
instance = this;
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||
|
||||
PauseSubState.script = "";
|
||||
GameOverSubstate.script = "";
|
||||
PauseSubState.script = Flags.DEFAULT_PAUSE_SCRIPT;
|
||||
GameOverSubstate.script = Flags.DEFAULT_GAMEOVER_SCRIPT;
|
||||
(scripts = new ScriptPack("PlayState")).setParent(this);
|
||||
|
||||
camGame = camera;
|
||||
@@ -642,7 +642,7 @@ class PlayState extends MusicBeatState
|
||||
for(folder in scriptsFolders) {
|
||||
for(file in Paths.getFolderContent(folder, true, fromMods ? MODS : BOTH)) {
|
||||
if (folder == 'data/charts/')
|
||||
Logs.trace('[PlayState] data/charts/ is deprecrated and will be removed in the future. Please move script $file to songs/', WARNING, DARKYELLOW);
|
||||
Logs.trace('[PlayState] data/charts/ is deprecated and will be removed in the future. Please move script $file to songs/', WARNING, DARKYELLOW);
|
||||
|
||||
addScript(file);
|
||||
}
|
||||
@@ -1947,8 +1947,12 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
private inline function get_cpuStrums():StrumLine
|
||||
return strumLines.members[0];
|
||||
private inline function set_cpuStrums(v:StrumLine):StrumLine
|
||||
return strumLines.members[0] = v;
|
||||
private inline function get_playerStrums():StrumLine
|
||||
return strumLines.members[1];
|
||||
private inline function set_playerStrums(v:StrumLine):StrumLine
|
||||
return strumLines.members[1] = v;
|
||||
private inline function get_gfSpeed():Int
|
||||
return (strumLines.members[2] != null && strumLines.members[2].characters[0] != null) ? strumLines.members[2].characters[0].beatInterval : 1;
|
||||
private inline function set_gfSpeed(v:Int):Int {
|
||||
|
||||
@@ -35,7 +35,7 @@ class SplashGroup extends FlxTypedGroup<FunkinSprite> {
|
||||
pregenerateSplashes(splash);
|
||||
add(splash);
|
||||
|
||||
// immediatly draw once and put image in GPU to prevent freezes
|
||||
// immediately draw once and put image in GPU to prevent freezes
|
||||
// TODO: change to graphics cache
|
||||
@:privateAccess
|
||||
splash.drawComplex(FlxG.camera);
|
||||
|
||||
@@ -15,6 +15,9 @@ import haxe.xml.Access;
|
||||
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* A class that handles loading a stage and putting the sprites into the state.
|
||||
**/
|
||||
class Stage extends FlxBasic implements IBeatReceiver {
|
||||
public var extra:Map<String, Dynamic> = [];
|
||||
|
||||
@@ -42,6 +45,9 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
public inline function getSprite(name:String)
|
||||
return stageSprites[name];
|
||||
|
||||
/**
|
||||
* Sets the sprites in the script, so you can access them by the name.
|
||||
**/
|
||||
public function setStagesSprites(script:Script)
|
||||
for (k=>e in stageSprites) script.set(k, e);
|
||||
|
||||
@@ -249,6 +255,12 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
prepareInfos(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a character position to the stage.
|
||||
* @param name The name of the character
|
||||
* @param node The XML node
|
||||
* @param nonXMLInfo (Optional) Non-XML information
|
||||
**/
|
||||
public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos {
|
||||
var charPos = new StageCharPos();
|
||||
charPos.visible = charPos.active = false;
|
||||
@@ -310,9 +322,21 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a character is flipped or not.
|
||||
* @param posName The name of the character position
|
||||
* @param def The default value
|
||||
**/
|
||||
public inline function isCharFlipped(posName:String, def:Bool = false)
|
||||
return characterPoses[posName] != null ? characterPoses[posName].flipX : def;
|
||||
|
||||
/**
|
||||
* Applies the character stuff to the character.
|
||||
* Adds the character to the stage, or inserts it into the stage.
|
||||
* @param char The character
|
||||
* @param posName The name of the character position
|
||||
* @param id The ID of the character
|
||||
**/
|
||||
public function applyCharStuff(char:Character, posName:String, id:Float = 0) {
|
||||
var charPos = characterPoses[char.curCharacter] != null ? characterPoses[char.curCharacter] : characterPoses[posName];
|
||||
if (charPos != null) {
|
||||
@@ -329,6 +353,10 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
|
||||
public function measureHit(curMeasure:Int) {}
|
||||
|
||||
/**
|
||||
* Gets a list of stages that are available to be used.
|
||||
* @param mods Whenever only the mods folder should be checked
|
||||
**/
|
||||
public static function getList(?mods:Bool = false, ?xmlOnly:Bool = false):Array<String> {
|
||||
var list:Array<String> = [];
|
||||
var extensions:Array<String> = ["xml"];
|
||||
|
||||
@@ -4,6 +4,9 @@ import flixel.math.FlxPoint;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
class Strum extends FlxSprite {
|
||||
/**
|
||||
* Extra data that can be added to the strum.
|
||||
**/
|
||||
public var extra:Map<String, Dynamic> = [];
|
||||
|
||||
/**
|
||||
@@ -11,21 +14,48 @@ class Strum extends FlxSprite {
|
||||
*/
|
||||
public var animSuffix:String = "";
|
||||
|
||||
public var cpu = false; // Unused
|
||||
/**
|
||||
* Whenever the strum should act as a CPU strum.
|
||||
* WARNING: Unused.
|
||||
**/
|
||||
@:dox(hide) public var cpu:Bool = false; // Unused
|
||||
/**
|
||||
* The last time the note/confirm animation was hit.
|
||||
**/
|
||||
public var lastHit:Float = -5000;
|
||||
|
||||
/**
|
||||
* The strum line that this strum belongs to.
|
||||
**/
|
||||
public var strumLine:StrumLine = null;
|
||||
|
||||
/**
|
||||
* The scroll speed of the notes.
|
||||
**/
|
||||
public var scrollSpeed:Null<Float> = null; // custom scroll speed per strum
|
||||
/**
|
||||
* The direction of the notes.
|
||||
* If you don't want angle of the strum to interfere with the direction the notes are going,
|
||||
* you can set noteAngle to = 0, and then you can use the angle of the strum without it affecting the direction of the notes.
|
||||
**/
|
||||
public var noteAngle:Null<Float> = null;
|
||||
|
||||
public var lastDrawCameras(default, null):Array<FlxCamera> = [];
|
||||
|
||||
/**
|
||||
* Whenever the strum is pressed.
|
||||
**/
|
||||
public var getPressed:StrumLine->Bool = null;
|
||||
/**
|
||||
* Whenever the strum was just pressed.
|
||||
**/
|
||||
public var getJustPressed:StrumLine->Bool = null;
|
||||
/**
|
||||
* Whenever the strum was just released.
|
||||
**/
|
||||
public var getJustReleased:StrumLine->Bool = null;
|
||||
|
||||
public inline function __getPressed(strumLine:StrumLine):Bool {
|
||||
@:dox(hide) public inline function __getPressed(strumLine:StrumLine):Bool {
|
||||
return getPressed != null ? getPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT;
|
||||
case 1: strumLine.controls.NOTE_DOWN;
|
||||
@@ -34,7 +64,7 @@ class Strum extends FlxSprite {
|
||||
default: false;
|
||||
}
|
||||
}
|
||||
public inline function __getJustPressed(strumLine:StrumLine) {
|
||||
@:dox(hide) public inline function __getJustPressed(strumLine:StrumLine) {
|
||||
return getJustPressed != null ? getJustPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT_P;
|
||||
case 1: strumLine.controls.NOTE_DOWN_P;
|
||||
@@ -43,7 +73,7 @@ class Strum extends FlxSprite {
|
||||
default: false;
|
||||
}
|
||||
}
|
||||
public inline function __getJustReleased(strumLine:StrumLine) {
|
||||
@:dox(hide) public inline function __getJustReleased(strumLine:StrumLine) {
|
||||
return getJustReleased != null ? getJustReleased(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustReleased(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT_R;
|
||||
case 1: strumLine.controls.NOTE_DOWN_R;
|
||||
@@ -53,6 +83,10 @@ class Strum extends FlxSprite {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scroll speed of the notes.
|
||||
* @param note (Optional) The note
|
||||
**/
|
||||
public inline function getScrollSpeed(?note:Note):Float {
|
||||
if (note != null && note.scrollSpeed != null) return note.scrollSpeed;
|
||||
if (scrollSpeed != null) return scrollSpeed;
|
||||
@@ -60,6 +94,12 @@ class Strum extends FlxSprite {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the angle of the notes.
|
||||
* If you don't want angle of the strum to interfere with the direction the notes are going,
|
||||
* you can set noteAngle to = 0, and then you can use the angle of the strum without it affecting the direction of the notes.
|
||||
* @param note (Optional) The note
|
||||
**/
|
||||
public inline function getNotesAngle(?note:Note):Float {
|
||||
if (note != null && note.noteAngle != null) return note.noteAngle;
|
||||
if (noteAngle != null) return noteAngle;
|
||||
@@ -83,6 +123,10 @@ class Strum extends FlxSprite {
|
||||
@:noCompletion public static inline final PIX180:Float = 565.4866776461628; // 180 * Math.PI
|
||||
@:noCompletion public static final N_WIDTHDIV2:Float = Note.swagWidth / 2;
|
||||
|
||||
/**
|
||||
* Updates the position of a note.
|
||||
* @param daNote The note
|
||||
**/
|
||||
public function updateNotePosition(daNote:Note) {
|
||||
if (!daNote.exists) return;
|
||||
|
||||
@@ -130,11 +174,21 @@ class Strum extends FlxSprite {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a sustain note.
|
||||
* @param daNote The note
|
||||
**/
|
||||
public inline function updateSustain(daNote:Note) {
|
||||
if (!daNote.isSustainNote) return;
|
||||
daNote.updateSustain(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the animation state based on the player input.
|
||||
* @param pressed Whenever the player is pressing the button
|
||||
* @param justPressed Whenever the player just pressed the button
|
||||
* @param justReleased Whenever the player just released the button
|
||||
**/
|
||||
public function updatePlayerInput(pressed:Bool, justPressed:Bool, justReleased:Bool) {
|
||||
switch(getAnim()) {
|
||||
case "confirm":
|
||||
@@ -151,16 +205,29 @@ class Strum extends FlxSprite {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the confirm animation.
|
||||
* @param time The time
|
||||
**/
|
||||
public inline function press(time:Float) {
|
||||
lastHit = time;
|
||||
playAnim("confirm");
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an animation.
|
||||
* @param anim The animation name
|
||||
* @param force Whenever the animation should be forced to play
|
||||
**/
|
||||
public function playAnim(anim:String, force:Bool = true) {
|
||||
animation.play(anim, force);
|
||||
centerOffsets();
|
||||
centerOrigin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current animation name.
|
||||
**/
|
||||
public inline function getAnim() {
|
||||
return animation.name;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ import funkin.backend.scripting.events.note.*;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.system.Controls;
|
||||
|
||||
/**
|
||||
* Group of strums, that contains the strums and notes.
|
||||
* Used in PlayState.
|
||||
**/
|
||||
class StrumLine extends FlxTypedGroup<Strum> {
|
||||
/**
|
||||
* Signal that triggers whenever a note is hit. Similar to onPlayerHit and onDadHit, except strumline specific.
|
||||
@@ -71,14 +75,16 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
*/
|
||||
public var animSuffix(default, set):String = "";
|
||||
/**
|
||||
* TODO: Write documention about this being a variable that can help when making multi key
|
||||
* TODO: Write documentation about this being a variable that can help when making multi key
|
||||
*/
|
||||
public var strumAnimPrefix = ["left", "down", "up", "right"];
|
||||
/**
|
||||
* Vocals sound (Vocals.ogg). Used for individual vocals per strumline.
|
||||
*/
|
||||
public var vocals:FlxSound;
|
||||
|
||||
/**
|
||||
* Extra data that can be added to the strum line.
|
||||
**/
|
||||
public var extra:Map<String, Dynamic> = [];
|
||||
|
||||
private function get_ghostTapping() {
|
||||
@@ -90,8 +96,14 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
private inline function set_ghostTapping(b:Bool):Bool
|
||||
return this.ghostTapping = b;
|
||||
|
||||
|
||||
private var startingPos:FlxPoint = FlxPoint.get(0,0);
|
||||
private var strumScale:Float = 1;
|
||||
/**
|
||||
* The scale of the strums.
|
||||
* If called after generate, the strums wont be scaled.
|
||||
* You can only change it in the Charter.
|
||||
**/
|
||||
public var strumScale:Float = 1;
|
||||
|
||||
public function new(characters:Array<Character>, startingPos:FlxPoint, strumScale:Float, cpu:Bool = false, opponentSide:Bool = true, ?controls:Controls, ?vocalPrefix:String = "") {
|
||||
super();
|
||||
@@ -106,6 +118,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
vocals.persist = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the notes for the strumline.
|
||||
**/
|
||||
public function generate(strumLine:ChartStrumLine, ?startTime:Float) {
|
||||
// TODO: implement double generate call support if needed
|
||||
|
||||
@@ -153,7 +168,7 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
if(scrollSpeed == null) if (PlayState.instance != null) scrollSpeed = PlayState.instance.scrollSpeed;
|
||||
if(scrollSpeed == null) scrollSpeed = 1;
|
||||
|
||||
// TODO: Make this work by accounting zoom and scrollspeed changes - Nex
|
||||
// TODO: Make this work by accounting zoom and scroll speed changes - Nex
|
||||
/*var limit = FlxG.height / 0.45;
|
||||
notes.limit = limit / scrollSpeed;
|
||||
OR
|
||||
@@ -171,6 +186,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
notes.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the notes.
|
||||
**/
|
||||
public inline function updateNotes() {
|
||||
__updateNote_songPos = Conductor.songPosition;
|
||||
if(__updateNote_event == null) __updateNote_event = PlayState.instance.__updateNote_event;
|
||||
@@ -180,6 +198,10 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
var __updateNote_strum:Strum;
|
||||
var __updateNote_songPos:Float;
|
||||
var __updateNote_event:NoteUpdateEvent;
|
||||
/**
|
||||
* Updates a note.
|
||||
* This updates the position, state, and handles the input.
|
||||
**/
|
||||
public function updateNote(daNote:Note) {
|
||||
__updateNote_strum = members[daNote.noteData];
|
||||
if (__updateNote_strum == null) return;
|
||||
@@ -236,6 +258,11 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
else if (note.strumTime < __notePerStrum[note.strumID].strumTime) __notePerStrum[note.strumID] = note;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the input for the strumline, and handles the input.
|
||||
* @param id The ID of the strum
|
||||
**/
|
||||
public function updateInput(id:Int = 0) {
|
||||
updateNotes();
|
||||
|
||||
@@ -291,9 +318,18 @@ class StrumLine extends FlxTypedGroup<Strum> {
|
||||
PlayState.instance.gameAndCharsCall("onPostInputUpdate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds/removes health to/from the strumline.
|
||||
* If the strumline is an opponent strumline, it will subtract the health, otherwise it will add the health.
|
||||
* @param health The amount of health to add/remove
|
||||
**/
|
||||
public inline function addHealth(health:Float)
|
||||
PlayState.instance.health += health * (opponentSide ? -1 : 1);
|
||||
|
||||
/**
|
||||
* Generates strums, and adds them to the strumline.
|
||||
* @param amount The amount of strums to generate (-1 for the default amount)
|
||||
**/
|
||||
public inline function generateStrums(amount:Int = -1) {
|
||||
if(amount == -1) amount = Flags.DEFAULT_STRUM_AMOUNT;
|
||||
for (i in 0...amount)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user