Merge remote-tracking branch 'dev/main'

This commit is contained in:
⍚~Nex
2024-03-26 02:01:02 +01:00
128 changed files with 2935 additions and 613 deletions
+9 -6
View File
@@ -2,6 +2,7 @@
name: Linux Build
on:
push:
workflow_dispatch:
jobs:
build:
name: Linux Build
@@ -34,16 +35,18 @@ jobs:
- name: Building the game
run: |
haxelib run lime build linux
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz export/release/linux/bin
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v2
with:
name: Codename Engine
path: export/release/linux/bin
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v2
with:
name: Update-Linux
path: export/release/linux/bin/CodenameEngine
path: CodenameEngine.tar.gz
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# with:
# name: Update-Linux
# path: export/release/linux/bin/CodenameEngine
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
+9 -6
View File
@@ -2,6 +2,7 @@
name: Mac OS Build
on:
push:
workflow_dispatch:
jobs:
build:
name: Mac OS Build
@@ -31,16 +32,18 @@ jobs:
- name: Building the game
run: |
haxelib run lime build mac
- name: Tar files
run: tar -zcvf CodenameEngine.tar.gz export/release/macos/bin
- name: Uploading artifact (entire build)
uses: actions/upload-artifact@v2
with:
name: Codename Engine
path: export/release/macos/bin
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v2
with:
name: Update-Mac
path: export/release/macos/bin/CodenameEngine
path: CodenameEngine.tar.gz
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# with:
# name: Update-Mac
# path: export/release/macos/bin/CodenameEngine
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
+5 -5
View File
@@ -37,11 +37,11 @@ jobs:
with:
name: Codename Engine
path: export/release/windows/bin
- name: Uploading artifact (executable)
uses: actions/upload-artifact@v2
with:
name: Update-Windows
path: export/release/windows/bin/CodenameEngine.exe
# - name: Uploading artifact (executable)
# uses: actions/upload-artifact@v2
# with:
# name: Update-Windows
# path: export/release/windows/bin/CodenameEngine.exe
- name: Clearing already existing cache
uses: actions/github-script@v6
with:
+2 -2
View File
@@ -1,6 +1,6 @@
{
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"files.associations": {
"*.hx": "haxe",
"*.hscript": "haxe",
+2 -2
View File
@@ -5,7 +5,7 @@ _**QOL = Quality of Life**_
<details>
<summary><h2>Player QOL Features</h2></summary>
- New input system
- New accuracy and misses system, added to fix the base game UI.
- New options, including:
@@ -72,7 +72,7 @@ _**QOL = Quality of Life**_
- Weeks are located in `data/weeks/weeks/`
- If you need to rearrange the weeks in-game, you can use the `data/weeks/weeks.txt` file.
- Editors for Charts and Characters (Stage coming soon)
- Undos/Redos supported
- Undos/Redos supported
- Warning on closing unsaved work
- Clean UI (for ocd freaks)
- Mature Chart editor (Character editor rework soon)
+4 -17
View File
@@ -30,22 +30,10 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish
##### Tested on Windows 10 21H2
1. Install [version 4.2.5 of Haxe](https://haxe.org/download/version/4.2.5/).
2. Download [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe)
3. Wait for the Visual Studio Installer to install
4. On the Visual Studio installer screen, go to the "Individual components" tab and only select those options:
- MSVC v143 VS 2022 C++ x64/x86 build tools (Latest)
- Windows 10/11 SDK (any works)
5. This is what your Installation details panel should look like. Once correct, press "Install".
- ⚠ This will download around 1.07 GB of data from the internet, and will require around 5.5 GB of available space on your computer.
<p align="center">
<img src="https://github.com/YoshiCrafter29/CodenameEngine/blob/main/art/github/windows-installation-details.png?raw=true" />
</p>
6. Once you finish downloading those components, download and install [`git-scm`](https://git-scm.com/download/win).
2. Download and install [`git-scm`](https://git-scm.com/download/win).
- Leave all installation options as default.
7. Run `update.bat` using cmd or double-clicking it, and wait for the libraries to install.
8. Once the libraries are installed, run `haxelib run lime test windows` to compile and launch the game (may take a long time)
3. Run `update.bat` using cmd or double-clicking it, and wait for the libraries to install.
4. Once the libraries are installed, run `haxelib run lime test windows` to compile and launch the game (may take a long time)
- ℹ You can run `haxelib run lime setup` to make the lime command global, allowing you to execute `lime test windows` directly.
</details>
<details>
@@ -97,6 +85,5 @@ In the future (when the engine won't be a WIP anymore) we're gonna also publish
- Credits to [Ne_Eo](https://twitter.com/Ne_Eo_Twitch) and the [3D-HaxeFlixel](https://github.com/lunarcleint/3D-HaxeFlixel) repository for Away3D Flixel support
- Credits to the [FlxAnimate](https://github.com/Dot-Stuff/flxanimate) team for the Animate Atlas support
- Credits to Smokey555 for the backup Animate Atlas to spritesheet code
- Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) for video cutscene/mp4 support
- Credits to Aidan63 for [discord-rpc](https://github.com/Aidan63/linc_discord-rpc) for discord rpc integration
- Credits to MAJigsaw77 for [hxvlc](https://github.com/MAJigsaw77/hxvlc) (video cutscene/mp4 support) and [hxdiscord_rpc](https://github.com/MAJigsaw77/hxdiscord_rpc) (discord rpc integration)
</details>
+27 -27
View File
@@ -1,30 +1,30 @@
__ /
/ _ _ |_ |_ . _
\__) (- |_ |_ | | )
__
|_ _ _ _ |
| | (- (_| |( \/
/
_ _ _
(_) | ) (_|
__
|_ _ . _| _ _ . _ |_ |_ _ _ |_
| | | (_| (_| \/ | ) | (_) | ) |_ \/ (- (_| | )
/ _/ /
__ /
/ _ _ |_ |_ . _
\__) (- |_ |_ | | )
__
|_ _ _ _ |
| | (- (_| |( \/
/
_ _ _
(_) | ) (_|
__
|_ _ . _| _ _ . _ |_ |_ _ _ |_
| | | (_| (_| \/ | ) | (_) | ) |_ \/ (- (_| | )
/ _/ /
HEY
Thanks for downloadin Friday Night FUNKIN'
If you downloaded this from any other place than ninja-muffin24.itch.io/FUNKIN
You might be in DANGER!!!
You might be in DANGER!!!
The Itch.io release is the only official source for the desktop (PC, Mac, Linux) versions of the game!
As of right now, the game is FREE! If you paid for it, you got SCAMMED!
Now that that's out of the way....
THANKS FOR DOWNLOADIN.
THANKS FOR DOWNLOADIN.
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
PERMISSIONS XDDDD
@@ -77,16 +77,16 @@ I love Tom Fulp.
- Cameron ♪(´▽`)
##################################################################################
_ _ _____ _ _ ____ ____ ___ _ _ _ _ ____ ____
| \ | | | ____| | | | | / ___| | _ \ / _ \ | | | | | \ | | | _ \ / ___|
| \| | | _| | | /\ | | | | _ | |_) | | | | | | | | | | \| | | | | | \___ \
_ _ _____ _ _ ____ ____ ___ _ _ _ _ ____ ____
| \ | | | ____| | | | | / ___| | _ \ / _ \ | | | | | \ | | | _ \ / ___|
| \| | | _| | | /\ | | | | _ | |_) | | | | | | | | | | \| | | | | | \___ \
| |\ | | |___ \ V V / | |_| | | _ < | |_| | | |_| | | |\ | | |_| | ___) |
|_| \_| |_____| \_/\_/ \____| |_| \_\ \___/ \___/ |_| \_| |____/ |____/
_ _ _ ___ ___ __ _ _ _ _ _
|_ | | |_ |_) \_/ | |_| | |\ | /__ |_ |_ | | |_ |_) \_/ / \ |\ | |_
|_| \_| |_____| \_/\_/ \____| |_| \_\ \___/ \___/ |_| \_| |____/ |____/
_ _ _ ___ ___ __ _ _ _ _ _
|_ | | |_ |_) \_/ | |_| | |\ | /__ |_ |_ | | |_ |_) \_/ / \ |\ | |_
|_ \/ |_ | \ | | | | _|_ | \| \_| o |_)\/ |_ \/ |_ | \ | \_/ | \| |_ o
/ /
/ /
##################################################################################
i stole this from stamper.
+1 -1
View File
@@ -7,7 +7,7 @@
<anim name="singDOWN" anim="GF Down Note" fps="24" loop="false" x="0" y="0" />
<anim name="danceLeft" anim="GF Dancing Beat" fps="24" loop="false" x="0" y="0" indices="30,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="GF Dancing Beat" fps="24" loop="false" x="0" y="0" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
<anim name="hairBlow" anim="GF Dancing Beat Hair blowing" fps="24" loop="false" x="0" y="0" indices="0,1,2,3" />
<anim name="hairBlow" anim="GF Dancing Beat Hair blowing" fps="24" loop="true" x="0" y="0" indices="0,1,2,3" />
<anim name="hairFall" anim="GF Dancing Beat Hair Landing" fps="24" loop="false" x="0" y="0" indices="0,1,2,3,4,5,6,7,8,9,10,11" />
<anim name="scared" anim="GF FEAR" fps="24" loop="true" x="0" y="0" />
<anim name="sad" anim="gf sad" fps="24" loop="true" x="0" y="0" indices="0,1,2,3,4,5,6,7,8,9,10,11,12" />
+50
View File
@@ -0,0 +1,50 @@
import funkin.backend.utils.DiscordUtil;
function onGameOver() {
DiscordUtil.changePresence('Game Over', PlayState.SONG.meta.displayName + " (" + PlayState.difficulty + ")");
}
function onDiscordPresenceUpdate(e) {
var data = e.presence;
if(data.button1Label == null)
data.button1Label = "Codename Engine Discord";
if(data.button1Url == null)
data.button1Url = "https://discord.gg/2NTCdsQvx4";
}
function onPlayStateUpdate() {
DiscordUtil.changeSongPresence(
PlayState.instance.detailsText,
(PlayState.instance.paused ? "Paused - " : "") + PlayState.SONG.meta.displayName + " (" + PlayState.difficulty + ")",
PlayState.instance.inst,
PlayState.instance.getIconRPC()
);
}
function onMenuLoaded(name:String) {
// Name is either "Main Menu", "Freeplay", "Title Screen", "Options Menu", "Credits Menu", "Beta Warning", "Update Available Screen", "Update Screen"
DiscordUtil.changePresenceSince("In the Menus", null);
}
function onEditorTreeLoaded(name:String) {
switch(name) {
case "Character Editor":
DiscordUtil.changePresenceSince("Choosing a Character", null);
case "Chart Editor":
DiscordUtil.changePresenceSince("Choosing a Chart", null);
case "Stage Editor":
DiscordUtil.changePresenceSince("Choosing a Stage", null);
}
}
function onEditorLoaded(name:String, editingThing:String) {
switch(name) {
case "Character Editor":
DiscordUtil.changePresenceSince("Editing a Character", editingThing);
case "Chart Editor":
DiscordUtil.changePresenceSince("Editing a Chart", editingThing);
case "Stage Editor":
DiscordUtil.changePresenceSince("Editing a Stage", editingThing);
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
//
function update(elapsed:Float)
if (FlxG.keys.justPressed.F5) FlxG.resetState();
if (FlxG.keys.justPressed.F5) FlxG.resetState();
+2 -2
View File
@@ -35,8 +35,8 @@ function onNoteCreation(event) {
note.updateHitbox();
}
function onPostNoteCreation(event) {
var splashes = event.note;
function onPostNoteCreation(event) {
var splashes = event.note;
if (pixelSplashes)
splashes.splash = "pixel-default";
}
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE codename-engine-splashes>
<splashes sprite="game/splashes/default" alpha="0.6">
<splashes sprite="game/splashes/default" alpha="0.6">
<strum id="0"> <!-- LEFT -->
<anim name="splashleft1" anim="note impact 1 purple" fps="24" x="0" y="0" />
<anim name="splashleft2" anim="note impact 2 purple" fps="24" x="0" y="0" />
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE codename-engine-splashes>
<!--Pixel Splashes by APurples!!-->
<splashes sprite="stages/school/ui/pixel-splashes" alpha="0.6" antialiasing="false" scale="3.5">
<splashes sprite="stages/school/ui/pixel-splashes" alpha="0.6" antialiasing="false" scale="3.5">
<strum id="0"> <!-- LEFT -->
<anim name="splashleft1" anim="splash purple 1" fps="24" x="-1" y="-2" />
<anim name="splashleft2" anim="splash purple 2" fps="24" x="-1" y="-2" />
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE codename-engine-splashes>
<!--Pixel Splashes by APurples!!-->
<splashes sprite="stages/school/ui/pixel-secret-splashes" alpha="0.6" antialiasing="false" scale="3.5">
<splashes sprite="stages/school/ui/pixel-secret-splashes" alpha="0.6" antialiasing="false" scale="3.5">
<strum id="0"> <!-- LEFT -->
<anim name="splashleft1" anim="splash purple 1" fps="24" x="-3"/>
</strum>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE codename-engine-splashes>
<splashes sprite="game/splashes/secret" alpha="0.6" scale="0.75">
<splashes sprite="game/splashes/secret" alpha="0.6" scale="0.75">
<strum id="0"> <!-- LEFT -->
<anim name="splash purple" anim="splash purple" fps="24" x="0" y="0" />
</strum>
+5 -5
View File
@@ -7,23 +7,23 @@
<!-- ONLY FOR INSTANCES WITH LOW MEMORY MODE DISABLED -->
<high-memory>
<!-- DANCERS !-->
<sprite name="dancer1" x="130" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat">
<sprite name="dancer1" x="130" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="bg dancer sketch PINK" loop="false" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="bg dancer sketch PINK" loop="false" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
<sprite name="dancer2" x="500" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat">
<sprite name="dancer2" x="500" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="bg dancer sketch PINK" loop="false" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="bg dancer sketch PINK" loop="false" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
<sprite name="dancer3" x="870" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat">
<sprite name="dancer3" x="870" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="bg dancer sketch PINK" loop="false" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="bg dancer sketch PINK" loop="false" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
<sprite name="dancer4" x="1240" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat">
<sprite name="dancer4" x="1240" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="bg dancer sketch PINK" loop="false" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="bg dancer sketch PINK" loop="false" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
<sprite name="dancer5" x="1610" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat">
<sprite name="dancer5" x="1610" y="80" sprite="limoDancer" scroll="0.4" zoomfactor="0.55" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="bg dancer sketch PINK" loop="false" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="bg dancer sketch PINK" loop="false" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
+5 -5
View File
@@ -1,17 +1,17 @@
<!DOCTYPE codename-engine-stage>
<stage zoom="0.8" name="mall" folder="stages/mall/" startCamPosY="450" startCamPosX="500">
<sprite name="bg" x="-1000" y="-500" sprite="bgWalls" scroll="0.2"/>
<high-memory>
<sprite name="upperBoppers" x="-240" y="-90" sprite="upperBop" scroll="0.2" type="onbeat"/>
<sprite name="upperBoppers" x="-240" y="-90" sprite="upperBop" scroll="0.2" type="onbeat" beatInterval="1"/>
</high-memory>
<sprite name="bgEscalator" x="-1100" y="-600" sprite="bgEscalator" scroll="0.3" scale="0.9" updateHitbox="true"/>
<sprite name="tree" x="370" y="-250" sprite="christmasTree" scroll="0.4"/>
<sprite name="bottomBoppers" x="-300" y="140" sprite="bottomBop" scroll="0.9" type="onbeat" />
<sprite name="bottomBoppers" x="-300" y="140" sprite="bottomBop" scroll="0.9" type="onbeat" beatInterval="1"/>
<sprite name="fgSnow" x="-600" y="700" sprite="fgSnow"/>
<high-memory>
<sprite name="santa" x="-840" y="150" sprite="santa" type="onbeat"/>
<sprite name="santa" x="-840" y="150" sprite="santa" type="onbeat" beatInterval="1"/>
</high-memory>
+1 -1
View File
@@ -90,7 +90,7 @@ function updateTrainPos():Void
function trainReset():Void
{
gf.playAnim('hairFall');
gf.playAnim('hairFall', true);
phillyTrain.x = FlxG.width + 200;
trainMoving = false;
// trainSound.stop();
+1 -1
View File
@@ -23,7 +23,7 @@
antialiasing="false" scale="6" scroll="0.85" updateHitbox="true" type="loop"/>
</high-memory>
<sprite name="bgGirls" x="-100" y="190" sprite="bgFreaks" scroll="0.9" scale="6" antialiasing="false" updateHitbox="true" type="onbeat">
<sprite name="bgGirls" x="-100" y="190" sprite="bgFreaks" scroll="0.9" scale="6" antialiasing="false" updateHitbox="true" type="onbeat" beatInterval="1">
<anim name="danceLeft" anim="BG girls group" indices="0,1,2,3,4,5,6,7,8,9,10,11,12,13" fps="24" loop="false"/>
<anim name="danceRight" anim="BG girls group" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" fps="24" loop="false"/>
</sprite>
+8 -8
View File
@@ -15,7 +15,7 @@
<high-memory>
<sprite name="smokeLeft" x="-200" y="-100" sprite="smokeLeft" scroll="0.4" zoomfactor="0.6"/>
<sprite name="smokeRight" x="1100" y="-100" sprite="smokeRight" scroll="0.4" zoomfactor="0.6"/>
<sprite name="tankWatchtower" x="100" y="50" sprite="tankWatchtower" scroll="0.5" type="onbeat" beatInterval="2" zoomfactor="0.7"/>
<sprite name="tankWatchtower" x="100" y="50" sprite="tankWatchtower" scroll="0.5" type="onbeat" zoomfactor="0.7"/>
</high-memory>
<sprite name="tankRolling" x="300" y="300" sprite="tankRolling" scroll="0.5" zoomfactor="0.8" type="loop"/>
<sprite name="tankGround" x="-420" y="-150" sprite="tankGround" scale="1.15" updateHitbox="true" zoomfactor="1.0"/>
@@ -26,13 +26,13 @@
<boyfriend x="810"/>
<!-- FOREGROUND TANKMANS -->
<high-memory>
<sprite name="tank0" x="-500" y="650" sprite="tank0" scrollx="1.7" scrolly="1.5" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank1" x="-300" y="750" sprite="tank1" scrollx="2" scrolly="0.2" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank2" x="450" y="940" sprite="tank2" scroll="1.5" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank4" x="1300" y="900" sprite="tank4" scroll="1.5" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank5" x="1620" y="700" sprite="tank5" scroll="1.5" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank3" x="1300" y="1200" sprite="tank3" scrollx="3.5" scrolly="2.5" type="onbeat" beatInterval="2" zoomfactor="1.25"/>
<sprite name="tank0" x="-500" y="650" sprite="tank0" scrollx="1.7" scrolly="1.5" type="onbeat" zoomfactor="1.25"/>
<sprite name="tank1" x="-300" y="750" sprite="tank1" scrollx="2" scrolly="0.2" type="onbeat" zoomfactor="1.25"/>
<sprite name="tank2" x="450" y="940" sprite="tank2" scroll="1.5" type="onbeat" zoomfactor="1.25"/>
<sprite name="tank4" x="1300" y="900" sprite="tank4" scroll="1.5" type="onbeat" zoomfactor="1.25"/>
<sprite name="tank5" x="1620" y="700" sprite="tank5" scroll="1.5" type="onbeat" zoomfactor="1.25"/>
<sprite name="tank3" x="1300" y="1200" sprite="tank3" scrollx="3.5" scrolly="2.5" type="onbeat" zoomfactor="1.25"/>
</high-memory>
</stage>
+2 -2
View File
@@ -1,8 +1,8 @@
<!DOCTYPE codename-titlestate >
<titlestate>
<sprites folder="menus/titlescreen">
<sprite name="logo" sprite="logo" x="-150" y="-100" />
<sprite name="gf" sprite="gf" x="512" y="50">
<sprite name="logo" sprite="logo" x="-150" y="-100" beatInterval="1"/>
<sprite name="gf" sprite="gf" x="512" y="50" beatInterval="1">
<anim name="danceLeft" anim="gfDance" indices="30,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14" />
<anim name="danceRight" anim="gfDance" indices="15,16,17,18,19,20,21,22,23,24,25,26,27,28,29" />
</sprite>
Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

@@ -10,7 +10,7 @@
<SubTexture name="add-bottomleft" x="0" y="30" width="11" height="4"/>
<SubTexture name="add-bottom" x="11" y="30" width="19" height="4"/>
<SubTexture name="add-bottomright" x="30" y="30" width="18" height="4"/>
<SubTexture name="edit-topleft" x="0" y="34" width="11" height="4"/>
<SubTexture name="edit-top" x="11" y="34" width="19" height="4"/>
<SubTexture name="edit-topright" x="30" y="34" width="18" height="4"/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 B

After

Width:  |  Height:  |  Size: 1.3 KiB

+1 -1
View File
@@ -8,7 +8,7 @@
<SubTexture name="middleleft" x="0" y="1" width="1" height="21"/>
<SubTexture name="middle" x="1" y="1" width="94" height="21"/>
<SubTexture name="middleright" x="95" y="1" width="1" height="21"/>
<SubTexture name="bottomleft" x="0" y="22" width="1" height="1"/>
<SubTexture name="bottom" x="1" y="22" width="94" height="1"/>
<SubTexture name="bottomright" x="95" y="22" width="1" height="1"/>
+7 -7
View File
@@ -6,12 +6,12 @@ uniform vec2 blueOff;
void main()
{
vec2 uv = getCamPos(openfl_TextureCoordv);
vec4 col;
col.r = textureCam(bitmap, uv + redOff).r;
col.g = textureCam(bitmap, uv + greenOff).g;
col.b = textureCam(bitmap, uv + blueOff).b;
col.a = texture2D(bitmap, openfl_TextureCoordv).a;
vec2 uv = getCamPos(openfl_TextureCoordv);
vec4 col;
col.r = textureCam(bitmap, uv + redOff).r;
col.g = textureCam(bitmap, uv + greenOff).g;
col.b = textureCam(bitmap, uv + blueOff).b;
col.a = texture2D(bitmap, openfl_TextureCoordv).a;
gl_FragColor = col;
gl_FragColor = col;
}
+3 -3
View File
@@ -6,10 +6,10 @@ uniform float strength;
void main() {
vec2 uv = getCamPos(openfl_TextureCoordv);
vec3 col = pow(textureCam(bitmap, uv).rgb, vec3(1.0 / strength));
vec3 col = pow(textureCam(bitmap, uv).rgb, vec3(1.0 / strength));
float vignette = mix(1.0, 1.0 - amount, distance(uv, vec2(0.5)));
col = pow(mix(col * color, col, vignette), vec3(strength));
float vignette = mix(1.0, 1.0 - amount, distance(uv, vec2(0.5)));
col = pow(mix(col * color, col, vignette), vec3(strength));
gl_FragColor = vec4(col, 1.0);
}
@@ -6,6 +6,6 @@ void main() {
float len = length(openfl_TextureCoordv - vec2(0.5, 0.5));
float r = 0.5 - (1.0 / openfl_TextureSize.x);
vec4 c = flixel_texture2D(bitmap, openfl_TextureCoordv);
vec4 color = mix(vec4(0, 0, 0, 0.5), c, c.a);
gl_FragColor = color * clamp(1 - ((len - r) * openfl_TextureSize.x), 0, 1);
vec4 color = mix(vec4(0.0, 0.0, 0.0, 0.5), c, c.a);
gl_FragColor = color * clamp(1.0 - ((len - r) * openfl_TextureSize.x), 0.0, 1.0);
}
+3 -3
View File
@@ -3,9 +3,9 @@
// Used in colorwheel
vec3 hsvToRgb(vec3 c) { //hsv.x = hue, hsv.y = saturation, hsv.z = value
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
uniform float hue;
+8 -5
View File
@@ -7,9 +7,9 @@ int steps = 16;
int stepsInside = 2;
float strength = 0.0075;
vec4 getColor(vec2 pos) {
if (pos.x < 0) pos.x = 0;
if (pos.x < 0.0) pos.x = 0.0;
else if (pos.x > 1.0 - (1.0 / openfl_TextureSize.x)) pos.x = 1.0 - (1.0 / openfl_TextureSize.x);
if (pos.y < 0) pos.y = 0;
if (pos.y < 0.0) pos.y = 0.0;
else if (pos.y > 1.0 - (1.0 / openfl_TextureSize.y)) pos.y = 1.0 - (1.0 / openfl_TextureSize.y);
// return textureCam(bitmap, pos);
return flixel_texture2D(bitmap, pos);
@@ -17,15 +17,18 @@ vec4 getColor(vec2 pos) {
void main() {
// vec2 camPos = getCamPos(openfl_TextureCoordv);
vec2 camPos = openfl_TextureCoordv;
if (camPos.x < 0 || camPos.x > 1 || camPos.y < 0 || camPos.y > 1)
if (camPos.x < 0.0 || camPos.x > 1.0 || camPos.y < 0.0 || camPos.y > 1.0)
return;
vec4 color = getColor(camPos);
float fsteps = steps;
for(float inside = 1; inside < stepsInside+1; inside++) {
for(float inside = 1.0; inside < stepsInside+1.0; inside++) {
for(int i = 0; i < steps; i++) {
float fi = i;
color += getColor(camPos + vec2(strength * (inside / stepsInside) * cos(fi / fsteps * (PI * 2.0)), strength * (inside / stepsInside) * sin(fi / fsteps * (PI * 2.0))));
color += getColor(camPos + vec2(
strength * (inside / stepsInside) * cos(fi / fsteps * (PI * 2.0)),
strength * (inside / stepsInside) * sin(fi / fsteps * (PI * 2.0))
));
}
}
@@ -0,0 +1,88 @@
#pragma header
// Used in charter by waveforms
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);
uniform vec2 textureRes;
uniform float pixelOffset;
uniform float playerPosition;
uniform sampler2D waveformTexture;
uniform ivec2 waveformSize;
uniform bool lowDetail;
float getAmplitude(vec2 pixel) {
float pixelID = floor((pixel.y+pixelOffset)/3.0);
// TODO: INVESTIGATE THE 1.+ AND WHY IT WORKS (SRSLY I GOT NO CLUE) -lunar
vec2 wavePixel = vec2(mod(pixelID,waveformSize.x), 1.0+floor(pixelID/waveformSize.x));
vec4 waveData = texture2D(waveformTexture, wavePixel / waveformSize);
int id = int(mod(wavePixel.x, 3.0));
if(id == 0) return waveData.r;
if(id == 1) return waveData.g;
if(id == 2) return waveData.b;
return 0.0;
}
float getAmpWidth(float amplitude) {
return (1.0-amplitude) * textureRes.x;
}
bool inWaveForm(vec2 pixel, float width) {
float widthdiv2 = width/2.0;
return pixel.x > widthdiv2 && pixel.x < textureRes.x-widthdiv2;
}
float getHightlight(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;
if (!inWaveForm(pixel, ampwidth))
gl_FragColor -= outlinecolor*mix(0.5, 0.8, amplitude);
}
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));
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));
gl_FragColor = vec4(vec3(gradientColor * mix(0.5, 0.8, amplitude))*openfl_Alphav, openfl_Alphav);
outlineWaveform(pixel, vec2(0.0, -1.0));
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 main()
{
vec2 pixel = openfl_TextureCoordv * textureRes;
float amplitude = getAmplitude(pixel);
float ampwidth = getAmpWidth(amplitude);
gl_FragColor = vec4(0.0);
if (!inWaveForm(pixel, ampwidth)) return;
if (lowDetail) lowDetailWaveform(pixel, amplitude, ampwidth);
else highDetailWaveform(pixel, amplitude, ampwidth);
}
+1 -1
View File
@@ -28,7 +28,7 @@ void main() {
hsv.g = (1.0/3.0) * strength * coords.y;
hsv.r = mod(elapsed + coords.x + coords.y, 1);
hsv.r = mod(elapsed + coords.x + coords.y, 1.0);
gl_FragColor = vec4(hsv2rgb(hsv), color.a);
}
+4 -4
View File
@@ -21,7 +21,7 @@ function postUpdate(elapsed) {
}
}
// for(s in strumLines) {
// for(i in 0...4) {
// var n = s.members[i];
@@ -29,7 +29,7 @@ function postUpdate(elapsed) {
// n.angle = 180;
// }
// }
// if (curSection != null)
// defaultCamZoom = curSection.mustHitSection ? 0.9 : 0.5;
// if (curSection != null)
// defaultCamZoom = curSection.mustHitSection ? 0.9 : 0.5;
}
+1 -1
View File
@@ -1 +1 @@
{"events":[{"params":[1],"time":0,"type":1},{"params":[0],"time":9600,"type":1},{"params":[1],"time":14400,"type":1},{"params":[0],"time":19200,"type":1},{"params":[1],"time":24000,"type":1},{"params":[0],"time":28800,"type":1},{"params":[1],"time":33600,"type":1},{"params":[0],"time":38400,"type":1},{"params":[1],"time":43200,"type":1},{"params":[0],"time":48000,"type":1},{"params":[1],"time":50400,"type":1}],"strumLines":[{"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"position":"girlfriend","type":0,"characters":["gf"]},{"notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"position":"boyfriend","type":1,"characters":["bf"]}],"codenameChart":true,"scrollSpeed":1,"noteTypes":[]}
{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]}
+1 -1
View File
@@ -1 +1 @@
{"events":[{"params":[1],"time":0,"type":1},{"params":[0],"time":9600,"type":1},{"params":[1],"time":14400,"type":1},{"params":[0],"time":19200,"type":1},{"params":[1],"time":24000,"type":1},{"params":[0],"time":28800,"type":1},{"params":[1],"time":33600,"type":1},{"params":[0],"time":38400,"type":1},{"params":[1],"time":43200,"type":1},{"params":[0],"time":48000,"type":1},{"params":[1],"time":50400,"type":1}],"strumLines":[{"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"position":"girlfriend","type":0,"characters":["gf"]},{"notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":1,"sLen":0,"time":62550,"type":0},{"id":2,"sLen":0,"time":62700,"type":0},{"id":3,"sLen":0,"time":62850,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":1,"sLen":0,"time":63150,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":0,"time":63450,"type":0},{"id":2,"sLen":0,"time":63600,"type":0},{"id":3,"sLen":0,"time":63750,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":3,"sLen":0,"time":64050,"type":0},{"id":2,"sLen":0,"time":64200,"type":0},{"id":3,"sLen":0,"time":64350,"type":0},{"id":0,"sLen":0,"time":64650,"type":0},{"id":1,"sLen":0,"time":64500,"type":0}],"position":"boyfriend","type":1,"characters":["bf"]}],"codenameChart":true,"scrollSpeed":1,"noteTypes":[]}
{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":1,"sLen":0,"time":62550,"type":0},{"id":2,"sLen":0,"time":62700,"type":0},{"id":3,"sLen":0,"time":62850,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":1,"sLen":0,"time":63150,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":0,"time":63450,"type":0},{"id":2,"sLen":0,"time":63600,"type":0},{"id":3,"sLen":0,"time":63750,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":3,"sLen":0,"time":64050,"type":0},{"id":2,"sLen":0,"time":64200,"type":0},{"id":3,"sLen":0,"time":64350,"type":0},{"id":1,"sLen":0,"time":64500,"type":0},{"id":0,"sLen":0,"time":64650,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]}
+1 -1
View File
@@ -1 +1 @@
{"events":[{"params":[1],"time":0,"type":1},{"params":[0],"time":9600,"type":1},{"params":[1],"time":14400,"type":1},{"params":[0],"time":19200,"type":1},{"params":[1],"time":24000,"type":1},{"params":[0],"time":28800,"type":1},{"params":[1],"time":33600,"type":1},{"params":[0],"time":38400,"type":1},{"params":[1],"time":43200,"type":1},{"params":[0],"time":48000,"type":1},{"params":[1],"time":50400,"type":1}],"strumLines":[{"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"position":"girlfriend","type":0,"characters":["gf"]},{"notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"position":"boyfriend","type":1,"characters":["bf"]}],"codenameChart":true,"scrollSpeed":1,"noteTypes":[]}
{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]}
+1
View File
@@ -0,0 +1 @@
{"events":[{"params":[1],"name":"Camera Movement","time":0},{"params":[0],"name":"Camera Movement","time":9600},{"params":[1],"name":"Camera Movement","time":14400},{"params":[0,"cheer"],"name":"Play Animation","time":18000},{"params":[1,"hey"],"name":"Play Animation","time":18600},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"name":"Camera Movement","time":24000},{"params":[0,"cheer"],"time":27600,"name":"Play Animation"},{"params":[1,"hey"],"time":28200,"name":"Play Animation"},{"params":[0],"name":"Camera Movement","time":28800},{"params":[1],"name":"Camera Movement","time":33600},{"params":[0],"name":"Camera Movement","time":38400},{"params":[1],"name":"Camera Movement","time":43200},{"params":[0],"name":"Camera Movement","time":48000},{"params":[1],"name":"Camera Movement","time":50400}]}
+8
View File
@@ -0,0 +1,8 @@
var lastFocused:Int = null;
var zoomin:FlxTween = null;
function onNoteHit(event)
event.enableCamZooming = false;
function onCameraMove(_) if(zoomin == null && lastFocused != (lastFocused = curCameraTarget))
zoomin = FlxTween.tween(FlxG.camera, {zoom: curCameraTarget == 0 ? 1.3 : 1}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut, onComplete: function(_) zoomin = null});
+1 -1
View File
@@ -20,7 +20,7 @@ class Compiler {
Sys.command("lime", arg);
}
private static function getBuildTarget() {
public static function getBuildTarget() {
return switch(Sys.systemName()) {
case "Windows":
"windows";
+14
View File
@@ -71,6 +71,20 @@ class Update {
}
}
}
// vswhere.exe its used to find any visual studio related installations on the system, including full visual studio ide installations, visual studio build tools installations, and other related components - Nex
if (Compiler.getBuildTarget().toLowerCase() == "windows" && new Process('"C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" -property catalog_productDisplayVersion').exitCode(true) == 1) {
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");
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");
}
}
public static function prettyPrint(text:String) {
+5 -5
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<libraries>
<!-- OpenFL & Lime (Required for Flixel) -->
<lib name="openfl" version="9.2.2"/>
<lib name="lime" version="8.0.2"/>
<lib name="openfl" version="9.2.2" />
<lib name="lime" version="8.1.2" />
<!-- Flixel -->
<git name="flixel" url="https://github.com/FNF-CNE-Devs/flixel" />
@@ -10,13 +10,13 @@
<!-- Other Libraries -->
<git name="hscript-improved" url="https://github.com/FNF-CNE-Devs/hscript-improved" ref="custom-classes" />
<git name="discord-rpc" url="https://github.com/Aidan63/linc_discord-rpc" />
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate" />
<git name="hxdiscord_rpc" url="https://github.com/FNF-CNE-Devs/hxdiscord_rpc" />
<lib name="hxvlc" />
<!-- Documentation and other features -->
<lib name="away3d" />
<git name="away3d" url="https://github.com/FNF-CNE-Devs/away3d" />
<!-- <lib name="dox" /> -->
<lib name="format" />
<lib name="markdown" />
</libraries>
</libraries>
+9 -4
View File
@@ -7,7 +7,7 @@
package="com.yoshman29.codenameengine"
version="0.1.0" company="Yoshman29" />
<!--
<!--
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
-->
<app save-path="YoshiCrafter29/CodenameEngine" save-name="save-default" mod-saves="true" />
@@ -161,6 +161,13 @@
<!-- _______________________________ Libraries ______________________________ -->
<!-- KEEP BOTH OF THESE AS THERE ARE SOME PARTICULAR CASES! -->
<define name="disable-version-check"/>
<haxedef name="disable-version-check"/>
<haxedef name="no-deprecation-warnings"/>
<haxedef name="DISCORD_DISABLE_IO_THREAD" if="DISCORD_RPC"/>
<haxelib name="flixel" />
<haxelib name="flixel-addons" />
@@ -170,11 +177,9 @@
<haxelib name="flxanimate" />
<haxelib name="hscript-improved" />
<haxelib name="discord-rpc" if="DISCORD_RPC"/>
<haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/>
<haxelib name="markdown" />
<haxedef name="no-deprecation-warnings"/>
<!-- _________________________________ Additional stuff _______________________________ -->
<section if="COMPILE_ALL_CLASSES">
+13 -1
View File
@@ -41,7 +41,10 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public var debugMode:Bool = false;
public var animDatas:Map<String, AnimData> = [];
public var beatInterval:Int = 1;
/**
* ODD interval -> asynced; EVEN interval -> synced
*/
public var beatInterval(default, set):Int = 2;
public var beatOffset:Int = 0;
public var skipNegativeBeats:Bool = false;
@@ -358,4 +361,13 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return animateAtlas != null ? (animateAtlas.anim.finished) : (animation.curAnim != null ? animation.curAnim.finished : true);
}
#end
// Getter / Setters
@:noCompletion private function set_beatInterval(v:Int) {
if (v < 1)
v = 1;
return beatInterval = v;
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ class MusicBeatTransition extends MusicBeatSubstate {
finish();
}
});
super.create();
}
+21 -2
View File
@@ -51,10 +51,11 @@ class ModsFolder {
*/
private static var __firstTime:Bool = true;
/**
* Initialises `mods` folder by adding callbacks and such.
* Initialises `mods` folder.
*/
public static function init() {
if(!getModsList().contains(Options.lastLoadedMod))
Options.lastLoadedMod = null;
}
/**
@@ -89,6 +90,24 @@ class ModsFolder {
#end
}
public static function getModsList():Array<String> {
var mods:Array<String> = [];
#if MOD_SUPPORT
for(modFolder in FileSystem.readDirectory(modsPath)) {
if (FileSystem.isDirectory('${modsPath}${modFolder}')) {
mods.push(modFolder);
} else {
var ext = Path.extension(modFolder).toLowerCase();
switch(ext) {
case 'zip':
// is a zip mod!!
mods.push(Path.withoutExtension(modFolder));
}
}
}
#end
return mods;
}
public static function getLoadedMods():Array<String> {
var libs = [];
for (i in Paths.assetsTree.libraries) {
+1 -1
View File
@@ -100,7 +100,7 @@ class Paths
}
return getPath('images/$key.$ext', library);
}
inline static public function script(key:String, ?library:String, isAssetsPath:Bool = false) {
var scriptPath = isAssetsPath ? key : getPath(key, library);
if (!OpenFlAssets.exists(scriptPath)) {
+10 -6
View File
@@ -1,5 +1,6 @@
package funkin.backend.chart;
import funkin.backend.system.Conductor;
import funkin.backend.chart.ChartData;
import flixel.util.FlxColor;
import haxe.io.Path;
@@ -143,7 +144,7 @@ class Chart {
base = data;
} else {
// base game chart
BaseGameParser.parse(data, base);
FNFLegacyParser.parse(data, base);
}
#end
}
@@ -200,17 +201,19 @@ class Chart {
var meta = filteredChart.meta;
#if sys
if (!FileSystem.exists('${songFolderPath}/charts/'))
FileSystem.createDirectory('${songFolderPath}/charts/');
var saveFolder:String = saveSettings.folder == null ? "charts" : saveSettings.folder;
var chartPath = '${songFolderPath}/charts/${difficulty.trim()}.json';
if (!FileSystem.exists('${songFolderPath}/$saveFolder/'))
FileSystem.createDirectory('${songFolderPath}/$saveFolder/');
var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json';
var metaPath = '${songFolderPath}/meta.json';
File.saveContent(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null));
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null));
// idk how null reacts to it so better be sure
if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath))
File.saveContent(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? "\t" : null));
CoolUtil.safeSaveFile(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? "\t" : null));
#end
return filteredChart;
}
@@ -242,4 +245,5 @@ typedef ChartSaveSettings = {
var ?saveMetaInChart:Bool;
var ?saveEventsInChart:Bool;
var ?prettyPrint:Bool;
var ?folder:String;
}
+1 -2
View File
@@ -59,8 +59,7 @@ typedef ChartEvent = {
var params:Array<Dynamic>;
}
@:enum
abstract ChartStrumLineType(Int) from Int to Int {
enum abstract ChartStrumLineType(Int) from Int to Int {
/**
* STRUMLINE IS MARKED AS OPPONENT - WILL BE PLAYED BY CPU, OR PLAYED BY PLAYER IF OPPONENT MODE IS ON
*/
+1 -1
View File
@@ -13,7 +13,7 @@ class EventsData {
public static var defaultEventsList:Array<String> = ["HScript Call", "Camera Movement", "Add Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"];
public static var defaultEventsParams:Map<String, Array<EventParamInfo>> = [
"HScript Call" => [
{name: "Function Name", type: TString, defValue: "myFunc"},
{name: "Function Name", type: TString, defValue: "myFunc"},
{name: "Function Parameters (String split with commas)", type: TString, defValue: ""}
],
"Camera Movement" => [{name: "Camera Target", type: TStrumLine, defValue: 0}],
@@ -1,6 +1,9 @@
package funkin.backend.chart;
class BaseGameParser {
import funkin.backend.chart.ChartData.ChartEvent;
import funkin.backend.system.Conductor;
class FNFLegacyParser {
public static function parse(data:Dynamic, result:ChartData) {
// base fnf chart parsing
var data:SwagSong = data;
@@ -109,7 +112,95 @@ class BaseGameParser {
curTime += curCrochet * beatsPerMeasure;
}
}
// have conductor set up BEFORE you run this :D -lunar
public static function encode(chart:ChartData):Dynamic {
var base:SwagSong = __convertToSwagSong(chart);
base.notes = __convertToSwagSections(chart);
for (strumLine in chart.strumLines)
for (note in strumLine.notes) {
var section:Int = Math.floor(Conductor.getStepForTime(note.time) / Conductor.getMeasureLength());
var swagSection:SwagSection = base.notes[section];
if (section > 0 && section < base.notes.length) {
var sectionNote:Array<Dynamic> = [
note.time, // TIME
note.id, // DATA
note.sLen // SUSTAIN LENGTH
];
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
sectionNote[1] += 4;
swagSection.sectionNotes.push(note);
}
}
return {song: base};
}
// To make it easier to write the psych parser... -lunar
@:noCompletion public static function __convertToSwagSong(chart:ChartData):SwagSong {
var base:SwagSong = {
song: chart.meta.name,
notes: null,
bpm: chart.meta.bpm,
needsVoices: chart.meta.needsVoices,
speed: chart.scrollSpeed,
player1: null,
player2: null,
validScore: true
};
for (strumLine in chart.strumLines)
switch (strumLine.type) {
case OPPONENT:
if (base.player2 == null) base.player2 = strumLine.characters.getDefault(["dad"])[0];
case PLAYER:
if (base.player1 == null) base.player1 = strumLine.characters.getDefault(["bf"])[0];
case ADDITIONAL: // do nothing
}
return base;
}
@:noCompletion public static function __convertToSwagSections(chart:ChartData):Array<SwagSection> {
var events:Array<ChartEvent> = [for (event in chart.events) Reflect.copy(event)];
var measures:Float = Conductor.getMeasuresLength();
var sections:Int = Math.floor(measures) + (measures % 1 > 0 ? 1 : 0);
var notes:Array<SwagSection> = cast new haxe.ds.Vector<SwagSection>(sections);
for (section in 0...sections) {
var baseSection:SwagSection = {
sectionNotes: [],
lengthInSteps: Std.int(Conductor.getMeasureLength()),
mustHitSection: notes[section-1] != null ? notes[section-1].mustHitSection : false,
bpm: notes[section-1] != null ? notes[section-1].bpm : chart.meta.bpm,
changeBPM: false,
altAnim: notes[section-1] != null ? notes[section-1].altAnim : false
};
var sectionEndTime:Float = Conductor.getTimeForStep(Conductor.getMeasureLength() * (section+1));
while(events.length > 0 && events[0].time < sectionEndTime) {
var event:ChartEvent = events.shift();
switch (event.name) {
case "Camera Movement":
baseSection.mustHitSection = chart.strumLines[event.params[0]].type == PLAYER;
case "Alt Animation Toggle":
baseSection.altAnim = event.params[0] || event.params[1];
case "BPM Change":
baseSection.changeBPM = true;
baseSection.bpm = event.params[0];
}
}
notes[section] = baseSection;
}
return notes;
}
}
typedef SwagSong =
{
var song:String;
@@ -117,13 +208,14 @@ typedef SwagSong =
var bpm:Float;
var needsVoices:Bool;
var speed:Float;
var stage:String;
var noteTypes:Array<String>;
var ?stage:String;
var ?noteTypes:Array<String>;
var ?events:Array<Dynamic>;
var player1:String;
var player2:String;
var gf:String;
var gfVersion:String;
var ?gf:String;
var ?gfVersion:String;
var validScore:Bool;
// ADDITIONAL STUFF THAT MAY NOT BE PRESENT IN CHART
@@ -136,9 +228,12 @@ typedef SwagSection =
{
var sectionNotes:Array<Dynamic>;
var lengthInSteps:Int;
var ?sectionBeats:Float;
var ?typeOfSection:Int;
var mustHitSection:Bool;
var ?gfSection:Bool;
var bpm:Float;
var changeBPM:Bool;
var altAnim:Null<Bool>;
@:optional var camTarget:Null<Float>;
var ?camTarget:Null<Float>;
}
+116
View File
@@ -0,0 +1,116 @@
package funkin.backend.chart;
import funkin.backend.chart.FNFLegacyParser.SwagSection;
import funkin.backend.chart.ChartData.ChartEvent;
import funkin.backend.chart.FNFLegacyParser.SwagSong;
import funkin.backend.system.Conductor;
class PsychParser {
// Alreadly parsed in sections
public static var ignoreEvents:Array<String> = [
"Camera Movement",
"Alt Animation Toggle",
"BPM Change"
];
public static function parse(data:Dynamic, result:ChartData)
FNFLegacyParser.parse(data, result);
public static function encode(chart:ChartData):Dynamic {
var base:SwagSong = FNFLegacyParser.__convertToSwagSong(chart);
base.notes = FNFLegacyParser.__convertToSwagSections(chart);
base.stage = chart.stage;
for (section in base.notes)
section.sectionBeats = Conductor.beatsPerMeasure;
for (strumLine in chart.strumLines)
if (strumLine.type == ADDITIONAL && base.gfVersion == null)
base.gfVersion = strumLine.characters.getDefault(["gf"])[0];
for (strumLine in chart.strumLines)
for (note in strumLine.notes) {
var section:Int = Math.floor(Conductor.getStepForTime(note.time) / Conductor.getMeasureLength());
var swagSection:SwagSection = base.notes[section];
if (section >= 0 && section < base.notes.length) {
var sectionNote:Array<Dynamic> = [
note.time, // TIME
note.id, // DATA
note.sLen, // SUSTAIN LENGTH
chart.noteTypes.getDefault([""])[note.type] // NOTE TYPE
];
if ((swagSection.mustHitSection && strumLine.type == OPPONENT) ||
(!swagSection.mustHitSection && strumLine.type == PLAYER))
sectionNote[1] += 4;
swagSection.sectionNotes.push(sectionNote);
}
}
var groupedEvents:Array<Array<ChartEvent>> = [];
var __last:Array<ChartEvent> = null;
var __lastTime:Float = Math.NaN;
for (e in [for (event in chart.events) Reflect.copy(event)]) {
if (e == null || ignoreEvents.contains(e.name)) continue;
if (__last != null && __lastTime == e.time)
__last.push(e);
else {
__last = [e];
__lastTime = e.time;
groupedEvents.push(__last);
}
}
base.events = [];
for (events in groupedEvents) {
var psychEvents:Array<Dynamic> = [];
for (event in events)
switch (event.name) {
case "Add Camera Zoom":
psychEvents.push([
event.name,
event.params[1] == "camGame" ? event.params[0] : 0, // CAMERA ZOOM
event.params[1] == "camHUD" ? event.params[0] : 0, // UI ZOOM
]);
case "Play Animation":
psychEvents.push([
event.name,
event.params[1], // ANIMATION TO PLAY
switch (chart.strumLines[event.params[0]].type) { // CHARACTER
case PLAYER: "bf";
case OPPONENT: "dad";
case ADDITIONAL: "gf";
}
]);
case "Scroll Speed Change":
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( // TIME
event.params[0] ? // IS TWEENED?
(Conductor.getTimeForStep(eventStep+event.params[2]) - Conductor.getTimeForStep(eventStep))/1000
: 0, 2)
]);
default:
// TODO: allow custom formats in event.json
var split:Float = event.params.length / 2;
var val1:String = [for (arg in 0...Math.ceil(split)) event.params[arg]].join(",");
var val2:String = [for (arg in Math.ceil(split)...event.params.length) event.params[arg]].join(",");
psychEvents.push([event.name, val1, val2]);
}
for (psychEvent in psychEvents)
for (i in 1...3) // Turn both vals into strings
if (!(psychEvent[i] is String))
psychEvent[i] = Std.string(psychEvent[i]);
base.events.push([events[0].time, psychEvents]);
}
return {song: base};
}
}
@@ -5,7 +5,7 @@ package funkin.backend.scripting;
*/
class DummyScript extends Script {
public var variables:Map<String, Dynamic> = [];
public override function get(v:String) {return variables.get(v);}
public override function set(v:String, v2:Dynamic) {return variables.set(v, v2);}
public override function onCall(method:String, parameters:Array<Dynamic>):Dynamic {
@@ -79,12 +79,14 @@ class GlobalScript {
}
public static function event<T:CancellableEvent>(name:String, event:T):T {
if (scripts != null) scripts.call(name, [event]);
if (scripts != null)
scripts.event(name, event);
return event;
}
public static function call(name:String, ?args:Array<Dynamic>) {
if (scripts != null) scripts.call(name, args);
if (scripts != null)
scripts.call(name, args);
}
public static function onModSwitch(newMod:String) {
call("destroy");
@@ -93,7 +95,8 @@ class GlobalScript {
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
var path = Paths.script('data/global/LIB_$i');
var script = Script.create(path);
if (script is DummyScript) continue;
if (script is DummyScript)
continue;
script.remappedNames.set(script.fileName, '$i:${script.fileName}');
scripts.add(script);
script.load();
@@ -1,12 +1,8 @@
package funkin.backend.scripting.events;
#if DISCORD_RPC
import discord_rpc.DiscordRpc.DiscordPresenceOptions;
#end
final class DiscordPresenceUpdateEvent extends CancellableEvent {
/**
* Object containing all of the data for the presence. Can be altered.
*/
public var presenceData:#if DISCORD_RPC DiscordPresenceOptions #else Dynamic #end;
public var presence:funkin.backend.utils.DiscordUtil.DPresence;
}
+12 -9
View File
@@ -370,15 +370,18 @@ class FunkinShader extends FlxShader implements IHScriptCustomBehaviour {
var field = Reflect.field(data, name);
var cl = Type.getClassName(Type.getClass(field));
// cant do "field is ShaderInput" for some reason
if (cl.startsWith("openfl.display.ShaderParameter")) {
var sp = cast(field, ShaderParameter<Dynamic>);
@:privateAccess
return (sp.__length > 1) ? sp.value : sp.value[0];
} else if (cl.startsWith("openfl.display.ShaderInput")) {
var si = cast(field, ShaderInput<Dynamic>);
return si.input;
}
// little problem we are facing boys...
// 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
// thanks for looking in the code cne fans :D!! -lunar
if (cl.startsWith("openfl.display.ShaderParameter"))
return (field.__length > 1) ? field.value : field.value[0];
else if (cl.startsWith("openfl.display.ShaderInput"))
return field.input;
return field;
}
+1 -2
View File
@@ -120,8 +120,6 @@ class Main extends Sprite
ShaderResizeFix.init();
Logs.init();
Paths.init();
ModsFolder.init();
DiscordUtil.init();
#if GLOBAL_SCRIPT
funkin.backend.scripting.GlobalScript.init();
#end
@@ -159,6 +157,7 @@ class Main extends Sprite
FlxG.mouse.useSystemCursor = true;
ModsFolder.init();
#if MOD_SUPPORT
ModsFolder.switchMod(modToLoad.getDefault(Options.lastLoadedMod));
#end
+3 -3
View File
@@ -41,7 +41,7 @@ class MainState extends FlxState {
else if (addon.startsWith("[HIGH]")) _highPriorityAddons.insert(0, addon);
else _noPriorityAddons.insert(0, addon);
}
for (addon in _lowPriorityAddons)
for (addon in _lowPriorityAddons)
Paths.assetsTree.addLibrary(ModsFolder.loadModLib('${ModsFolder.addonsPath}$addon', StringTools.ltrim(addon.substr("[LOW]".length))));
}
if (ModsFolder.currentModFolder != null)
@@ -55,7 +55,7 @@ class MainState extends FlxState {
Main.refreshAssets();
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
DiscordUtil.reloadJsonData();
DiscordUtil.init();
EventsData.reloadEvents();
TitleState.initialized = false;
@@ -68,7 +68,7 @@ class MainState extends FlxState {
#if sys
sys.FileSystem.createDirectory('./.temp/');
#if windows Sys.command("attrib +h .temp"); #end
#if windows new funkin.backend.utils.native.HiddenProcess("attrib +h .temp"); #end
#end
}
}
@@ -13,7 +13,7 @@ class FlixelInfo extends FramerateCategory {
var c:Int = 0;
for(_ in FlxG.bitmap._cache.keys())
c++;
_text = 'State: ${Type.getClassName(Type.getClass(FlxG.state))}';
_text += '\nObject Count: ${FlxG.state.members.length}';
_text += '\nCamera Count: ${FlxG.cameras.list.length}';
@@ -27,7 +27,7 @@ class FlixelInfo extends FramerateCategory {
_text += '\nPoint Count: ${points._count}';
#end
}
this.text.text = _text;
super.__enterFrame(t);
}
@@ -7,7 +7,7 @@ import openfl.text.TextField;
class MemoryCounter extends Sprite {
public var memoryText:TextField;
public var memoryPeakText:TextField;
public var memory:Float = 0;
public var memoryPeak:Float = 0;
@@ -14,7 +14,7 @@ class StatsInfo extends FramerateCategory {
_text = "totalDC: " + Context3DStats.totalDrawCalls();
_text += "\nstageDC: " + Context3DStats.contextDrawCalls(DrawCallContext.STAGE);
_text += "\nstage3DDC: " + Context3DStats.contextDrawCalls(DrawCallContext.STAGE3D);
this.text.text = _text;
super.__enterFrame(t);
}
+5 -59
View File
@@ -3,7 +3,6 @@ package funkin.backend.system.github;
#if GITHUB_API
import haxe.Json;
import haxe.Exception;
import haxe.Http;
#end
// TODO: Document further and perhaps make this a Haxelib.
@@ -18,7 +17,7 @@ class GitHub {
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
#if GITHUB_API
try {
var data = Json.parse(__requestOnGitHubServers('https://api.github.com/repos/${user}/${repository}/releases'));
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/releases'));
if (!(data is Array))
throw __parseGitHubException(data);
@@ -41,7 +40,7 @@ class GitHub {
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
#if GITHUB_API
try {
var data = Json.parse(__requestOnGitHubServers('https://api.github.com/repos/${user}/${repository}/contributors'));
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors'));
if (!(data is Array))
throw __parseGitHubException(data);
@@ -63,7 +62,7 @@ class GitHub {
public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization {
#if GITHUB_API
try {
var data = Json.parse(__requestOnGitHubServers('https://api.github.com/orgs/$org'));
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org'));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
@@ -86,7 +85,7 @@ class GitHub {
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
#if GITHUB_API
try {
var data = Json.parse(__requestOnGitHubServers('https://api.github.com/orgs/$org/members'));
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
@@ -111,7 +110,7 @@ class GitHub {
try {
var url = 'https://api.github.com/users/$user';
var data = Json.parse(__requestOnGitHubServers(url));
var data = Json.parse(HttpUtil.requestText(url));
if (Reflect.hasField(data, "documentation_url"))
throw __parseGitHubException(data);
@@ -134,59 +133,6 @@ class GitHub {
public static inline function filterReleases(releases:Array<GitHubRelease>, keepPrereleases:Bool = true, keepDrafts:Bool = false)
return #if GITHUB_API [for(release in releases) if (release != null && (!release.prerelease || (release.prerelease && keepPrereleases)) && (!release.draft || (release.draft && keepDrafts))) release] #else releases #end;
public static function __requestOnGitHubServers(url:String) {
var r = null;
#if GITHUB_API
var h = new Http(url);
h.setHeader("User-Agent", "request");
h.onStatus = function(s) {
if(isRedirect(s))
r = __requestOnGitHubServers(h.responseHeaders.get("Location"));
};
h.onData = function(d) {
if(r == null) r = d;
}
h.onError = function(e) {
throw e;
}
h.request(false);
#end
return r;
}
public static function __requestBytesOnGitHubServers(url:String) {
var r = null;
#if GITHUB_API
var h = new Http(url);
h.setHeader("User-Agent", "request");
h.onStatus = function(s) {
if(isRedirect(s))
r = __requestBytesOnGitHubServers(h.responseHeaders.get("Location"));
};
h.onBytes = function(d) {
if(r == null) r = d;
}
h.onError = function(e) {
throw e;
}
h.request(false);
#end
return r;
}
private static function isRedirect(status:Int):Bool {
switch (status) {
// 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex
case 301 | 302 | 307 | 308 :
trace("Redirected with status code: " + status);
return true;
}
return false;
}
private static function __parseGitHubException(obj:Dynamic):GitHubException {
#if GITHUB_API
var msg:String = "(No message)";
@@ -37,4 +37,21 @@ class Utils {
return expr;
#end
}
public static macro function safeSet(variable:Null<Expr>, value:Null<Expr>):Null<Expr> {
#if macro
return macro if (${value} != null) ${variable} = ${value};
#end
}
public static macro function safeSetWrapper(variable:Null<Expr>, value:Null<Expr>, wrapper:Null<Expr>):Null<Expr> {
#if macro
return macro if (${value} != null) ${variable} = ${wrapper}(${value});
#end
}
public static macro function safeReflection(variable:Null<Expr>, value:Null<Expr>, field:Null<Expr>):Null<Expr> {
#if macro
return macro if (Reflect.hasField(${value}, ${field})) ${variable} = Reflect.field(${value}, ${field});
#end
}
}
@@ -21,14 +21,14 @@ class AsyncUpdater {
public function new(releases:Array<GitHubRelease>) {
this.releases = releases;
}
public function execute() {
Main.execAsync(installUpdates);
}
#end
#if windows
#if windows
public static var executableGitHubName:String = "update-windows.exe";
public static var executableName:String = "CodenameEngine.exe";
#end
@@ -49,7 +49,7 @@ class AsyncUpdater {
public var lastTime:Float = 0;
public var oldBytesLoaded:Float = 0;
public function installUpdates() {
prepareInstallationEnvironment();
downloadFiles();
@@ -64,7 +64,7 @@ class AsyncUpdater {
progress.curFileName = e;
trace('extracting file ${path}');
var reader = ZipUtil.openZip(path);
progress.curZipProgress = new ZipProgress();
ZipUtil.uncompressZip(reader, './', null, progress.curZipProgress);
// FileSystem.deleteFile(path);
@@ -72,7 +72,7 @@ class AsyncUpdater {
if (executableReplaced = FileSystem.exists('$path$executableName')) {
progress.curFile = files.length;
progress.curFileName = executableName;
var progPath = Sys.programPath();
var bakFile = '${Path.withoutExtension(progPath)}.bak';
if (FileSystem.exists(bakFile))
@@ -133,7 +133,7 @@ class AsyncUpdater {
downloadStream.addEventListener(ProgressEvent.PROGRESS, function(e) {
progress.bytesLoaded = e.bytesLoaded;
progress.bytesTotal = e.bytesTotal;
var curTime = Lib.getTimer();
progress.downloadSpeed = (e.bytesLoaded - oldBytesLoaded) / ((curTime - lastTime) / 1000);
@@ -160,7 +160,7 @@ class AsyncUpdater {
public function prepareInstallationEnvironment() {
progress.step = PREPARING;
#if windows
path = '${Sys.getEnv("TEMP")}\\Codename Engine\\Updater\\';
#else
@@ -92,6 +92,8 @@ class UpdateAvailableScreen extends MusicBeatState {
add(skipButton);
oldPos = FlxG.mouse.getScreenPosition();
DiscordUtil.call("onMenuLoaded", ["Update Available Screen"]);
}
var destY:Float = 0;
@@ -59,8 +59,10 @@ class UpdateScreen extends MusicBeatState {
overSound = FlxG.sound.load(Paths.sound('gameOverEnd'));
updater.execute();
FlxG.camera.addShader(rainbowShader = new CustomShader("engine/updaterShader"));
DiscordUtil.call("onMenuLoaded", ["Update Screen"]);
}
@@ -104,7 +106,7 @@ class UpdateScreen extends MusicBeatState {
// update is done, play bf's anim
FlxG.sound.music.stop();
overSound.play();
remove(generalProgress);
remove(partProgress);
generalProgress = FlxDestroyUtil.destroy(generalProgress);
@@ -11,7 +11,7 @@ using funkin.backend.system.github.GitHub;
class UpdateUtil {
public static final repoOwner:String = "YoshiCrafter29";
public static final repoName:String = "CodenameTestRepo";
public static function init() {
// deletes old bak file if it exists
#if sys
@@ -22,11 +22,11 @@ class UpdateUtil {
}
public static function checkForUpdates():UpdateCheckCallback {
var curTag = 'v${Application.current.meta.get('version')}';
var curTag = 'v${Application.current.meta.get('version')}';
trace(curTag);
var error = false;
var newUpdates = __doReleaseFiltering(GitHub.getReleases(repoOwner, repoName, function(e) {
error = true;
}), curTag);
@@ -35,7 +35,7 @@ class UpdateUtil {
success: false,
newUpdate: false
};
if (newUpdates.length <= 0) {
return {
success: true,
+44
View File
@@ -2,6 +2,7 @@ package funkin.backend.utils;
import flixel.text.FlxText;
import funkin.backend.utils.XMLUtil.TextFormat;
import flixel.util.typeLimit.OneOfTwo;
import flixel.tweens.FlxTween;
import flixel.system.frontEnds.SoundFrontEnd;
import flixel.sound.FlxSound;
@@ -9,6 +10,7 @@ import funkin.backend.system.Conductor;
import flixel.sound.FlxSoundGroup;
import haxe.Json;
import haxe.io.Path;
import haxe.io.Bytes;
import haxe.xml.Access;
import flixel.input.keyboard.FlxKey;
import lime.utils.Assets;
@@ -77,6 +79,48 @@ class CoolUtil
#end
}
/**
* Safe saves a file (even adding eventual missing folders) and shows a warning box instead of making the program crash
* @param path Path to save the file at.
* @param content Content of the file to save (as String or Bytes).
*/
@:noUsing public static function safeSaveFile(path:String, content:OneOfTwo<String, Bytes>, showErrorBox:Bool = true) {
#if sys
try {
addMissingFolders(Path.directory(path));
if(content is Bytes) sys.io.File.saveBytes(path, content);
else sys.io.File.saveContent(path, content);
} catch(e) {
var errMsg:String = 'Error while trying to save the file: ${Std.string(e).replace('\n', ' ')}';
Logs.traceColored([Logs.logText(errMsg, RED)], ERROR);
if(showErrorBox) funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING);
}
#end
}
/**
* Creates eventual missing folders to the specified `path`
*
* WARNING: eventual files in `path` will be considered as folders! Just to make possible folders be named as `songs.json` for example
*
* @param path Path to check.
* @return The initial Path.
*/
@:noUsing public static function addMissingFolders(path:String):String {
#if sys
var folders:Array<String> = path.split("/");
var currentPath:String = "";
for (folder in folders) {
currentPath += folder + "/";
if (!sys.FileSystem.exists(currentPath)) {
sys.FileSystem.createDirectory(currentPath);
}
}
#end
return path;
}
/**
* Shortcut to parse a JSON string
* @param str Path to the JSON string
+396 -65
View File
@@ -1,62 +1,122 @@
package funkin.backend.utils;
import funkin.backend.scripting.events.DiscordPresenceUpdateEvent;
import funkin.backend.scripting.events.CancellableEvent;
import funkin.backend.scripting.*; // lazy
import flixel.util.typeLimit.OneOfTwo;
import openfl.display.BitmapData;
import funkin.backend.system.macros.Utils;
import haxe.Json;
import flixel.sound.FlxSound;
#if DISCORD_RPC
import discord_rpc.DiscordRpc;
import hxdiscord_rpc.Discord;
import hxdiscord_rpc.Types;
import sys.thread.Thread;
import Sys;
import lime.app.Application;
#end
class DiscordUtil {
public static var currentID:String = null;
class DiscordUtil
{
public static var currentID(default, set):String = null;
public static var discordThread:#if DISCORD_RPC Thread #else Dynamic #end = null;
public static var ready:Bool = false;
public static var data:DiscordJson = null;
public static var initialized:Bool = false;
private static var stopThread:Bool = false;
public static function init() {
public static var user:#if DISCORD_RPC DUser #else Dynamic #end = null;
public static var lastPresence:#if DISCORD_RPC DPresence #else Dynamic #end = null;
public static var config:#if DISCORD_RPC DiscordJson #else Dynamic #end = null;
public static var script:Script;
// Constants
public static var REPLY_NO:Int = Discord.REPLY_NO;
public static var REPLY_YES:Int = Discord.REPLY_YES;
public static var REPLY_IGNORE:Int = Discord.REPLY_IGNORE;
public static var PARTY_PRIVATE:Int = Discord.PARTY_PRIVATE;
public static var PARTY_PUBLIC:Int = Discord.PARTY_PUBLIC;
public static function init()
{
#if DISCORD_RPC
reloadJsonData();
discordThread = Thread.create(function() {
if (initialized)
return;
initialized = true;
discordThread = Thread.create(function()
{
while (true)
{
while(!ready) {
Sys.sleep(1/60);
while (!stopThread)
{
#if DISCORD_DISABLE_IO_THREAD
Discord.UpdateConnection();
#end
Discord.RunCallbacks();
Sys.sleep(2);
}
trace("Processing Discord RPC...");
DiscordRpc.process();
Sys.sleep(2);
Sys.sleep(1); // to reduce cpu
}
});
Application.current.onExit.add(function(exitCode) {
shutdown();
});
Application.current.onExit.add(function(_) shutdown());
#end
}
public static function reloadJsonData() {
public static function reloadJsonData()
{
#if DISCORD_RPC
data = {};
var jsonPath = Paths.json("config/discord");
if (Assets.exists(jsonPath)) {
try {
data = Json.parse(Assets.getText(jsonPath));
} catch(e) {
if (Assets.exists(jsonPath))
{
try
config = Json.parse(Assets.getText(jsonPath))
catch (e)
Logs.trace('Couldn\'t load Discord RPC configuration: ${e.toString()}', ERROR);
}
}
data.setFieldDefault("clientID", "1027994136193810442");
data.setFieldDefault("logoKey", "icon");
data.setFieldDefault("logoText", Application.current.meta.get('title'));
else
{
config = {};
}
changeClientID(data.clientID);
config.logoKey = config.logoKey.getDefault("icon");
config.logoText = config.logoText.getDefault(Application.current.meta.get('title'));
config.clientID = config.clientID.getDefault("1027994136193810442");
currentID = config.clientID;
#end
}
public static function changePresence(details:String, state:String, ?smallImageKey : String) {
public static function event<T:CancellableEvent>(name:String, event:T):T
{
if (script != null)
script.call(name, [event]);
return event;
}
public static function call(name:String, ?args:Array<Dynamic>)
{
if (script != null)
script.call(name, args);
}
public static function loadScript()
{
if (script != null)
{
call("destroy");
script = FlxDestroyUtil.destroy(script);
}
script = Script.create(Paths.script('data/discord'));
// script.setParent(DiscordUtil);
script.load();
}
public static function changePresence(details:String, state:String, ?smallImageKey:String)
{
#if DISCORD_RPC
changePresenceAdvanced({
state: state,
@@ -66,12 +126,14 @@ class DiscordUtil {
#end
}
public static function changeSongPresence(details:String, state:String, audio:FlxSound, ?smallImageKey : String) {
public static function changeSongPresence(details:String, state:String, audio:FlxSound, ?smallImageKey:String)
{
#if DISCORD_RPC
var start:Float = 0;
var end:Float = 0;
if (audio != null && audio.playing) {
if (audio != null && audio.playing)
{
start = Date.now().getTime();
end = start + (audio.length - audio.time);
}
@@ -86,66 +148,335 @@ class DiscordUtil {
#end
}
public static function changePresenceAdvanced(data:#if DISCORD_RPC DiscordPresenceOptions #else Dynamic #end) {
public static function changePresenceSince(details:String, state:String, ?smallImageKey:String, ?time:Float)
{
#if DISCORD_RPC
if (data == null) return;
if(time == null)
time = Date.now().getTime();
if (data.largeImageKey == null)
data.largeImageKey = DiscordUtil.data.logoKey;
if (data.largeImageText == null)
data.largeImageText = DiscordUtil.data.logoText;
#if GLOBAL_SCRIPT
var event = funkin.backend.scripting.GlobalScript.event("onDiscordPresenceUpdate", EventManager.get(DiscordPresenceUpdateEvent).recycle(data));
if (event.cancelled) return;
#end
DiscordRpc.presence(data);
changePresenceAdvanced({
state: state,
details: details,
smallImageKey: smallImageKey,
startTimestamp: Std.int(time / 1000)
});
#end
}
public static function changeClientID(id:String) {
@:noCompletion public static function fixString(str:String)
{
return new cpp.ConstCharStar(cast(str, String));
}
@:noCompletion public static function toString(str:cpp.ConstCharStar)
{
return cast(str, String);
}
public static function changePresenceAdvanced(data:DPresence)
{
#if DISCORD_RPC
if (currentID != null) {
DiscordRpc.shutdown();
if (data == null)
return;
// copy last presence
if (data.largeImageKey == null)
data.largeImageKey = config.logoKey;
if (data.largeImageText == null)
data.largeImageText = config.logoText;
var evt = EventManager.get(DiscordPresenceUpdateEvent).recycle(data);
#if GLOBAL_SCRIPT
// kept for "backwards compat"
funkin.backend.scripting.GlobalScript.event("onDiscordPresenceUpdate", evt);
#end
event("onDiscordPresenceUpdate", evt);
if (evt.cancelled)
return;
data = evt.presence;
lastPresence = data;
var dp:DiscordRichPresence = DiscordRichPresence.create();
// TODO: make this use a reflection-like macro
Utils.safeSetWrapper(dp.state, data.state, fixString);
Utils.safeSetWrapper(dp.details, data.details, fixString);
Utils.safeSet(dp.startTimestamp, data.startTimestamp);
Utils.safeSet(dp.endTimestamp, data.endTimestamp);
Utils.safeSetWrapper(dp.largeImageKey, data.largeImageKey, fixString);
Utils.safeSetWrapper(dp.largeImageText, data.largeImageText, fixString);
Utils.safeSetWrapper(dp.smallImageKey, data.smallImageKey, fixString);
Utils.safeSetWrapper(dp.smallImageText, data.smallImageText, fixString);
Utils.safeSetWrapper(dp.partyId, data.partyId, fixString);
Utils.safeSet(dp.partySize, data.partySize);
Utils.safeSet(dp.partyMax, data.partyMax);
Utils.safeSet(dp.partyPrivacy, data.partyPrivacy);
Utils.safeSetWrapper(dp.matchSecret, data.matchSecret, fixString);
Utils.safeSetWrapper(dp.joinSecret, data.joinSecret, fixString);
Utils.safeSetWrapper(dp.spectateSecret, data.spectateSecret, fixString);
Utils.safeSet(dp.instance, data.instance);
if (data.matchSecret == null && data.joinSecret == null && data.spectateSecret == null)
{
Utils.safeSetWrapper(dp.button1Label, data.button1Label, fixString);
Utils.safeSetWrapper(dp.button1Url, data.button1Url, fixString);
Utils.safeSetWrapper(dp.button2Label, data.button2Label, fixString);
Utils.safeSetWrapper(dp.button2Url, data.button2Url, fixString);
}
ready = false;
DiscordRpc.start({
clientID: id,
onReady: function() {
Logs.trace('Discord RPC started');
ready = true;
},
onError: onError,
onDisconnected: onDisconnected
});
currentID = id;
Discord.UpdatePresence(cpp.RawConstPointer.addressOf(dp));
#end
}
public static function shutdown() {
public static function clearPresence() {
#if DISCORD_RPC
DiscordRpc.shutdown();
Discord.ClearPresence();
#end
}
private static function set_currentID(id:String):String
{
if (currentID == id)
return id;
#if DISCORD_RPC
if (currentID != null)
shutdown();
var handlers:DiscordEventHandlers = DiscordEventHandlers.create();
handlers.ready = cpp.Function.fromStaticFunction(onReady);
handlers.disconnected = cpp.Function.fromStaticFunction(onDisconnected);
handlers.errored = cpp.Function.fromStaticFunction(onError);
handlers.joinGame = cpp.Function.fromStaticFunction(onJoin);
handlers.joinRequest = cpp.Function.fromStaticFunction(onJoinReq);
handlers.spectateGame = cpp.Function.fromStaticFunction(onSpectate);
Discord.Initialize(id, cpp.RawPointer.addressOf(handlers), 1, null);
stopThread = false;
loadScript();
#end
return currentID = id;
}
public static function shutdown()
{
ready = false;
stopThread = true;
#if DISCORD_RPC
Discord.Shutdown();
#end
call("destroy");
script = FlxDestroyUtil.destroy(script);
}
public static function respond(userId:String, reply:Int)
{
#if DISCORD_RPC
Discord.Respond(fixString(userId), reply);
#end
}
// HANDLERS
#if DISCORD_RPC
static function onError(_code:Int, _message:String) {
Logs.trace('Discord RPC Error: ${_message} (Code: $_code)', ERROR);
private static function onReady(request:cpp.RawConstPointer<DiscordUser>):Void
{
user = DUser.initRaw(request);
Logs.traceColored([
Logs.logText("[Discord] ", BLUE),
Logs.logText("Connected to User " + user.globalName + " ("),
Logs.logText(user.handle, GRAY),
Logs.logText(")")
], INFO);
ready = true;
call("onReady", [user]);
}
static function onDisconnected(_code:Int, _message:String) {
Logs.trace('Discord RPC Disconnected: ${_message} (Code: $_code)', WARNING);
private static function onDisconnected(errorCode:Int, message:cpp.ConstCharStar):Void
{
var finalMsg:String = cast(message, String);
Logs.traceColored([
Logs.logText("[Discord] ", BLUE),
Logs.logText("Disconnected ("),
Logs.logText('$errorCode: $finalMsg', RED),
Logs.logText(")")
], INFO);
call("onReady", [errorCode, cast(finalMsg, String)]);
}
private static function onError(errorCode:Int, message:cpp.ConstCharStar):Void
{
var finalMsg:String = cast(message, String);
Logs.traceColored([
Logs.logText("[Discord] ", BLUE),
Logs.logText('Error ($errorCode: $finalMsg)', RED)
], ERROR);
call("onError", [errorCode, cast(finalMsg, String)]);
}
private static function onJoin(joinSecret:cpp.ConstCharStar):Void
{
Logs.traceColored([Logs.logText("[Discord] ", BLUE), Logs.logText("Someone has just joined", GREEN)], INFO);
call("onJoinGame", [cast(joinSecret, String)]);
}
private static function onSpectate(spectateSecret:cpp.ConstCharStar):Void
{
Logs.traceColored([
Logs.logText("[Discord] ", BLUE),
Logs.logText("Someone started spectating your game", YELLOW)
], INFO);
call("onSpectateGame", [cast(spectateSecret, String)]);
}
private static function onJoinReq(request:cpp.RawConstPointer<DiscordUser>):Void
{
Logs.traceColored([
Logs.logText("[Discord] ", BLUE),
Logs.logText("Someone has just requested to join", YELLOW)
], WARNING);
var req:DUser = DUser.initRaw(request);
call("onJoinRequest", [req]);
}
#end
}
typedef DiscordJson = {
typedef DiscordJson =
{
var ?clientID:String;
var ?logoKey:String;
var ?logoText:String;
}
}
@:noCustomClass
final class DUser
{
/**
* The username + discriminator if they have it
**/
public var handle:String;
/**
* The user id, aka 860561967383445535
**/
public var userId:String;
/**
* The user's username
**/
public var username:String;
/**
* The #number from before discord changed to usernames only, if the user has changed to a username them its just a 0
**/
public var discriminator:Int;
/**
* The user's avatar filename
**/
public var avatar:String;
/**
* The user's display name
**/
public var globalName:String;
/**
* If the user is a bot or not
**/
public var bot:Bool;
/**
* Idk check discord docs
**/
public var flags:Int;
/**
* If the user has nitro
**/
public var premiumType:NitroType;
private function new()
{
}
public static function initRaw(req:cpp.RawConstPointer<DiscordUser>)
{
return init(cpp.ConstPointer.fromRaw(req).ptr);
}
public static function init(userData:cpp.Star<DiscordUser>)
{
var d = new DUser();
d.userId = userData.userId;
d.username = userData.username;
d.discriminator = Std.parseInt(userData.discriminator);
d.avatar = userData.avatar;
d.globalName = userData.globalName;
d.bot = userData.bot;
d.flags = userData.flags;
d.premiumType = userData.premiumType;
if (d.discriminator != 0)
d.handle = '${d.username}#${d.discriminator}';
else
d.handle = '${d.username}';
return d;
}
/**
* Calling this function gets the BitmapData of the user
**/
public function getAvatar(size:Int = 256):BitmapData
return BitmapData.fromBytes(HttpUtil.requestBytes('https://cdn.discordapp.com/avatars/$userId/$avatar.png?size=$size'));
}
enum abstract NitroType(Int) to Int from Int
{
var NONE = 0;
var NITRO_CLASSIC = 1;
var NITRO = 2;
var NITRO_BASIC = 3;
}
typedef DPresence =
{
var ?state:String; /* max 128 bytes */
var ?details:String; /* max 128 bytes */
var ?startTimestamp:OneOfTwo<Int, haxe.Int64>;
var ?endTimestamp:OneOfTwo<Int, haxe.Int64>;
var ?largeImageKey:String; /* max 32 bytes */
var ?largeImageText:String; /* max 128 bytes */
var ?smallImageKey:String; /* max 32 bytes */
var ?smallImageText:String; /* max 128 bytes */
var ?partyId:String; /* max 128 bytes */
var ?partySize:Int;
var ?partyMax:Int;
var ?partyPrivacy:Int;
var ?matchSecret:String; /* max 128 bytes */
var ?joinSecret:String; /* max 128 bytes */
var ?spectateSecret:String; /* max 128 bytes */
var ?instance:OneOfTwo<Int, cpp.Int8>;
var ?button1Label:String; /* max 32 bytes */
var ?button1Url:String; /* max 512 bytes */
var ?button2Label:String; /* max 32 bytes */
var ?button2Url:String; /* max 512 bytes */
}
typedef DEvents =
{
var ?ready:DUser->Void;
var ?disconnected:(errorCode:Int, message:String) -> Void;
var ?errored:(errorCode:Int, message:String) -> Void;
var ?joinGame:String->Void;
var ?spectateGame:String->Void;
var ?joinRequest:DUser->Void;
}
+71
View File
@@ -0,0 +1,71 @@
package funkin.backend.utils;
import haxe.Http;
class HttpUtil
{
public static var userAgent:String = "request";
public static function requestText(url:String)
{
var r = null;
var h = new Http(url);
h.setHeader("User-Agent", userAgent);
h.onStatus = function(s)
{
if (isRedirect(s))
r = requestText(h.responseHeaders.get("Location"));
};
h.onData = function(d)
{
if (r == null)
r = d;
}
h.onError = function(e)
{
throw e;
}
h.request(false);
return r;
}
public static function requestBytes(url:String)
{
var r = null;
var h = new Http(url);
h.setHeader("User-Agent", userAgent);
h.onStatus = function(s)
{
if (isRedirect(s))
r = requestBytes(h.responseHeaders.get("Location"));
};
h.onBytes = function(d)
{
if (r == null)
r = d;
}
h.onError = function(e)
{
throw e;
}
h.request(false);
return r;
}
private static function isRedirect(status:Int):Bool
{
switch (status)
{
// 301: Moved Permanently, 302: Found (Moved Temporarily), 307: Temporary Redirect, 308: Permanent Redirect - Nex
case 301 | 302 | 307 | 308:
Logs.traceColored([Logs.logText('[Connection Status] ', BLUE), Logs.logText('Redirected with status code: ', YELLOW), Logs.logText('$status', GREEN)], WARNING);
return true;
}
return false;
}
}
@@ -79,6 +79,8 @@ class MemoryUtil {
public static inline function currentMemUsage() {
#if cpp
return Gc.memInfo64(Gc.MEM_INFO_USAGE);
#elseif hl
return Gc.stats().currentMemory;
#elseif sys
return cast(cast(System.totalMemory, UInt), Float);
#else
+6 -6
View File
@@ -180,13 +180,13 @@ class XMLUtil {
}
/**
* Creates a new sprite based on a XML node.
*/
* Creates a new sprite based on a XML node.
*/
public static inline function createSpriteFromXML(node:Access, parentFolder:String = "", defaultAnimType:XMLAnimType = BEAT, ?cl:Class<FunkinSprite>, ?args:Array<Dynamic>):FunkinSprite {
if(cl == null) cl = FunkinSprite;
if(args == null) args = [];
return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType);
}
if(cl == null) cl = FunkinSprite;
if(args == null) args = [];
return loadSpriteFromXML(Type.createInstance(cl, args), node, parentFolder, defaultAnimType);
}
public static function extractAnimFromXML(anim:Access, animType:XMLAnimType = NONE, loop:Bool = false):AnimData {
var animData:AnimData = {
+24 -4
View File
@@ -36,16 +36,36 @@ class DebugOptionsScreen extends OptionsScreen {
}));
#end
add(new Checkbox(
"Enable Editor SFXs",
"Editor SFXs",
"If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)",
"editorSFX"));
add(new Checkbox(
"Resizable Editors",
"If checked, editors will be resizable and extensible like other programs instead of zooming in upon maximization.",
"resizableEditors"));
"Pretty Print",
"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",
"If checked, will use more intensive blur that may be laggier but look better.",
"intensiveBlur"));
add(new Checkbox(
"Editor Autosaves",
"If checked, this will autosave your files in the editor, with the settings listed below.",
"charterAutoSaves"));
add(new NumOption(
"Autosaving Time",
"This controls how often the editor will autosave your file (in seconds...)",
60, 60*10, 30,
"charterAutoSaveTime"
));
add(new NumOption(
"Save Warning Time",
"This controls how long the editor will warn you before it autosaves (in seconds..., 0 to disable)",
0, 15, 1,
"charterAutoSaveWarningTime"
));
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"));
}
}
+2 -14
View File
@@ -1,8 +1,5 @@
package funkin.editors;
#if desktop
import sys.io.File;
#end
import haxe.io.Path;
import lime.ui.FileDialog;
@@ -30,13 +27,9 @@ class SaveSubstate extends MusicBeatSubstate {
super.create();
var fileDialog = new FileDialog();
fileDialog.onCancel.add(function() {
close();
});
fileDialog.onCancel.add(function() close());
fileDialog.onSelect.add(function(str) {
#if desktop
File.saveContent(str, data);
#end
CoolUtil.safeSaveFile(str, data);
close();
});
fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile);
@@ -46,11 +39,6 @@ class SaveSubstate extends MusicBeatSubstate {
super.update(elapsed);
parent.persistentUpdate = false;
}
private function onError(_) {
// TODO: error handling
close();
}
}
typedef SaveSubstateData = {
+1 -1
View File
@@ -21,7 +21,7 @@ class SaveWarning {
if (warningFunc != null) warningFunc(closingWindow);
if (FlxG.state != null && FlxG.state is UIState) {
FlxG.state.openSubState(new UIWarningSubstate("Unsaved Changes!", "Your changes will be lost if you don't save them. (Can't be recovered)\n\n\nWould you like to Cancel?",
FlxG.state.openSubState(new UIWarningSubstate("Unsaved Changes!", "Your changes will be lost if you don't save them. (Can't be recovered)\n\n\nWould you like to Cancel?",
[
{
label: "Cancel",
@@ -52,7 +52,7 @@ class CharacterEditor extends UIState {
WindowUtils.endfix = " (Character Editor)";
SaveWarning.selectionClass = CharacterSelection;
SaveWarning.saveFunc = () -> {_file_save(null);};
topMenu = [
{
label: "File",
@@ -224,7 +224,7 @@ class CharacterEditor extends UIState {
FlxG.cameras.add(uiCamera);
FlxG.cameras.add(animsCamera);
character = new Character(0,0,__character, false, false);
character = new Character(0,0, __character, false, false);
character.debugMode = true;
character.cameras = [charCamera];
@@ -257,6 +257,8 @@ class CharacterEditor extends UIState {
Framerate.memoryCounter.alpha = 0.4;
Framerate.codenameBuildField.alpha = 0.4;
}
DiscordUtil.call("onEditorLoaded", ["Character Editor", __character]);
}
override function destroy() {
@@ -328,14 +330,14 @@ class CharacterEditor extends UIState {
function _file_save(_) {
#if sys
sys.io.File.saveContent(
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
buildCharacter()
);
undos.save();
return;
#end
#else
_file_saveas(_);
#end
}
function _file_saveas(_) {
@@ -181,8 +181,8 @@ class CharacterInfoScreen extends UISubstateWindow {
xml.set("antialiasing", antialiasingCheckbox.checked ? "true" : "false");
xml.set("sprite", spriteTextBox.label.text);
if (iconColorWheel.colorChanged)
xml.set("color", iconColorWheel.curColor.toHexString(false).replace("0x", "#"));
for (val in customPropertiesButtonList.buttons.members)
xml.set("color", iconColorWheel.curColor.toWebString());
for (val in customPropertiesButtonList.buttons.members)
xml.set(val.propertyText.label.text, val.valueText.label.text);
for (anim in character.animDatas)
@@ -33,6 +33,8 @@ class CharacterSelection extends EditorTreeMenu
}));
main = new OptionsScreen("Character Editor", "Select a character to edit", list);
DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]);
}
override function createPost() {
+224 -50
View File
@@ -1,5 +1,11 @@
package funkin.editors.charter;
// ! FUCK YOU CHUF (your biggest fan -lunar) <3
import flixel.input.FlxPointer;
import openfl.geom.Point;
import openfl.geom.Rectangle;
import flixel.graphics.FlxGraphic;
import flixel.addons.display.FlxTiledSprite;
import funkin.editors.ui.UIContextMenu.UIContextMenuOptionSpr;
import funkin.editors.ui.UITopMenu.UITopMenuButton;
import funkin.editors.charter.CharterStrumline;
@@ -16,6 +22,9 @@ import funkin.backend.chart.ChartData;
import flixel.addons.display.FlxBackdrop;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import funkin.editors.ui.UIState;
import flixel.util.FlxColor;
import openfl.display.BitmapData;
import funkin.backend.shaders.CustomShader;
class Charter extends UIState {
public static var __song:String;
@@ -52,6 +61,7 @@ class Charter extends UIState {
public var gridBackdropDummy:CharterBackdropDummy;
public var noteHoverer:CharterNoteHoverer;
public var noteDeleteAnims:CharterDeleteAnim;
public var strumlineInfoBG:FlxSprite;
public var strumlineAddButton:CharterStrumlineButton;
@@ -76,6 +86,8 @@ class Charter extends UIState {
public var charterCamera:FlxCamera;
public var uiCamera:FlxCamera;
public var selectionBox:UISliceSprite;
public var autoSaveNotif:CharterAutoSaveUI;
public static var autoSaveTimer:Float = 0;
public static var selection:Selection;
@@ -83,6 +95,7 @@ class Charter extends UIState {
public static var undos:UndoList<CharterChange>;
public static var clipboard:Array<CharterCopyboardObject> = [];
public static var waveformHandler:CharterWaveformHandler;
public function new(song:String, diff:String, reload:Bool = true) {
super();
@@ -99,7 +112,7 @@ class Charter extends UIState {
WindowUtils.endfix = " (Chart Editor)";
SaveWarning.selectionClass = CharterSelection;
SaveWarning.saveFunc = () -> {_file_save(null);};
topMenu = [
{
label: "File",
@@ -151,6 +164,15 @@ class Charter extends UIState {
onSelect: _file_meta_saveas,
},
null,
{
label: "Export For FNF Legacy...",
onSelect: _file_saveas_fnflegacy,
},
{
label: "Export For Psych Engine...",
onSelect: _file_saveas_psych,
},
null,
{
label: "Exit",
onSelect: _file_exit
@@ -267,6 +289,12 @@ class Charter extends UIState {
label: 'Show Beats Separator',
onSelect: _view_showeventBeatSeparator,
icon: Options.charterShowBeats ? 1 : 0
},
null,
{
label: 'Low Detail Waveforms',
onSelect: _view_switchWaveformDetail,
icon: Options.charterLowDetailWaveforms ? 1 : 0
}
]
},
@@ -379,8 +407,10 @@ class Charter extends UIState {
selectionBox.incorporeal = true;
noteHoverer = new CharterNoteHoverer();
noteDeleteAnims = new CharterDeleteAnim();
selectionBox.cameras = notesGroup.cameras = gridBackdrops.cameras = noteHoverer.cameras = [charterCamera];
selectionBox.cameras = notesGroup.cameras = gridBackdrops.cameras =
noteHoverer.cameras = noteDeleteAnims.cameras = [charterCamera];
topMenuSpr = new UITopMenu(topMenu);
topMenuSpr.cameras = uiGroup.cameras = [uiCamera];
@@ -420,6 +450,9 @@ class Charter extends UIState {
strumlineInfoBG.y = 23;
strumlineInfoBG.scrollFactor.set();
autoSaveNotif = new CharterAutoSaveUI(20, strumlineInfoBG.y + strumlineInfoBG.height + 20);
uiGroup.add(autoSaveNotif);
strumlineAddButton = new CharterStrumlineButton("editors/new", "Create New");
strumlineAddButton.onClick = createStrumWithUI;
strumlineAddButton.animationOnClick = false;
@@ -448,6 +481,7 @@ class Charter extends UIState {
add(addEventSpr);
add(eventsGroup);
add(noteHoverer);
add(noteDeleteAnims);
add(notesGroup);
add(selectionBox);
add(strumlineInfoBG);
@@ -468,6 +502,16 @@ class Charter extends UIState {
Framerate.codenameBuildField.alpha = 0.4;
}
updateDisplaySprites();
// ! IF YOU EVER WANNA VIEW IN THE FUTURE, JUST USE A FLXSPRITE :D -lunar
/*var dataDisplay:FlxSprite = new FlxSprite().loadGraphic(waveformHandler.waveDatas.get("Voices.ogg"));
dataDisplay.scrollFactor.set(1, 0);
dataDisplay.scale.set(2, 2);
dataDisplay.updateHitbox();
dataDisplay.screenCenter(Y);
dataDisplay.cameras = [charterCamera]; dataDisplay.x = -dataDisplay.width; add(dataDisplay);*/
DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")"]);
}
override function destroy() {
@@ -501,12 +545,12 @@ class Charter extends UIState {
for(strL in PlayState.SONG.strumLines)
createStrumline(strumLines.members.length, strL, false, false);
// create notes
notesGroup.autoSort = false;
var noteCount:Int = 0;
for (strL in PlayState.SONG.strumLines)
for (note in strL.notes) noteCount++;
noteCount += strL.notes.length;
notesGroup.preallocate(noteCount);
var notesCreated:Int = 0;
@@ -544,8 +588,10 @@ class Charter extends UIState {
buildNoteTypesUI();
refreshBPMSensitive();
// Undo Stuffs :D
__relinkUndos();
// Just for now until i add event stacking -lunar
try {__relinkUndos();}
catch (e) {Logs.trace('Failed to relink undos: ${Std.string(e)}', ERROR);}
__applyPlaytestInfo();
}
@@ -555,8 +601,55 @@ class Charter extends UIState {
var length = FlxG.sound.music.getDefault(vocals).length;
scrollBar.length = __endStep = Conductor.getStepForTime(length);
gridBackdrops.bottomLimitY = Conductor.getStepForTime(length) * 40;
gridBackdrops.bottomLimitY = __endStep * 40;
eventsBackdrop.bottomSeparator.y = gridBackdrops.bottomLimitY-2;
updateWaveforms();
}
public function updateWaveforms() {
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [
{name: "Inst.ogg", sound: FlxG.sound.music},
];
if (PlayState.SONG.meta.needsVoices != false)
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
for (strumLine in strumLines)
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "")
wavesToGenerate.push({
name: 'Voices${strumLine.strumLine.vocalsSuffix}.ogg',
sound: strumLine.vocals
});
var oldWaveformList:Array<String> = waveformHandler.waveformList;
var newWaveformList:Array<String> = [for (data in wavesToGenerate) data.name];
if (waveformHandler == null ? true : waveformHandler.ampsNeeded != __endStep*40) {
waveformHandler.clearWaveforms();
waveformHandler.ampsNeeded = __endStep * 40;
for (data in wavesToGenerate) {
waveformHandler.generateShader(data.name, data.sound);
waveformHandler.waveformList.push(data.name);
}
} else if (waveformHandler != null && waveformHandler.waveformList.length != newWaveformList.length) {
for (name in oldWaveformList)
if (!newWaveformList.contains(name))
waveformHandler.clearWaveform(name);
for (data in wavesToGenerate)
if (!oldWaveformList.contains(data.name))
waveformHandler.generateShader(data.name, data.sound);
waveformHandler.waveformList = newWaveformList;
}
for (strumLine in strumLines) {
if (strumLine.selectedWaveform == -1) continue;
var oldName:String = oldWaveformList[strumLine.selectedWaveform];
strumLine.selectedWaveform = waveformHandler.waveformList.indexOf(oldName);
}
}
public override function beatHit(curBeat:Int) {
@@ -598,6 +691,36 @@ class Charter extends UIState {
for(s in selection) s.selected = true;
}
var __autoSaveLocation:String = null;
public function updateAutoSaving(elapsed:Float) {
if (!Options.charterAutoSaves) return;
autoSaveTimer -= elapsed;
if (autoSaveTimer < Options.charterAutoSaveWarningTime && !autoSaveNotif.cancelled && !autoSaveNotif.showedAnimation) {
if (Options.charterAutoSavesSeperateFolder)
__autoSaveLocation = __diff.toLowerCase() + DateTools.format(Date.now(), "%m-%d_%H-%M");
autoSaveNotif.startAutoSave(autoSaveTimer,
!Options.charterAutoSavesSeperateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' :
'Saved chart at $__autoSaveLocation.json!'
);
}
if (autoSaveTimer <= 0) {
autoSaveTimer = Options.charterAutoSaveTime;
if (!autoSaveNotif.cancelled) {
buildChart();
var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}';
if (Options.charterAutoSavesSeperateFolder)
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});
undos.save();
}
autoSaveNotif.cancelled = false;
}
}
var deletedNotes:Selection = new Selection();
public function updateNoteLogic(elapsed:Float) {
updateSelectionLogic();
@@ -669,11 +792,11 @@ class Charter extends UIState {
for (s in selection) {
if (s.draggable) {
var changePoint:FlxPoint = FlxPoint.get(verticalChange, horizontalChange);
if (!FlxG.keys.pressed.SHIFT)
if (!FlxG.keys.pressed.SHIFT)
changePoint.x -= ((s.step + verticalChange) - quantStepRounded(s.step+verticalChange, verticalChange > 0 ? 0.35 : 0.65));
var boundedChange:FlxPoint = changePoint.clone();
// Some maths, so cool bro -lunar (i dont know why i quopte 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);
@@ -702,7 +825,11 @@ class Charter extends UIState {
}
case NONE:
if (FlxG.mouse.justPressed)
FlxG.mouse.getWorldPosition(charterCamera, dragStartPos);
FlxG.mouse.getWorldPosition(charterCamera, dragStartPos);
else if (FlxG.mouse.justPressedRight) {
closeCurrentContextMenu();
gridActionType = DELETE_SELECTION;
}
if (gridBackdropDummy.hovered) {
// AUTO DETECT
@@ -711,11 +838,11 @@ class Charter extends UIState {
var id = Math.floor(mousePos.x / 40);
var mouseOnGrid = id >= 0 && id < 4 * gridBackdrops.strumlinesAmount && mousePos.y >= 0;
if (FlxG.mouse.justReleased) {
for (n in selection) n.selected = false;
selection = [];
if (mouseOnGrid && mousePos.y > 0 && mousePos.y < (__endStep)*40) {
var note = new CharterNote();
note.updatePos(
@@ -745,13 +872,6 @@ class Charter extends UIState {
}
}
}
if (FlxG.mouse.justReleasedRight) {
var mousePos = FlxG.mouse.getScreenPosition(uiCamera);
closeCurrentContextMenu();
openContextMenu(topMenu[1].childs, null, mousePos.x, mousePos.y);
mousePos.put();
}
case SUSTAIN_DRAG:
selectionDragging = FlxG.mouse.pressed;
if (selectionDragging) {
@@ -760,7 +880,7 @@ class Charter extends UIState {
var change:Float = Math.max((mousePos.y-(FlxG.keys.pressed.SHIFT ? dragStartPos.y : quantStep(dragStartPos.y))) / 40, -n.susLength);
n.tempSusLength = change;
if (!FlxG.keys.pressed.SHIFT)
if (!FlxG.keys.pressed.SHIFT)
n.tempSusLength -= (n.susLength + change) - quantStepRounded(n.susLength + change, change > 0 ? 0.35 : 0.65);
@:privateAccess n.__susInstaLerp = FlxG.keys.pressed.SHIFT;
});
@@ -777,15 +897,38 @@ class Charter extends UIState {
undoChanges.push({before: oldSusLen, after: n.susLength, note: n});
});
undos.addToUndo(CEditSustains(undoChanges));
gridActionType = NONE;
currentCursor = ARROW;
}
case DELETE_SELECTION:
notesGroup.forEach(function(n) {
if (n.hovered || n.sustainDraggable) {
deletedNotes.push(n);
deleteSingleSelection(n, false);
if (selection.contains(n)) selection.remove(n);
noteDeleteAnims.deleteNotes.push({
note: n, time: noteDeleteAnims.deleteTime
});
}
});
if (FlxG.mouse.justReleasedRight) {
if (deletedNotes.length > 0) undos.addToUndo(CDeleteSelection(deletedNotes.copy()));
else if (noteDeleteAnims.garbageIcon.alpha <= .5) {
var mousePos = FlxG.mouse.getScreenPosition(uiCamera);
closeCurrentContextMenu();
openContextMenu(topMenu[1].childs, null, mousePos.x, mousePos.y);
mousePos.put();
}
gridActionType = NONE; deletedNotes = [];
}
}
addEventSpr.selectable = !selectionBox.visible;
var inBoundsY:Bool = (mousePos.y > 0 && mousePos.y < (__endStep)*40);
// Event Spr
if (mousePos.x < 0 && mousePos.x > -addEventSpr.bWidth && gridActionType == NONE && inBoundsY) {
addEventSpr.incorporeal = false;
@@ -927,7 +1070,7 @@ class Charter extends UIState {
var i = 0;
var toBeDeleted:Selection = [];
for (note in notesGroup.members)
if (note.strumLineID == strumLineID) {
if (note.strumLineID == strumLineID) {
undoNotes.push(buildNote(note));
toBeDeleted.push(note);
}
@@ -977,6 +1120,8 @@ class Charter extends UIState {
strumLines.members[strID].updateInfo();
undos.addToUndo(CEditStrumLine(strID, oldData, _));
if (oldData.vocalsSuffix != _.vocalsSuffix) updateWaveforms();
}));
}
@@ -997,6 +1142,7 @@ class Charter extends UIState {
var __firstFrame:Bool = true;
public override function update(elapsed:Float) {
updateNoteLogic(elapsed);
updateAutoSaving(elapsed);
if (FlxG.sound.music.playing || __firstFrame) {
gridBackdrops.conductorSprY = curStepFloat * 40;
@@ -1019,8 +1165,8 @@ class Charter extends UIState {
var buttonI:Int = 0;
for (button in quantButtons) {
button.visible = ((button.quant == quant) ||
(button.quant == quants[FlxMath.wrap(quants.indexOf(quant)-1, 0, quants.length-1)]) ||
button.visible = ((button.quant == quant) ||
(button.quant == quants[FlxMath.wrap(quants.indexOf(quant)-1, 0, quants.length-1)]) ||
(button.quant == quants[FlxMath.wrap(quants.indexOf(quant)+1, 0, quants.length-1)]));
button.selectable = button.visible;
if (!button.visible) continue;
@@ -1039,7 +1185,7 @@ class Charter extends UIState {
noteTypeText.y = Std.int((noteTopButton.bHeight - noteTypeText.height) / 2);
}
noteTypeText.text = '($noteType) ${noteTypes[noteType-1] == null ? "Default Note" : noteTypes[noteType-1]}';
super.update(elapsed);
scrollBar.size = (FlxG.height / 40 / charterCamera.zoom);
@@ -1141,7 +1287,7 @@ class Charter extends UIState {
}
function _file_saveas(_) {
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false)), {
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? "\t" : null), {
defaultSaveFile: '${__diff.toLowerCase()}.json'
}));
undos.save();
@@ -1157,7 +1303,7 @@ class Charter extends UIState {
}
function _file_saveas_no_events(_) {
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false)), {
openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? "\t" : null), {
defaultSaveFile: '${__diff.toLowerCase()}.json'
}));
undos.save();
@@ -1165,13 +1311,13 @@ class Charter extends UIState {
function _file_meta_save(_) {
#if sys
sys.io.File.saveContent(
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json',
Json.stringify(PlayState.SONG.meta == null ? {} : PlayState.SONG.meta, null, "\t")
);
return;
#end
#else
_file_meta_saveas(_);
#end
}
function _file_meta_saveas(_) {
@@ -1180,20 +1326,32 @@ class Charter extends UIState {
}));
}
function _file_saveas_fnflegacy(_) {
openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), {
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json',
}));
}
function _file_saveas_psych(_) {
openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? "\t" : null), {
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == "normal" ? "" : '-${__diff.toLowerCase()}'}.json',
}));
}
function _file_events_save(_) {
#if sys
sys.io.File.saveContent(
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json',
Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events})
Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null)
);
return;
#end
#else
_file_events_saveas(_);
#end
}
function _file_events_saveas(_) {
#if sys
openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}), {
openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? "\t" : null), {
defaultSaveFile: 'events.json'
}));
#end
@@ -1202,7 +1360,7 @@ class Charter extends UIState {
#if sys
function saveTo(path:String, separateEvents:Bool = false) {
buildChart();
Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveEventsInChart: !separateEvents});
Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveEventsInChart: !separateEvents, prettyPrint: Options.editorPrettyPrint});
}
#end
@@ -1245,7 +1403,7 @@ class Charter extends UIState {
}
selection = sObjects;
_edit_copy(_); // to fix stupid bugs
undos.addToUndo(CCreateSelection(sObjects.copy()));
}
@@ -1258,12 +1416,15 @@ class Charter extends UIState {
function _edit_delete(_) {
if (selection == null || selection.length == 0) return;
selection = deleteSelection(selection);
selection.loop((n:CharterNote) -> {
noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime});
});
selection = deleteSelection(selection, true);
}
function _edit_undo(_) {
if (strumLines.isDragging || selectionDragging || subState != null) return;
if (strumLines.isDragging || selectionDragging || (subState != null && !(subState is UIContextMenu))) return;
selection = [];
var undo = undos.undo();
switch(undo) {
@@ -1286,7 +1447,7 @@ class Charter extends UIState {
case CSelectionDrag(selectionDrags):
for (s in selectionDrags)
if (s.selectable.draggable) s.selectable.handleDrag(s.change * -1);
selection = [for (s in selectionDrags) s.selectable];
case CEditSustains(changes):
for(n in changes)
@@ -1308,7 +1469,7 @@ class Charter extends UIState {
}
function _edit_redo(_) {
if (strumLines.isDragging || selectionDragging || subState != null) return;
if (strumLines.isDragging || selectionDragging || (subState != null && !(subState is UIContextMenu))) return;
selection = [];
var redo = undos.redo();
@@ -1367,7 +1528,7 @@ class Charter extends UIState {
function chart_edit_data(_)
FlxG.state.openSubState(new ChartDataScreen(PlayState.SONG));
function chart_edit_metadata(_)
FlxG.state.openSubState(new MetaDataScreen(PlayState.SONG.meta));
FlxG.state.openSubState(new CharterMetaDataScreen(PlayState.SONG.meta));
function _playback_play(_) {
if (Conductor.songPosition >= FlxG.sound.music.getDefault(vocals).length - Conductor.songOffset) return;
@@ -1439,6 +1600,11 @@ class Charter extends UIState {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
eventsBackdrop.eventBeatSeparator.visible = gridBackdrops.beatsVisible = Options.charterShowBeats;
}
function _view_switchWaveformDetail(t) {
t.icon = (Options.charterLowDetailWaveforms = !Options.charterLowDetailWaveforms) ? 1 : 0;
for (shader in waveformHandler.waveShaders) shader.data.lowDetail.value = [Options.charterLowDetailWaveforms];
}
inline function _snap_increasesnap(_) changequant(1);
inline function _snap_decreasesnap(_) changequant(-1);
inline function _snap_resetsnap(_) setquant(16);
@@ -1466,7 +1632,7 @@ class Charter extends UIState {
null
];
for (_quant in quants)
for (_quant in quants)
newChilds.push({
label: '${_quant}x Grid Snap',
onSelect: (_) -> {setquant(_quant); buildSnapsUI();},
@@ -1485,7 +1651,7 @@ class Charter extends UIState {
function _note_selectall(_) {
selection = [for (note in notesGroup.members) note];
}
}
function _note_selectmeasure(_) {
selection = [for (note in notesGroup.members)
@@ -1614,7 +1780,7 @@ class Charter extends UIState {
}
notesGroup.sortNotes();
for(n in notesGroup.members) {
if (PlayState.SONG.strumLines[n.strumLineID] != null)
if (PlayState.SONG.strumLines[n.strumLineID] != null)
PlayState.SONG.strumLines[n.strumLineID].notes.push(buildNote(n));
}
buildEvents();
@@ -1681,7 +1847,7 @@ class Charter extends UIState {
newChanges[i] = CSelectionDrag([
for (selectionDrag in selectionDrags)
{
selectable: __relinkSingleSelection(selectionDrag.selectable),
selectable: __relinkSingleSelection(selectionDrag.selectable),
change: selectionDrag.change
}
]);
@@ -1714,10 +1880,13 @@ class Charter extends UIState {
selection = new Selection();
undos = new UndoList<CharterChange>();
clipboard = []; playtestInfo = null;
waveformHandler = new CharterWaveformHandler();
autoSaveTimer = Options.charterAutoSaveTime;
}
@:noCompletion public function __clearStatics() {
selection = null; undos = null; clipboard = null; playtestInfo = null;
selection = null; undos = null; clipboard = null; playtestInfo = null;
waveformHandler.destroy(); Charter.waveformHandler = null; autoSaveTimer = 0;
}
@:noCompletion public function __updatePlaytestInfo() {
@@ -1725,10 +1894,11 @@ class Charter extends UIState {
songPosition: Conductor.songPosition,
playbackSpeed: playBackSlider.value,
quantSelected: quant,
noteTypeSelected: noteType,
noteTypeSelected: noteType,
strumlinesDraggable: strumLines.draggable,
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsounds],
mutedVocals: [for (strumLine in strumLines.members) !(strumLine.vocals.volume > 0)],
waveforms: [for (strumLine in strumLines.members) strumLine.selectedWaveform]
}
}
@@ -1745,6 +1915,8 @@ class Charter extends UIState {
strumLine.hitsounds = playtestInfo.hitSounds[i];
for (i => strumLine in strumLines.members)
strumLine.vocals.volume = playtestInfo.mutedVocals[i] ? 0 : 1;
for (i => strumLine in strumLines.members)
strumLine.selectedWaveform = playtestInfo.waveforms[i];
}
}
@@ -1814,6 +1986,7 @@ enum abstract CharterGridActionType(Int) {
var NOTE_DRAG = 2;
var INVALID_DRAG = 3;
var SUSTAIN_DRAG = 4;
var DELETE_SELECTION = 5;
}
typedef PlaytestInfo = {
@@ -1824,4 +1997,5 @@ typedef PlaytestInfo = {
var strumlinesDraggable:Bool;
var hitSounds:Array<Bool>;
var mutedVocals:Array<Bool>;
var waveforms:Array<Int>;
}
@@ -0,0 +1,129 @@
package funkin.editors.charter;
import flixel.util.FlxTimer;
import flixel.tweens.FlxTween;
import flixel.math.FlxRect;
using flixel.util.FlxSpriteUtil;
class CharterAutoSaveUI extends UISliceSprite {
public var icon:FlxSprite;
public var autosavingText:UIText;
public var progressBarBack:FlxSprite;
public var progressBar:FlxSprite;
public var cancelButton:UIButton;
public var cancelIcon:FlxSprite;
public var showedAnimation:Bool = false;
public var cancelled:Bool = false;
public function new(x:Float, y:Float) {
super(x, y, 300, 46, "editors/ui/inputbox");
icon = new FlxSprite(x+12, y+9).loadGraphic(Paths.image("editors/autosave-icons"), true, 10, 10);
icon.animation.add("icon", [for(i in 0...3) i], 0, true);
icon.animation.play("icon"); icon.animation.curAnim.curFrame = 0;
members.push(icon);
members.push(autosavingText = new UIText(x+12+10+4, y+8, 0, "Autosaving in 10 seconds...", 12));
progressBarBack = new FlxSprite(x + 10, y + bHeight - 20).makeGraphic(Std.int(bWidth-20), 10, 0x00000000, true);
progressBarBack.drawRoundRect(0, 0, progressBarBack.width, progressBarBack.height, 4, 6, 0xFF727272, null, {smoothing: false});
progressBarBack.drawRoundRect(1, 1, progressBarBack.width-2, progressBarBack.height-2, 4, 6, 0xFF0D0D0D, null, {smoothing: false});
members.push(progressBarBack); progressBarBack.antialiasing = false;
progressBar = new FlxSprite(x + 10, y + bHeight - 20).makeGraphic(Std.int(bWidth-20), 10, 0x00000000, true);
progressBar.drawRoundRect(0, 0, progressBar.width, progressBar.height, 4, 6, 0xFF727272, null, {smoothing: false});
progressBar.drawRoundRect(1, 1, progressBar.width-2, progressBar.height-2, 4, 6, 0xFFFFFFFF, null, {smoothing: false});
progressBar.antialiasing = false; progressBar.clipRect = new FlxRect(0, 0, progressBar.width, progressBar.height);
members.push(progressBar);
cancelButton = new UIButton(x-(10+16), y+8, "", () -> {
if (cancelled || !showedAnimation) return;
cancelled = true; __timer.cancel(); __tween.cancel();
icon.animation.curAnim.curFrame = 2;
(new FlxTimer()).start(1, (_) -> {disappearAnimation(true);});
autosavingText.text += " (Canceled)!"; cancelButton.visible = false;
for (member in [this, autosavingText]) member.color = 0xFFE67F7F;
for (member in [progressBar, progressBarBack]) member.color = 0xFFE67F7F;
}, 24, 14);
cancelButton.frames = Paths.getFrames("editors/ui/grayscale-button");
cancelButton.color = 0xFFAC3D3D; cancelButton.alpha = 0.2;
members.push(cancelButton); cancelButton.visible = false;
cancelIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image("editors/autosave-delete"));
cancelIcon.color = 0xFFD60E0E;
members.push(cancelIcon);
alpha = 0;
}
var __timer:FlxTimer;
var __tween:FlxTween;
public function startAutoSave(time:Float, sucessText:String) {
appearAnimation();
__tween = FlxTween.num(0, 1, time, null, (v:Float) -> {
if ((progress = v) < .95) {
autosavingText.text = 'Autosaving in ${Math.min(Math.round(time), 1+Math.floor(Math.abs(time-(progress*time))))} seconds';
autosavingText.text += [for (i in 0...(Math.floor((progress*time*3)%4))) "."].join("");
}
});
__timer = new FlxTimer();
__timer.start(time, (_) -> {
autosavingText.text = sucessText; cancelButton.visible = false;
icon.animation.curAnim.curFrame = 1;
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
(new FlxTimer()).start(1, (_) -> {disappearAnimation();});
});
}
public var progress:Float = 0;
public override function update(elapsed:Float) {
super.update(elapsed);
cancelButton.follow(this, bWidth-(10+24), 7);
cancelButton.alpha = alpha * .7;
cancelIcon.follow(cancelButton, 3.5+1, 2);
cancelIcon.alpha = alpha;
cancelIcon.visible = cancelButton.visible;
icon.follow(this, 12, 9);
icon.alpha = alpha;
autosavingText.follow(this, 12+10+4, 8);
autosavingText.alpha = alpha;
for (bar in [progressBar, progressBarBack]) {
bar.follow(this, 10, bHeight-20);
bar.alpha = alpha;
}
progressBar.clipRect = progressBar.clipRect.set(0, 0, progressBar.width*progress,progressBar.height);
}
public function appearAnimation() {
autosavingText.text = "Autosaving in seconds..."; progress = 0; icon.animation.curAnim.curFrame = 0;
for (member in [this, autosavingText, progressBar, progressBarBack]) member.color = 0xFFFFFFFF;
x = -(320); alpha=0; FlxTween.cancelTweensOf(this); cancelled = false; cancelButton.visible = true;
FlxTween.tween(this, {x: 20}, .4, {ease: FlxEase.circInOut});
FlxTween.tween(this, {alpha: 1}, .3, {ease: FlxEase.sineOut, startDelay: .1});
showedAnimation = true;
}
public function disappearAnimation(?canned:Bool = false) {
x = 20; alpha = 1; FlxTween.cancelTweensOf(this);
if (canned) FlxTween.tween(this, {x: -320}, .4, {ease: FlxEase.circInOut});
FlxTween.tween(this, {alpha: 0}, .3, {ease: FlxEase.sineOut});
showedAnimation = false;
}
}
@@ -46,7 +46,7 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
public override function update(elapsed:Float) {
for (grid in members)
grid.active = grid.visible = false;
super.update(elapsed);
for (i => strumLine in strumLineGroup.members) {
@@ -64,6 +64,8 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
grid.sectionSeparator.visible = sectionsVisible;
grid.beatSeparator.visible = beatsVisible;
grid.waveformSprite.shader = strumLine.waveformShader;
grid.notesGroup.clear();
notesGroup.forEach((n) -> {
var onStr:Bool = (n.snappedToStrumline ? n.strumLineID : Std.int(FlxMath.bound((n.x+n.width)/(40*4), 0, strumLineGroup.members.length-1))) == i;
@@ -74,7 +76,6 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
grid.active = grid.visible = true;
grid.updateSprites();
}
}
public var draggingObj:CharterBackdrop = null;
@@ -115,6 +116,8 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
public var bottomLimit:FlxSprite;
public var bottomSeparator:FlxSprite;
public var waveformSprite:FlxSprite;
public var conductorFollowerSpr:FlxSprite;
public var beatSeparator:FlxBackdrop;
public var sectionSeparator:FlxBackdrop;
@@ -128,6 +131,11 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
gridBackDrop = new FlxBackdrop(gridGraphic, Y, 0, 0);
add(gridBackDrop);
waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000);
waveformSprite.scale.set(160, 1);
waveformSprite.updateHitbox();
add(waveformSprite);
sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
sectionSeparator.y = -2;
sectionSeparator.visible = Options.charterShowSections;
@@ -135,7 +143,7 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
beatSeparator = new FlxBackdrop(null, Y, 0, 0);
beatSeparator.y = -1;
beatSeparator.visible = Options.charterShowBeats;
for(sep in [sectionSeparator, beatSeparator]) {
sep.makeSolid(1, 1, -1);
sep.alpha = 0.5;
@@ -192,8 +200,9 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
} else alpha = 0.9;
for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit, topSeparator, bottomSeparator, conductorFollowerSpr]) {
spr.x = x; spr.alpha = alpha;
for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit,
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) {
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
spr.cameras = this.cameras;
}
@@ -206,6 +215,25 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> {
bottomLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom));
bottomLimit.updateHitbox();
waveformSprite.visible = waveformSprite.shader != null;
if (waveformSprite.shader == null) return;
waveformSprite.scale.set(160, FlxG.height * (1/cameras[0].zoom));
waveformSprite.updateHitbox();
waveformSprite.y = (cameras[0].scroll.y+FlxG.height/2)-(waveformSprite.height/2);
if (waveformSprite.y < 0) {waveformSprite.scale.y += waveformSprite.y; waveformSprite.y = 0;}
if (waveformSprite.y + waveformSprite.height > bottomLimit.y) {
waveformSprite.scale.y -= (waveformSprite.y + waveformSprite.height)-(bottomLimit.y);
waveformSprite.y = (bottomLimit.y) - waveformSprite.scale.y;
}
waveformSprite.updateHitbox();
waveformSprite.shader.data.pixelOffset.value = [Math.max(conductorFollowerSpr.y - ((FlxG.height * (1/cameras[0].zoom)) * 0.5), 0)];
waveformSprite.shader.data.textureRes.value = [waveformSprite.width, waveformSprite.height];
waveformSprite.shader.data.playerPosition.value = [conductorFollowerSpr.y];
}
}
@@ -0,0 +1,86 @@
package funkin.editors.charter;
class CharterDeleteAnim extends CharterNote {
public var garbageIcon:FlxSprite;
public var garbageCircle:FlxSprite;
public var deleteNotes:Array<{note:CharterNote, time:Float}> = [];
public var deleteTime:Float = .4;
public function new() {
super();
sustainSpr.color = 0xFF630000; color = 0xFF605A5A;
snappedToStrumline = selectable = autoAlpha = false;
@:privateAccess __animSpeed = 1.25;
garbageIcon = new FlxSprite().loadGraphic(Paths.image("editors/deleter"));
garbageIcon.color = 0xFF880000;
garbageIcon.cameras = [Charter.instance.uiCamera];
garbageCircle = new FlxSprite().loadGraphic(Paths.image("editors/deleter circle"));
garbageCircle.color = 0xFF880000; garbageCircle.alpha = .6;
garbageCircle.cameras = [Charter.instance.uiCamera];
}
var __garbageAlpha:Float = 0;
var __deletionTimer:Float = .1;
@:noCompletion var __mousePos:FlxPoint = FlxPoint.get();
public override function update(elapsed:Float) {
for (deleteData in deleteNotes) {
deleteData.time -= elapsed;
if (deleteData.time < 0) deleteNotes.remove(deleteData);
}
if (FlxG.mouse.justPressedRight) __deletionTimer = .1;
__deletionTimer -= elapsed;
__garbageAlpha = FlxMath.lerp(__garbageAlpha, FlxG.mouse.pressedRight && __deletionTimer <= 0 ? 1 : 0, 1/10);
FlxG.mouse.getWorldPosition(Charter.instance.uiCamera, __mousePos);
if (FlxG.mouse.pressedRight && __deletionTimer <= 0) {
garbageIcon.setPosition(
__mousePos.x + garbageIcon.width/2 + (.65*FlxG.random.float(-1, 1)) + 2,
__mousePos.y - garbageIcon.height + (.65*FlxG.random.float(-1, 1)) - 8
);
garbageCircle.setPosition(
__mousePos.x - garbageCircle.width/2,
(__mousePos.y - garbageCircle.height/2) + 2
);
}
garbageIcon.alpha = __garbageAlpha;
garbageCircle.alpha = __garbageAlpha*.6;
}
public override function draw() @:privateAccess {
for (deleteData in deleteNotes) {
y = deleteData.note.y + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :((
x = deleteData.note.x + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :((
angle = deleteData.note.angle; alpha = 1;
animation.curAnim.curFrame = 3;
sustainSpr.scale.set(10, (40 * deleteData.note.susLength) + (height/2));
sustainSpr.updateHitbox(); sustainSpr.follow(this, 15, 20);
sustainSpr.exists = deleteData.note.susLength != 0; sustainSpr.alpha = .8;
typeText.text = Std.string(deleteData.note.type);
typeText.exists = deleteData.note.type != 0; typeText.alpha = .4;
typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2));
for (member in [this, sustainSpr, typeText])
member.alpha *= FlxEase.quadInOut(deleteData.time/deleteTime);
super.draw();
}
if (garbageIcon.alpha > 0) garbageIcon.draw();
if (garbageCircle.alpha > 0) garbageCircle.draw();
}
public override function destroy() {
super.destroy();
__mousePos.put();
}
}
@@ -51,7 +51,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
if (Assets.exists(Paths.pack('events/$name'))) {
var packimg = Assets.getText(Paths.pack('events/$name')).split('________PACKSEP________')[3];
if (isBase64 = (packimg != null))
path = Assets.getText(Paths.pack('events/$name')).split('________PACKSEP________')[3];
path = Assets.getText(Paths.pack('events/$name')).split('________PACKSEP________')[3];
}
var spr = new FlxSprite().loadGraphic(isBase64 ? openfl.display.BitmapData.fromBase64(path.trim(), 'UTF8') : path);
return spr;
@@ -79,13 +79,13 @@ class CharterEventScreen extends UISubstateWindow {
else {
chartEvent.events = [for (i in eventsList.buttons.members) i.event];
chartEvent.refreshEventIcons();
Charter.instance.updateBPMEvents();
Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)]));
}
}
close();
Charter.instance.updateBPMEvents();
});
saveButton.x -= saveButton.bWidth;
add(saveButton);
@@ -6,7 +6,7 @@ import funkin.editors.extra.PropertyButton;
using StringTools;
class MetaDataScreen extends UISubstateWindow {
class CharterMetaDataScreen extends UISubstateWindow {
public var metadata:ChartMetaData;
public var saveButton:UIButton;
public var closeButton:UIButton;
+2 -2
View File
@@ -31,7 +31,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
public function new() {
super();
antialiasing = true; ID = -1;
loadGraphic(Paths.image('editors/charter/note'), true, 157, 154);
animation.add("note", [for(i in 0...frames.frames.length) i], 0, true);
@@ -154,7 +154,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
if (strumLine != null) {
sustainSpr.alpha = alpha = !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1);
typeText.alpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
typeText.alpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1);
}
colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1;
@@ -29,11 +29,11 @@ class CharterNoteGroup extends FlxTypedGroup<CharterNote> {
for(i in begin...end) {
__loopSprite = members[i];
if (!Charter.selection.contains(__loopSprite))
if (__loopSprite != null && !Charter.selection.contains(__loopSprite))
noteFunc(__loopSprite);
}
for(c in Charter.selection.copy())
if (c is CharterNote) noteFunc(cast (c, CharterNote));
if (c != null && c is CharterNote) noteFunc(cast (c, CharterNote));
__currentlyLooping = oldCur;
}
@@ -64,7 +64,7 @@ class CharterNoteGroup extends FlxTypedGroup<CharterNote> {
if (autoSort && members.length != __lastSort)
sortNotes();
forEach((n) -> {
if(n.exists && n.active) {
n.cameras = n.__lastDrawCameras = cameras;
@@ -44,18 +44,18 @@ class CharterNoteHoverer extends CharterNote {
if (Charter.instance.gridActionType == NOTE_DRAG) {
var verticalChange:Float = (__mousePos.y - Charter.instance.dragStartPos.y) / 40;
var horizontalChange:Int = CoolUtil.floorInt((__mousePos.x - (Std.int(Charter.instance.dragStartPos.x / 40) * 40)) / 40);
for (s in Charter.selection) {
if (s != null && s.draggable && s is CharterNote) {
var draggingNote:CharterNote = cast(s, CharterNote);
y = (draggingNote.step + verticalChange);
if (!FlxG.keys.pressed.SHIFT)
if (!FlxG.keys.pressed.SHIFT)
y -= ((draggingNote.step + verticalChange)
- Charter.instance.quantStepRounded(draggingNote.step+verticalChange, verticalChange > 0 ? 0.35 : 0.65));
y *= 40;
var newID:Int = Std.int(FlxMath.bound(draggingNote.fullID + horizontalChange, 0, (Charter.instance.strumLines.members.length*4)-1));
x = (id=newID) * 40; y = FlxMath.bound(y, 0, (Charter.instance.__endStep*40) - height);
angle = switch(animation.curAnim.curFrame = (draggingNote.id % 4)) {
case 0: -90;
case 1: 180;
@@ -108,7 +108,7 @@ class NoteTypeButton extends UIButton {
var path:String = 'game/notes/default';
if (Assets.exists(Paths.image('game/notes/${notetype}')))
path = 'game/notes/${notetype}';
noteSpr.frames = Paths.getFrames(path);
noteSpr.animation.addByPrefix('scroll', 'green0');
noteSpr.animation.play("scroll");
@@ -29,7 +29,7 @@ class CharterSelection extends EditorTreeMenu {
for(s in freeplayList.songs) new EditorIconOption(s.name, "Press ACCEPT to choose a difficulty to edit.", s.icon, function() {
curSong = s;
var list:Array<OptionType> = [
for(d in s.difficulties) if (d != "")
for(d in s.difficulties) if (d != "")
new TextOption(d, "Press ACCEPT to edit the chart for the selected difficulty", function() {
FlxG.switchState(new Charter(s.name, d));
})
@@ -46,6 +46,8 @@ class CharterSelection extends EditorTreeMenu {
}));
main = new OptionsScreen("Chart Editor", "Select a song to modify the charts from.", list);
DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]);
}
override function createPost() {
@@ -107,7 +109,7 @@ class CharterSelection extends EditorTreeMenu {
sys.FileSystem.createDirectory('$songFolder/charts');
// Save Files
sys.io.File.saveContent('$songFolder/meta.json', Json.stringify(creation.meta, "\t"));
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, "\t"));
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Paths.SOUND_EXT}', creation.instBytes);
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Paths.SOUND_EXT}', creation.voicesBytes);
#end
@@ -145,9 +147,7 @@ class CharterSelection extends EditorTreeMenu {
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${curSong.name}';
// Save Files
#if sys
sys.io.File.saveContent('$songFolder/charts/${name}.json', Json.stringify(data, "\t"));
#end
CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, "\t"));
// Add to List
curSong.difficulties.push(name);
@@ -160,7 +160,7 @@ class CharterSelection extends EditorTreeMenu {
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
if (meta.difficulties != null && !meta.difficulties.contains(name)) {
meta.difficulties.push(name);
sys.io.File.saveContent('$songFolder/meta.json', Json.stringify(meta));
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(meta));
}
}
}
@@ -79,7 +79,7 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> {
fixEvents();
}
public inline function fixEvents() {
public inline function fixEvents() {
for (i in Charter.instance.eventsGroup.members) {
for (j in i.events) {
var paramTypes:Array<EventParamInfo> = EventsData.getEventParams(j.name);
@@ -5,6 +5,7 @@ import funkin.game.Character;
import funkin.editors.ui.UITopMenu.UITopMenuButton;
import funkin.game.HealthIcon;
import funkin.backend.chart.ChartData.ChartStrumLine;
import funkin.backend.shaders.CustomShader;
import flixel.sound.FlxSound;
class CharterStrumline extends UISprite {
@@ -22,6 +23,17 @@ class CharterStrumline extends UISprite {
public var vocals:FlxSound;
public var selectedWaveform(default, set):Int = -1;
public function set_selectedWaveform(value:Int):Int {
if (value == -1) waveformShader = null;
else {
var shaderName:String = Charter.waveformHandler.waveformList[value];
waveformShader = Charter.waveformHandler.waveShaders.get(shaderName);
}
return selectedWaveform = value;
}
public var waveformShader:CustomShader;
public function new(strumLine:ChartStrumLine) {
super();
this.strumLine = strumLine;
@@ -60,6 +72,8 @@ class CharterStrumline extends UISprite {
vocals = strumLine.vocalsSuffix.length > 0 ? FlxG.sound.load(Paths.voices(PlayState.SONG.meta.name, PlayState.difficulty, strumLine.vocalsSuffix)) : new FlxSound();
vocals.group = FlxG.sound.defaultMusicGroup;
selectedWaveform = -1;
}
private var __healthYOffset:Float = 0;
@@ -154,6 +168,22 @@ class CharterStrumlineOptions extends UITopMenuButton {
icon: 3
}
];
contextMenu.insert(0, {
label: "No Waveform",
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
});
for (i => name in Charter.waveformHandler.waveformList)
contextMenu.insert(1+i, {
label: name,
onSelect: function(_) {strLine.selectedWaveform = i;},
icon: strLine.selectedWaveform == i ? 6 : 5
});
contextMenu.insert(1+Charter.waveformHandler.waveformList.length, null);
super.openContextMenu();
}
}
@@ -59,10 +59,10 @@ class CharterStrumlineButton extends UISprite {
super.update(elapsed);
UIState.state.updateSpriteRect(button);
if(UIState.state.isOverlapping(button, button.__rect)) {
if(UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect)) {
buttonScale.set(0.95, 0.95);
if (FlxG.mouse.justReleased && onClick != null) {
onClick();
if (FlxG.mouse.justPressed && onClick != null) {
onClick();
if (animationOnClick) pressAnimation();
}
}
@@ -96,7 +96,7 @@ class CharterStrumlineButton extends UISprite {
scaleTween = FlxTween.tween(buttonScaleOffset, {x: .015 * (extra ? 3 : 1), y: .02 * (extra ? 3 : 1)}, .12, {ease: FlxEase.circOut})
.then(FlxTween.tween(buttonScaleOffset, {x: 0, y: 0}, .1 + FlxG.random.float(.1, .3), {ease: FlxEase.circIn}));
if (!extra) return;
if (!extra) return;
if (angleTween != null) angleTween.cancel(); button.angle = 0;
angleTween = FlxTween.tween(button, {angle: 360}, .5, {ease: FlxEase.circInOut});
@@ -63,7 +63,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
var charFileList = Character.getList(true);
if (charFileList.length == 0) charFileList = Character.getList(false);
charactersList = new UIButtonList<CharacterButton>(15, title.y + title.height + 36, 250, 269, "", FlxPoint.get(250, 54), null, 0);
charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, "New Char", charFileList, charactersList));
charactersList.cameraSpacing = 0;
@@ -112,7 +112,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
var strOffset:Float = strumLine.strumLinePos == null ? (strumLine.type == 1 ? 0.75 : 0.25) : strumLine.strumLinePos;
var startingPos:FlxPoint = strumLine.strumLinePos == null ?
var startingPos:FlxPoint = strumLine.strumPos == null ?
FlxPoint.get(strOffset, 50) :
FlxPoint.get(strOffset, strumLine.strumPos[1]);
@@ -142,7 +142,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
closeButton.color = 0xFFFF0000;
var suffixlist = ["NONE"];
for (i in Paths.getFolderContent('songs/${Charter.__song.toLowerCase()}/song'))
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;
@@ -0,0 +1,110 @@
package funkin.editors.charter;
import flixel.util.FlxColor;
import funkin.backend.system.Conductor;
import flixel.sound.FlxSound;
import funkin.backend.shaders.CustomShader;
import openfl.display.BitmapData;
class CharterWaveformHandler extends FlxBasic {
public var ampsNeeded:Float = 0;
public var ampSqrt(get, never):Int;
public function get_ampSqrt():Int {
var sqrt:Float = Math.sqrt(Math.floor(ampsNeeded/3));
var ampRet:Int = Math.floor(sqrt);
if (sqrt % 1 > 0) ampRet += 1;
return ampRet;
}
public var waveDatas:Map<String, BitmapData> = [];
public var waveShaders:Map<String, CustomShader> = [];
public var sounds:Map<String, FlxSound> = [];
public var analyzers:Map<String, AudioAnalyzer> = [];
public var waveformList:Array<String> = [];
public function new() {super(); exists = false;}
public function generateData(name:String, sound:FlxSound):BitmapData {
if (!sounds.exists(name)) sounds.set(name, sound);
if (!analyzers.exists(name))
analyzers.set(name, new AudioAnalyzer(sound));
var analyzer:AudioAnalyzer = analyzers.get(name);
var pixelsNeeded:Int = Math.floor(ampsNeeded/3);
if ((ampsNeeded/3) % 1 > 0) pixelsNeeded += 1;
var waveData:BitmapData = new BitmapData(
ampSqrt, 1+Math.floor(pixelsNeeded/ampSqrt), true, 0xFF000000
);
for (y in 0...waveData.height)
for (x in 0...waveData.width) {
var amplitudes:Array<Float> = [0., 0., 0.];
for (color in 0...3) {
var gridY:Float = (y * (waveData.width * 3)) + (x * 3) + color;
var startTime:Float = Conductor.getTimeForStep(gridY/40);
if (startTime > sound.length) if (color == 0) break; else continue;
var endTime:Float = Conductor.getTimeForStep((gridY+1)/40);
if (endTime > sound.length) if (color == 0) break; else continue;
var amplitude:Float = analyzer.analyze(startTime, endTime);
amplitudes[color] = amplitude;
}
waveData.setPixel(x, y, FlxColor.fromRGBFloat(amplitudes[0], amplitudes[1], amplitudes[2]));
}
waveDatas.set(name, waveData);
return waveData;
}
public function generateShader(name:String, sound:FlxSound):CustomShader {
if (!waveDatas.exists(name)) generateData(name, sound);
var waveData:BitmapData = waveDatas.get(name);
var waveShader:CustomShader = new CustomShader("engine/editorWaveforms");
waveShader.data.waveformSize.value = [waveData.width, waveData.height];
waveShader.data.waveformTexture.input = waveData;
waveShader.data.textureRes.value = [0, 0];
waveShader.data.pixelOffset.value = [0];
waveShader.data.lowDetail.value = [Options.charterLowDetailWaveforms];
waveShaders.set(name, waveShader);
return waveShader;
}
public function clearWaveform(name:String) {
waveDatas.get(name).dispose();
waveDatas.remove(name);
waveShaders.set(name, null);
waveShaders.remove(name);
analyzers.set(name, null);
analyzers.remove(name);
sounds.remove(name);
waveformList.remove(name);
}
public function clearWaveforms() {
for (data in waveDatas) data.dispose();
for (shader in waveShaders) shader = null;
for (analyzer in analyzers) analyzer = null;
waveDatas.clear(); waveShaders.clear();
analyzers.clear(); sounds.clear();
waveformList = [];
}
public override function destroy() {
clearWaveforms();
super.destroy();
}
}
+4 -4
View File
@@ -87,7 +87,7 @@ class UIAudioPlayer extends UIButton {
playingSprite.animation.play(sound.playing ? "playing" : "paused");
timeText.text = '${FlxStringUtil.formatTime(sound.time/1000, true)} / ${FlxStringUtil.formatTime(sound.length/1000)}';
if(timeBarPlayer.clipRect == null)
if(timeBarPlayer.clipRect == null)
timeBarPlayer.clipRect = new FlxRect(0, 0, timeBarPlayer.frameWidth, timeBarPlayer.frameHeight);
timeBarPlayer.clipRect.x = timeBarPlayer.frameWidth * (sound.time/sound.length);
@@ -106,13 +106,13 @@ class UIAudioPlayer extends UIButton {
var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
if (((mousePos.x > (spritePos.x)) && (mousePos.x < (spritePos.x) + timeBar.barWidth))
if (((mousePos.x > (spritePos.x)) && (mousePos.x < (spritePos.x) + timeBar.barWidth))
&& ((mousePos.y > (spritePos.y)) && (mousePos.y < (spritePos.y) + timeBar.barHeight))) {
if (FlxG.mouse.justPressed) {
draggingObj = sprite;
wasPlaying = draggingObj == timeBar ? sound.playing : false;
}
if (FlxG.mouse.pressed) {
if (draggingObj == timeBar) {
if (sound.playing) sound.pause();
@@ -137,7 +137,7 @@ class UIAudioPlayer extends UIButton {
super.destroy();
sound.stop();
@:privateAccess sound.reset();
@:privateAccess sound.reset();
bytes = null;
}
}
-1
View File
@@ -121,7 +121,6 @@ class UIButtonList<T:UIButton> extends UIWindow {
if (dragCallback != null) dragCallback(cast button, button.ID, i);
button.ID = i; // Ok back to normal :D
}
}
addButton.selectable = (hovered && !dragging);
+6 -6
View File
@@ -87,11 +87,11 @@ class UIColorwheel extends UISliceSprite {
for (item in [colorPicker, colorPickerSelector, colorSlider, colorSliderSelector, colorHexTextBox, hexLabel, rgbLabel])
members.push(item);
updateWheel();
updateWheel(false);
}
inline function updateColorPickerSelector()
colorPickerSelector.selector.setPosition(colorPicker.x + (colorPicker.width *saturation) - 8, colorPicker.y + (colorPicker.height + (colorPicker.height * -brightness))- 8);
colorPickerSelector.selector.setPosition(colorPicker.x + (colorPicker.width *saturation) - 8, colorPicker.y + (colorPicker.height + (colorPicker.height * -brightness))- 8);
inline function updateColorPickerMouse(mousePos:FlxPoint) {
saturation = mousePos.x/colorPicker.width; brightness = 1 + -(mousePos.y/colorPicker.height);
@@ -103,12 +103,13 @@ class UIColorwheel extends UISliceSprite {
inline function updateColorSliderMouse(mousePos:FlxPoint)
hue = (mousePos.y / colorSlider.height) * 360;
public function updateWheel() {
public function updateWheel(checkChanged:Bool = true) {
if(checkChanged) colorChanged = true;
colorPickerShader.hset("hue", hue / 360);
colorPickerSelector.curColor = color = curColor = FlxColor.fromHSB(hue, saturation, brightness); colorSliderSelector.curColor = FlxColor.fromHSB(hue, 1, 1);
updateColorPickerSelector(); updateColorSliderPickerSelector();
colorHexTextBox.label.text = curColorString = curColor.toHexString(false).replace("0x", "#");
colorHexTextBox.label.text = curColorString = curColor.toWebString();
for (numStepper in rgbNumSteppers) {
numStepper.label.text = Std.string(switch (numStepper.ID) {
default: curColor.red;
@@ -121,7 +122,7 @@ class UIColorwheel extends UISliceSprite {
// For Character Editor
public var colorChanged:Bool = false;
// Make the colorwhell feel better
// Make the colorwheel feel better
static inline var hitBoxExtenstion:Float = 8;
public override function update(elapsed:Float) {
@@ -138,7 +139,6 @@ class UIColorwheel extends UISliceSprite {
if (sprite == colorSlider) updateColorSliderMouse(mousePos);
if (sprite == colorPicker) updateColorPickerMouse(mousePos);
updateWheel();
colorChanged = true;
spritePos.put();
break;

Some files were not shown because too many files have changed in this diff Show More