Merge branch 'main' into new-editors

This commit is contained in:
Ne_Eo
2025-01-09 20:05:06 +01:00
123 changed files with 1211 additions and 417 deletions
+3 -3
View File
@@ -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
View File
@@ -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 !
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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");
}
+1 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
# THIS FILE ISNT NECESSARY,
# THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT WEEKS
tutorial
+2 -2
View File
@@ -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.
+18 -18
View File
@@ -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
View File
@@ -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")
+3 -3
View File
@@ -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");
}
}
+10 -13
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+1 -1
View File
@@ -7,7 +7,7 @@ import flixel.FlxG;
#end
/**
* @author lunarclient
* @author lunarcleint
* @see https://twitter.com/lunarcleint
*
* Help from Ne_Eo
+11 -6
View File
@@ -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;
}
}
}
+35 -11
View File
@@ -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
+3
View File
@@ -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 -1
View File
@@ -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() {
+5 -1
View File
@@ -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;
/**
+55 -14
View File
@@ -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();
}
}
+9 -3
View File
@@ -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");
+80 -27
View File
@@ -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;
}
}
+1 -1
View File
@@ -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;
+27 -5
View File
@@ -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
+1 -1
View File
@@ -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;
}
+20 -2
View File
@@ -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() {}
}
+61 -1
View File
@@ -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;
+46 -3
View File
@@ -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('
+2 -2
View File
@@ -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,
-10
View File
@@ -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;
}
+9 -2
View File
@@ -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 {
/**
+1 -1
View File
@@ -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;
+17 -3
View File
@@ -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
*/
+1 -1
View File
@@ -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;
+58 -16
View File
@@ -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 -1
View File
@@ -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>)
+31
View File
@@ -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> = [
+10 -10
View File
@@ -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
+1 -1
View File
@@ -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);
+15 -2
View File
@@ -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);
}
}
+23 -6
View File
@@ -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;
}
}
+92 -26
View File
@@ -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;
}
}
+3 -3
View File
@@ -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();
+4 -4
View File
@@ -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)));
+8 -8
View File
@@ -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,
};
+1 -1
View File
@@ -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({
+6 -6
View File
@@ -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;
}
+3 -3
View File
@@ -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;
+4 -4
View File
@@ -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();
+15 -6
View File
@@ -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
}
+1 -1
View File
@@ -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>) {
+1 -1
View File
@@ -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
+7
View File
@@ -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);
+1 -1
View File
@@ -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;
+12 -2
View File
@@ -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++;
+12 -8
View File
@@ -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 {
+1 -1
View File
@@ -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);
+28
View File
@@ -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"];
+71 -4
View File
@@ -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;
}
+40 -4
View File
@@ -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