Compare commits
86
Commits
v1.0.1-rc1
..
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9757b002bd | ||
|
|
3140ee8512 | ||
|
|
c96ee6c465 | ||
|
|
1c4c962f4d | ||
|
|
49d4708265 | ||
|
|
ffa5e66bc8 | ||
|
|
18fcacc2b5 | ||
|
|
a2ff5c112e | ||
|
|
3862995213 | ||
|
|
f7e4875097 | ||
|
|
cabdffd058 | ||
|
|
c28e056a8a | ||
|
|
3fd87c8d52 | ||
|
|
eaacc5dfc2 | ||
|
|
4573fef74d | ||
|
|
978022dc0b | ||
|
|
970d294ea1 | ||
|
|
7568cbfd99 | ||
|
|
c5686b09db | ||
|
|
ce4d1a0d61 | ||
|
|
2eac58aaa5 | ||
|
|
ce98ad0645 | ||
|
|
037f564676 | ||
|
|
5227ed3253 | ||
|
|
9e4b6f31c0 | ||
|
|
fe02ba7b4b | ||
|
|
ec63be9e77 | ||
|
|
88ea51064b | ||
|
|
647c70839d | ||
|
|
39237dd01d | ||
|
|
d3bd40baf3 | ||
|
|
7d7db50c19 | ||
|
|
8aa2023057 | ||
|
|
d979b5c33d | ||
|
|
ba50edfc99 | ||
|
|
5acabccd9a | ||
|
|
66c1cd9dde | ||
|
|
ec65cae8fc | ||
|
|
3de652c73e | ||
|
|
8303e7ecfd | ||
|
|
e17b5d6b86 | ||
|
|
0ff829e1c6 | ||
|
|
769a33f138 | ||
|
|
91abb37281 | ||
|
|
32bca0420e | ||
|
|
faa0dbb324 | ||
|
|
14c81ef26d | ||
|
|
62f4819493 | ||
|
|
8ba41990e6 | ||
|
|
6c92fcff8b | ||
|
|
6a802c2281 | ||
|
|
3a6f4819a9 | ||
|
|
e24a62a0b7 | ||
|
|
9c7bb8f073 | ||
|
|
6112e2f759 | ||
|
|
3939439086 | ||
|
|
59b54dfbae | ||
|
|
a83f62ab8a | ||
|
|
f86773223b | ||
|
|
b1133171c7 | ||
|
|
05006802ee | ||
|
|
0eaffe3ea9 | ||
|
|
03df52c542 | ||
|
|
1ca4b64402 | ||
|
|
b8b8d08be5 | ||
|
|
406f76af5d | ||
|
|
1a855e5061 | ||
|
|
41474738c7 | ||
|
|
bcbfa90f37 | ||
|
|
b39f0d993e | ||
|
|
65839283e5 | ||
|
|
d2a113907a | ||
|
|
d3ef27b9c7 | ||
|
|
90fc02c128 | ||
|
|
cbaa5a6155 | ||
|
|
1f479a5b16 | ||
|
|
967bd4cf01 | ||
|
|
3074107322 | ||
|
|
9c7cc22a47 | ||
|
|
7c8dd987dd | ||
|
|
48624554cd | ||
|
|
a86749953f | ||
|
|
482c213079 | ||
|
|
8d8e6e453a | ||
|
|
d6937e866c | ||
|
|
9ce1486269 |
@@ -2,12 +2,18 @@
|
||||
name: Bug Report
|
||||
about: Report a bug or critical performance issue
|
||||
title: 'Bug Report: [DESCRIBE YOUR BUG IN DETAIL HERE]'
|
||||
labels: bug
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
|
||||
[weed]: <> (MAKE SURE TO ADD YOUR TARGET OR IT'LL BE IGNORED!)
|
||||
|
||||
### What version of the engine are you using?
|
||||
[weed]: <> (IF YOU COMPILED THE ENGINE PLEASE PROVIDE A COMMIT HASH THAT WE CAN BASE OFF OF)
|
||||
Version:
|
||||
|
||||
### Which target are you using?
|
||||
[weed]: <> (CHANGE [ ] TO [x] IN FRONT OF YOUR TARGET!)
|
||||
|
||||
@@ -17,4 +23,4 @@ labels: bug
|
||||
|
||||
### Explain your issue
|
||||
##### Please check first if your issue haven't already been reported yet.
|
||||
[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
|
||||
[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
---
|
||||
name: Compiling help
|
||||
about: If you need help compiling the game, and you're running into issues. (Look through the 'compiling help' label in case it's been solved!)
|
||||
about: If you need help compiling the game, and you're running into issues. (Look
|
||||
through the 'compiling help' label in case it's been solved!)
|
||||
title: 'Compiling help: [BRIEF DESCRIPTION / ERROR MESSAGE OUTPUT]'
|
||||
labels: compiling help
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
name: Enhancement
|
||||
about: Suggest a new feature
|
||||
title: 'Enhancement: '
|
||||
labels: enhancement
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Please check for duplicates or similar issues before creating this issue.
|
||||
## What is your suggestion, and why should it be implemented?
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Codename Engine (Executable Only)
|
||||
path: export/release/macos/bin/CodenameEngine
|
||||
path: export/release/macos/bin/CodenameEngine.app/Contents/MacOS/CodenameEngine
|
||||
- name: Uploading artifact (entire build)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
Vendored
+33
-1
@@ -6,5 +6,37 @@
|
||||
"*.hscript": "haxe",
|
||||
"*.hsc": "haxe",
|
||||
"*.hxs": "haxe"
|
||||
}
|
||||
},
|
||||
"lime.targetConfigurations": [
|
||||
{
|
||||
"label": "Windows (Test Build)",
|
||||
"target": "windows",
|
||||
"args": ["-DTEST_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "Linux (Test Build)",
|
||||
"target": "linux",
|
||||
"args": ["-DTEST_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "MacOS (Test Build)",
|
||||
"target": "macos",
|
||||
"args": ["-DTEST_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Test Build)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DTEST_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "Linux / Debug (Test Build)",
|
||||
"target": "linux",
|
||||
"args": ["-debug", "-DTEST_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "MacOS / Debug (Test Build)",
|
||||
"target": "macos",
|
||||
"args": ["-debug", "-DTEST_BUILD"]
|
||||
},
|
||||
]
|
||||
}
|
||||
+2
-1
@@ -79,4 +79,5 @@ _**QOL = Quality of Life**_
|
||||
- Features not found in other editors!
|
||||
- Every single state & substate can be modified via HScript (`data/states/StateName.hx`)
|
||||
- **Instances launched via `lime test windows` will automatically use assets from source.**
|
||||
</details>
|
||||
- Modcharting features powered by [FunkinModchart](https://lib.haxe.org/p/funkin-modchart/).
|
||||
</details>
|
||||
|
||||
@@ -87,4 +87,5 @@ If you don't have a github account please go onto https://codename-engine.com/ a
|
||||
- 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) (video cutscene/mp4 support) and [hxdiscord_rpc](https://github.com/MAJigsaw77/hxdiscord_rpc) (discord rpc integration)
|
||||
- Credits to [TheoDev](https://github.com/TheoDevelops) for [FunkinModchart](https://lib.haxe.org/p/funkin-modchart/). ***(library used for modcharting features)***
|
||||
</details>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 210 B |
Binary file not shown.
|
After Width: | Height: | Size: 215 B |
@@ -9,6 +9,8 @@
|
||||
<str id="save">Save</str>
|
||||
<str id="saveClose">Save & Close</str>
|
||||
<str id="close">Close</str>
|
||||
<str id="notNow">Not Now</str>
|
||||
<str id="yes">Yes</str>
|
||||
</group>
|
||||
|
||||
<group name="UIImageExplorer" prefix="uiImageExplorer.">
|
||||
@@ -43,12 +45,15 @@
|
||||
<group name="CharterSelection" prefix="charterSelection.">
|
||||
<str id="desc">Select a song to modify the charts from.</str>
|
||||
<str id="acceptSong">Press ACCEPT to choose a difficulty to edit.</str>
|
||||
<str id="acceptVariation">Press ACCEPT to choose a song variation to edit.</str>
|
||||
<str id="acceptDifficulty">Press ACCEPT to edit the chart for the selected difficulty.</str>
|
||||
<str id="selectDifficulty">Select a difficulty to continue.</str>
|
||||
<str id="newDifficulty">New Difficulty</str>
|
||||
<str id="newDifficultyDesc">Press ACCEPT to create a new chart difficulty.</str>
|
||||
<str id="newSong">New Song</str>
|
||||
<str id="newSongDesc">Press ACCEPT to create a new song.</str>
|
||||
<str id="newVariation">New Variation</str>
|
||||
<str id="newVariationDesc">Press ACCEPT to create a new song variation.</str>
|
||||
</group>
|
||||
|
||||
<group name="ChartCreationScreen" prefix="chartCreation.">
|
||||
@@ -102,6 +107,7 @@
|
||||
|
||||
<str id="songData">Song Data</str>
|
||||
<str id="songName">Song Name</str>
|
||||
<str id="variation">Song Variation</str>
|
||||
<str id="bpm">BPM</str>
|
||||
<str id="timeSignature">Time Signature</str>
|
||||
<str id="menusData">Menus Data (Freeplay/Story)</str>
|
||||
@@ -111,6 +117,7 @@
|
||||
<str id="color">Color</str>
|
||||
<str id="difficulties">Difficulties</str>
|
||||
<str id="customValues">Custom Values (Advanced)</str>
|
||||
<str id="needsVoices">Needs Voices</str>
|
||||
|
||||
<str id="opponentMode">Opponent Mode</str>
|
||||
<str id="coopAllowed">Co-op Mode</str>
|
||||
@@ -665,4 +672,12 @@
|
||||
<str id="saveAndExitToMenu">Save & Exit To Menu</str>
|
||||
<str id="cancel">Cancel</str>
|
||||
</group>
|
||||
|
||||
<group name="ModConfigWarning" prefix="modConfigWarning.">
|
||||
<str id="warningTitle">Missing mod folder configuration!</str>
|
||||
<str id="warningDesc">Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)</str>
|
||||
|
||||
<str id="createdTitle">Created mod config!</str>
|
||||
<str id="createdDesc">Your mod config file has been created at {0}!</str>
|
||||
</group>
|
||||
</language>
|
||||
@@ -42,6 +42,7 @@
|
||||
<str id="back">Back</str>
|
||||
<str id="reset">Reset</str>
|
||||
<str id="pause">Pause</str>
|
||||
<str id="changeMode">Change Mode</str>
|
||||
</group>
|
||||
|
||||
<str id="category.engine">Engine</str>
|
||||
@@ -50,6 +51,8 @@
|
||||
</group>
|
||||
|
||||
<str id="category.volume">Volume</str>
|
||||
<str id="volume.up">Up</str>
|
||||
<str id="volume.down">Down</str>
|
||||
<str id="volume.mute">Mute</str>
|
||||
|
||||
<str id="category.developer">Developer</str>
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
<str id="modesAllowed">Modos Permitidos</str>
|
||||
<str id="difficulties">Difficultades</str>
|
||||
<str id="customValues">Valores Especiales (Avanzado)</str>
|
||||
<str id="needsVoices">Necesita Voces</str>
|
||||
|
||||
<str id="opponentMode">Modo Oponente</str>
|
||||
<str id="coopAllowed">Modo Co-op</str>
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
</group>
|
||||
|
||||
<str id="category.volume">Volumen</str>
|
||||
<str id="volume.up">Arriba</str>
|
||||
<str id="volume.down">Abajo</str>
|
||||
<str id="volume.mute">mutear</str>
|
||||
|
||||
<str id="category.developer">Desarrollador</str>
|
||||
|
||||
@@ -108,8 +108,9 @@
|
||||
<str id="color">Colore</str>
|
||||
<str id="difficulties">Difficoltà</str>
|
||||
<str id="customValues">Valori Personalizzati (Avanzato)</str>
|
||||
<str id="needsVoices">Bisogno Voci</str>
|
||||
|
||||
<str id="opponentMode">Modalità Avvers.</str>
|
||||
<str id="opponentMode">Modalità Avvers</str>
|
||||
<str id="coopAllowed">Modalità Co-op</str>
|
||||
</group>
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
<str id="switchMod">Cambia Mod</str>
|
||||
</group>
|
||||
|
||||
<str id="volume.up">Su</str>
|
||||
<str id="volume.down">Giù</str>
|
||||
<str id="volume.mute">Muto</str>
|
||||
|
||||
<str id="category.developer">Sviluppatore</str>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<str id="save">Zapisz</str>
|
||||
<str id="saveClose">Zapisz i Zamknij</str>
|
||||
<str id="close">Zamknij</str>
|
||||
<str id="notNow">Nie Teraz</str>
|
||||
<str id="yes">Tak</str>
|
||||
</group>
|
||||
|
||||
<group name="UIImageExplorer" prefix="uiImageExplorer.">
|
||||
@@ -109,6 +111,7 @@
|
||||
<str id="color">Kolor</str>
|
||||
<str id="difficulties">Trudności</str>
|
||||
<str id="customValues">Własna Waluta (Zaawansowane)</str>
|
||||
<str id="needsVoices">Potrzebne Wokale</str>
|
||||
|
||||
<str id="opponentMode">Tryb Przeciwnika</str>
|
||||
<str id="coopAllowed">Tryb Ko-operacyjny</str>
|
||||
@@ -653,4 +656,12 @@
|
||||
<str id="saveAndExitToMenu">Zapisz i Wyjdź do Menu</str>
|
||||
<str id="cancel">Anuluj</str>
|
||||
</group>
|
||||
|
||||
<group name="ModConfigWarning" prefix="modConfigWarning.">
|
||||
<str id="warningTitle">Brakujący plik konfiguracyjny modu!</str>
|
||||
<str id="warningDesc">Twój mod obecnie brakuje jego pliku konfiguracyjnego!\n\nCzy chcesz go automatycznie wygenerować?\n\n(PS: Jeżeli to nie twój mod, wyłącz Tryb Dewelopera, aby zapobiec wyskakiwaniu się tego okienka.)</str>
|
||||
|
||||
<str id="createdTitle">Utworzono konfigurację modu!</str>
|
||||
<str id="createdDesc">Twój plik konfiguracyjny modu został utworzony w {0}!</str>
|
||||
</group>
|
||||
</language>
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<str id="credits.allContributors">Wszyscy współpracownicy silnika! - Naciśnij RESET aby zaktualizować listę (Jeden reset co 2 minuty).</str>
|
||||
</group>
|
||||
<group name="CreditsMain">
|
||||
<str id="credits.name">Kredyty</str>
|
||||
<str id="credits.name">Twórcy</str>
|
||||
<str id="credits.madePossible">Ludzie którzy to umożliwili!</str>
|
||||
<str id="credits.selectCodename">Wybierz to aby zobaczyć wszystkich współpracowników!</str>
|
||||
<str id="credits.selectBase">Wybierz to aby otworzyć stronę itch.io oryginalnej gry!</str>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<str id="back">Wstecz</str>
|
||||
<str id="reset">Reset</str>
|
||||
<str id="pause">Pauza</str>
|
||||
<str id="changeMode">Zmień Tryb</str>
|
||||
</group>
|
||||
|
||||
<str id="category.engine">Silnik</str>
|
||||
@@ -46,6 +47,8 @@
|
||||
</group>
|
||||
|
||||
<str id="category.volume">Głośność</str>
|
||||
<str id="volume.up">Głóśniej</str>
|
||||
<str id="volume.down">Ciszej</str>
|
||||
<str id="volume.mute">Wycisz</str>
|
||||
|
||||
<str id="category.developer">Deweloper</str>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 68 KiB |
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<TextureAtlas imagePath="credits.png" width="3020" height="169">
|
||||
<SubTexture name="credits basic0000" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0001" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0002" x="1" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0003" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0004" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0005" x="433" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0006" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0007" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits basic0008" x="865" y="1" width="430" height="114" frameX="-0" frameY="-0" frameWidth="430" frameHeight="114"/>
|
||||
<SubTexture name="credits white0000" x="1297" y="1" width="574" height="164" frameX="-0" frameY="24" frameWidth="574" frameHeight="169"/>
|
||||
<SubTexture name="credits white0001" x="2446" y="1" width="573" height="167" frameX="-0" frameY="25" frameWidth="574" frameHeight="169"/>
|
||||
<SubTexture name="credits white0002" x="1873" y="1" width="571" height="165" frameX="-0" frameY="21" frameWidth="574" frameHeight="169"/>
|
||||
<TextureAtlas imagePath="credits.png" width="3341" height="162">
|
||||
<SubTexture name="credits basic0000" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0001" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0002" x="1" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0003" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0004" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0005" x="443" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0006" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0007" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits basic0008" x="885" y="1" width="440" height="116" frameX="-0" frameY="-0" frameWidth="440" frameHeight="116"/>
|
||||
<SubTexture name="credits white0000" x="2668" y="1" width="672" height="160" frameX="-0" frameY="25" frameWidth="672" frameHeight="160"/>
|
||||
<SubTexture name="credits white0001" x="1327" y="1" width="669" height="156" frameX="-2" frameY="24" frameWidth="672" frameHeight="160"/>
|
||||
<SubTexture name="credits white0002" x="1998" y="1" width="668" height="157" frameX="-4" frameY="22" frameWidth="672" frameHeight="160"/>
|
||||
</TextureAtlas>
|
||||
@@ -16,7 +16,7 @@
|
||||
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" />
|
||||
<git name="flxanimate" url="https://github.com/CodenameCrew/cne-flxanimate" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
|
||||
<git name="funkin-modchart" url="https://github.com/CodenameCrew/FunkinModchart" skipDeps="true" />
|
||||
<git name="funkin-modchart" url="https://github.com/TheoDevelops/FunkinModchart" ref="f30676579d8de7ccac7d12e13b664e8b2c42c6bf" skipDeps="true" />
|
||||
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
|
||||
|
||||
<!-- Documentation and other features -->
|
||||
|
||||
+2
-2
@@ -227,9 +227,9 @@
|
||||
<setenv name="HAXEPATH" value="./"/>
|
||||
|
||||
<haxedef name="HXCPP_M32" if="32bit" />
|
||||
<haxedef name="HXCPP_CHECK_POINTER" if="release" />
|
||||
<haxedef name="HXCPP_STACK_LINE" if="release" />
|
||||
<haxedef name="HXCPP_DEBUG_LINK" if="debug" />
|
||||
<haxedef name="HXCPP_CHECK_POINTER" />
|
||||
<haxedef name="HXCPP_STACK_LINE" />
|
||||
<haxedef name="HXCPP_CATCH_SEGV" />
|
||||
|
||||
<haxedef name="hscriptPos" /> <!-- for logging -->
|
||||
|
||||
@@ -38,6 +38,7 @@ class Paths
|
||||
for (entry in entries) {
|
||||
if (entry.toLowerCase() == part.toLowerCase()) {
|
||||
fixedPath += entry + (it != parts.length - 1 ? "/" : "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,18 +89,18 @@ class Paths
|
||||
inline static public function music(key:String, ?library:String, ?ext:String)
|
||||
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
|
||||
|
||||
inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
|
||||
inline static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (ext == null) ext = Flags.SOUND_EXT;
|
||||
var diff = getPath('songs/$song/song/Voices$prefix-${difficulty}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null);
|
||||
var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null);
|
||||
}
|
||||
|
||||
inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
|
||||
inline static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (ext == null) ext = Flags.SOUND_EXT;
|
||||
var diff = getPath('songs/$song/song/Inst$prefix-${difficulty}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null);
|
||||
var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null);
|
||||
return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null);
|
||||
}
|
||||
|
||||
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) {
|
||||
@@ -127,11 +128,11 @@ class Paths
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
static public function chart(song:String, ?difficulty:String):String
|
||||
static public function chart(song:String, ?difficulty:String, ?variant:String):String
|
||||
{
|
||||
difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY);
|
||||
|
||||
return getPath('songs/$song/charts/$difficulty.json', null);
|
||||
return getPath('songs/$song/charts/${variant != null ? variant + "/" : ""}$difficulty.json', null);
|
||||
}
|
||||
|
||||
public static function character(character:String):String {
|
||||
|
||||
@@ -71,8 +71,9 @@ class Chart {
|
||||
return LEGACY;
|
||||
}
|
||||
|
||||
public static function loadEventsJson(songName:String) {
|
||||
var path = Paths.file('songs/${songName}/events.json');
|
||||
public static function loadEventsJson(songName:String, ?variant:String) {
|
||||
var variantSuffix = variant != null && variant != "" ? '-$variant' : "";
|
||||
var path = Paths.file('songs/${songName}/events$variantSuffix.json');
|
||||
var data:Array<ChartEvent> = null;
|
||||
if (Assets.exists(path)) {
|
||||
try {
|
||||
@@ -83,62 +84,91 @@ class Chart {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true):ChartMetaData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
inline public static function defaultChartMetaFields(data:ChartMetaData):ChartMetaData {
|
||||
data.setFieldDefault("displayName", data.name);
|
||||
|
||||
var metaPath = Paths.file('songs/${songName}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json');
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
|
||||
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
|
||||
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
|
||||
data.setFieldDefault("instSuffix", "");
|
||||
data.setFieldDefault("vocalsSuffix", "");
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("variants", []);
|
||||
data.setFieldDefault("metas", []);
|
||||
|
||||
var data:ChartMetaData = null;
|
||||
var fromMods:Bool = fromMods;
|
||||
for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, ?variant:String, ?difficulty:String, fromMods:Bool = true, includeMetaVariations = true):ChartMetaData {
|
||||
var folder = 'songs/$songName', isVariant = false, data:ChartMetaData = null;
|
||||
var defaultPaths = [Paths.file('$folder/meta-$difficulty.json'), Paths.file('$folder/meta.json')], variantPaths = [];
|
||||
if (difficulty != null) defaultPaths.unshift(Paths.file('$folder/meta-$difficulty.json'));
|
||||
|
||||
if (variant != null && variant != '') {
|
||||
variantPaths.push(Paths.file('$folder/meta-$variant.json'));
|
||||
if (difficulty != null) variantPaths.unshift(Paths.file('$folder/meta-$variant-$difficulty.json'));
|
||||
}
|
||||
|
||||
for (path in variantPaths.concat(defaultPaths)) if (Assets.exists(path)) {
|
||||
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
|
||||
try {
|
||||
var tempData = Json.parse(Assets.getText(path));
|
||||
tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR);
|
||||
data = tempData;
|
||||
} catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
|
||||
if (data != null) break;
|
||||
} catch(e) Logs.trace('Failed to load song metadata for $songName ($path): ${Std.string(e)}', ERROR);
|
||||
|
||||
if (data != null) {
|
||||
isVariant = variantPaths.contains(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == null) data = {
|
||||
if (data != null) data.name = songName;
|
||||
else data = {
|
||||
name: songName,
|
||||
bpm: 100
|
||||
color: Flags.DEFAULT_COLOR
|
||||
};
|
||||
|
||||
data.name = songName;
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
|
||||
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
|
||||
data.setFieldDefault("displayName", data.name);
|
||||
if (isVariant) data.variant = variant;
|
||||
else data.variant = null;
|
||||
|
||||
defaultChartMetaFields(data);
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
var tempDiffs = [];
|
||||
var path = 'songs/$songName/charts/';
|
||||
if (isVariant) path += '$variant/';
|
||||
|
||||
data.difficulties = [for(f in Paths.getFolderContent(path, false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
if (data.difficulties.length == 3) {
|
||||
for(d in data.difficulties) {
|
||||
switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
var tempDiffs = [];
|
||||
for (d in data.difficulties) switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
data.difficulties = tempDiffs;
|
||||
if (tempDiffs.length == 3) data.difficulties = tempDiffs;
|
||||
}
|
||||
}
|
||||
|
||||
data.metas = [];
|
||||
if (includeMetaVariations && data.variants.length > 0) for (variant in data.variants) {
|
||||
if (!data.metas.exists(variant) && Assets.exists(Paths.file('songs/$songName/meta-$variant.json'))) {
|
||||
var meta = loadChartMeta(songName, variant, fromMods);
|
||||
if (meta.variant != null) data.metas.set(variant, meta);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function parse(songName:String, ?difficulty:String):ChartData {
|
||||
public static function parse(songName:String, ?difficulty:String, ?variant:String):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var chartPath = Paths.chart(songName, difficulty);
|
||||
var chartPath = Paths.chart(songName, difficulty, variant);
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
noteTypes: [],
|
||||
@@ -152,15 +182,15 @@ class Chart {
|
||||
fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS)
|
||||
};
|
||||
|
||||
var valid:Bool = true;
|
||||
var valid:Bool = true, namePrint = '$songName $difficulty' + ((variant != null && variant != '') ? ' ($variant)' : '');
|
||||
if (!Assets.exists(chartPath)) {
|
||||
Logs.error('Chart for song ${songName} ($difficulty) at "$chartPath" was not found.');
|
||||
Logs.error('Chart for song $namePrint at "$chartPath" was not found.');
|
||||
valid = false;
|
||||
}
|
||||
var data:Dynamic = null;
|
||||
if (valid) {
|
||||
try data = Json.parse(Assets.getText(chartPath))
|
||||
catch(e) Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
|
||||
catch(e) Logs.trace('Could not parse chart for song $namePrint: ${Std.string(e)}', ERROR, RED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,13 +222,12 @@ class Chart {
|
||||
}
|
||||
#end
|
||||
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
var loadedMeta = loadChartMeta(songName, variant, base.fromMods, false);
|
||||
if (base.meta == null) base.meta = loadedMeta;
|
||||
else {
|
||||
for (field in Reflect.fields(base.meta)) {
|
||||
var f = Reflect.field(base.meta, field);
|
||||
if (f != null)
|
||||
Reflect.setField(loadedMeta, field, f);
|
||||
if (f != null) Reflect.setField(loadedMeta, field, f);
|
||||
}
|
||||
base.meta = loadedMeta;
|
||||
}
|
||||
@@ -207,9 +236,8 @@ class Chart {
|
||||
* events.json LOADING
|
||||
*/
|
||||
#if REGION
|
||||
var extraEvents:Array<ChartEvent> = loadEventsJson(songName);
|
||||
if (extraEvents != null)
|
||||
base.events = base.events.concat(extraEvents);
|
||||
var extraEvents:Array<ChartEvent> = loadEventsJson(songName, variant);
|
||||
if (extraEvents != null) base.events = base.events.concat(extraEvents);
|
||||
#end
|
||||
|
||||
/**
|
||||
@@ -237,61 +265,59 @@ class Chart {
|
||||
|
||||
/**
|
||||
* Saves the chart to the specific song folder path.
|
||||
* @param songFolderPath Path to the song folder (ex: `mods/your mod/songs/song/`)
|
||||
* @param chart Chart to save
|
||||
* @param difficulty Name of the difficulty
|
||||
* @param saveSettings
|
||||
* @param chart Chart to save.
|
||||
* @param difficulty Name of the difficulty (Optional).
|
||||
* @param variant Name of the Variant (Optional).
|
||||
* @param saveSettings (Optional).
|
||||
* @return Filtered chart used for saving.
|
||||
*/
|
||||
public static function save(songFolderPath:String, chart:ChartData, ?difficulty:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
public static function save(chart:ChartData, ?difficulty:String, ?variant:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (saveSettings == null) saveSettings = {};
|
||||
|
||||
if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true;
|
||||
if (saveSettings.saveLocalEvents == null) saveSettings.saveLocalEvents = true;
|
||||
if (saveSettings.saveGlobalEvents == null) saveSettings.saveGlobalEvents = false;
|
||||
|
||||
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents);
|
||||
var meta = filteredChart.meta;
|
||||
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true);
|
||||
|
||||
#if sys
|
||||
var saveFolder:String = saveSettings.folder == null ? "charts" : saveSettings.folder;
|
||||
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "";
|
||||
var metaPath = 'meta$variantSuffix.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
|
||||
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
|
||||
songPath = temp.substr(0, temp.length - metaPath.length - 1);
|
||||
metaPath = temp;
|
||||
}
|
||||
else if (saveSettings.songFolder == null)
|
||||
metaPath = (songPath = '${Paths.getAssetsRoot()}/$songPath') + '/$metaPath';
|
||||
|
||||
if (!FileSystem.exists('${songFolderPath}/$saveFolder/'))
|
||||
FileSystem.createDirectory('${songFolderPath}/$saveFolder/');
|
||||
var chartFolder = saveSettings.folder == null ? ((variant == null || variant == '') ? 'charts' : 'charts/$variant') : saveSettings.folder;
|
||||
var chartPath = '$songPath/$chartFolder/${difficulty.trim()}.json';
|
||||
|
||||
var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json';
|
||||
var metaPath = '${songFolderPath}/meta.json';
|
||||
if (saveSettings.saveChart == null || saveSettings.saveChart == true)
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, prettyPrint));
|
||||
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
if (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath))
|
||||
CoolUtil.safeSaveFile(metaPath, Json.stringify(filterMetaForSaving(chart.meta), null, prettyPrint));
|
||||
|
||||
if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath))
|
||||
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta));
|
||||
if (saveSettings.seperateGlobalEvents == true) {
|
||||
var eventsPath = '$songPath/events$variantSuffix.json', events = filterEventsForSaving(chart.events, false, true);
|
||||
|
||||
if (events.length != 0) CoolUtil.safeSaveFile(eventsPath, Json.stringify({events: events}, null, prettyPrint));
|
||||
}
|
||||
#end
|
||||
|
||||
return filteredChart;
|
||||
}
|
||||
|
||||
public static function filterChartForSaving(chart:ChartData, ?saveMetaInChart:Bool, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
public static function filterChartForSaving(chart:ChartData, saveMetaInChart = true, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
var meta = chart.meta, events = chart.events;
|
||||
chart.meta = null;
|
||||
chart.events = null;
|
||||
|
||||
var data = Reflect.copy(chart); // make a copy of the chart to leave the OG intact
|
||||
if (saveMetaInChart != true) {
|
||||
data.meta = null;
|
||||
} else {
|
||||
data.meta = Reflect.copy(chart.meta); // also make a copy of the metadata to leave the OG intact.
|
||||
if (data.meta != null && Reflect.hasField(data.meta, "parsedColor")) Reflect.deleteField(data.meta, "parsedColor");
|
||||
}
|
||||
|
||||
// in this part abt the events, i gotta account that these booleans can be null - Nex
|
||||
if (saveLocalEvents != true && saveGlobalEvents != true) data.events = null;
|
||||
else {
|
||||
data.events = [];
|
||||
for (event in chart.events) if ((saveLocalEvents == true && event.global != true) || (saveGlobalEvents == true && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents == true ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.events.push(copy);
|
||||
}
|
||||
if (data.events.length == 0) data.events = null;
|
||||
}
|
||||
chart.meta = meta;
|
||||
chart.events = events;
|
||||
|
||||
data.meta = saveMetaInChart ? filterMetaForSaving(meta) : null;
|
||||
data.events = filterEventsForSaving(events, saveLocalEvents, saveGlobalEvents);
|
||||
data.fromMods = null;
|
||||
|
||||
var sortedData:Dynamic = {};
|
||||
@@ -303,18 +329,47 @@ class Chart {
|
||||
return sortedData;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String {
|
||||
public static function filterEventsForSaving(events:Array<ChartEvent>, saveLocalEvents = true, saveGlobalEvents = false):Array<ChartEvent> {
|
||||
var data = [];
|
||||
if (!saveLocalEvents && !saveGlobalEvents) return data;
|
||||
|
||||
for (event in events) if ((saveLocalEvents && event.global != true) || (saveGlobalEvents && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.push(copy);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String
|
||||
return Json.stringify(filterMetaForSaving(meta), null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
|
||||
public static inline function filterMetaForSaving(meta:ChartMetaData):ChartMetaData {
|
||||
var data:Dynamic = Reflect.copy(meta);
|
||||
if (data.color != null) data.color = new FlxColor(data.color).toWebString(); // dont even ask me - Nex
|
||||
return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
|
||||
Reflect.deleteField(data, "name");
|
||||
Reflect.deleteField(data, 'parsedColor');
|
||||
Reflect.deleteField(data, 'metas');
|
||||
Reflect.deleteField(data, "variant");
|
||||
if (data.instSuffix != null && data.instSuffix == "") Reflect.deleteField(data, "instSuffix");
|
||||
if (data.vocalsSuffix != null && data.vocalsSuffix == "") Reflect.deleteField(data, "vocalsSuffix");
|
||||
if (data.variants != null && data.variants.length == 0) Reflect.deleteField(data, "variants");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
typedef ChartSaveSettings = {
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveMetaInChart:Bool;
|
||||
var ?saveLocalEvents:Bool;
|
||||
var ?saveGlobalEvents:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveChart:Bool;
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?seperateGlobalEvents:Bool;
|
||||
|
||||
var ?folder:String;
|
||||
}
|
||||
var ?songFolder:String;
|
||||
}
|
||||
|
||||
@@ -18,16 +18,26 @@ typedef ChartData = {
|
||||
|
||||
typedef ChartMetaData = {
|
||||
public var name:String;
|
||||
public var ?bpm:Float;
|
||||
public var ?variant:String;
|
||||
public var ?displayName:String;
|
||||
|
||||
public var ?bpm:Float;
|
||||
public var ?beatsPerMeasure:Float;
|
||||
public var ?stepsPerBeat:Int;
|
||||
|
||||
public var ?difficulties:Array<String>;
|
||||
public var ?variants:Array<String>;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?icon:String;
|
||||
public var ?color:FlxColor;
|
||||
public var ?difficulties:Array<String>;
|
||||
|
||||
public var ?coopAllowed:Bool;
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?metas:Map<String, ChartMetaData>;
|
||||
public var ?instSuffix:String;
|
||||
public var ?vocalsSuffix:String;
|
||||
public var ?needsVoices:Bool;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ class VSliceParser {
|
||||
stage: Flags.DEFAULT_STAGE,
|
||||
codenameChart: true
|
||||
};
|
||||
parseChart(Reflect.field(chartData.notes, diff), metaData, chartData.events, base);
|
||||
parseChart(Reflect.field(chartData.notes, diff), metaData, chartData.events, base, resultMeta);
|
||||
resultCharts.push({diffName: diff, chart: base});
|
||||
}
|
||||
}
|
||||
|
||||
public static function parseChart(data:Dynamic, metaData:Dynamic, events:Dynamic, result:ChartData) {
|
||||
public static function parseChart(data:Dynamic, metaData:Dynamic, events:Dynamic, result:ChartData, resultMeta:ChartMetaData) {
|
||||
// vslice chart parsing
|
||||
var data:Array<SwagNote> = data;
|
||||
var metadata:SwagMetadata = metaData;
|
||||
@@ -45,14 +45,14 @@ class VSliceParser {
|
||||
type: 0,
|
||||
position: (p2isGF = metadata.playData.characters.opponent.startsWith("gf")) ? "girlfriend" : "dad",
|
||||
notes: [],
|
||||
vocalsSuffix: "-Opponent"
|
||||
vocalsSuffix: '-${metadata.playData.characters.opponent}${resultMeta.vocalsSuffix != null ? resultMeta.vocalsSuffix : ""}'
|
||||
});
|
||||
result.strumLines.push({
|
||||
characters: [metadata.playData.characters.player],
|
||||
type: 1,
|
||||
position: "boyfriend",
|
||||
notes: [],
|
||||
vocalsSuffix: "-Player"
|
||||
vocalsSuffix: '-${metadata.playData.characters.player}${resultMeta.vocalsSuffix != null ? resultMeta.vocalsSuffix : ""}'
|
||||
});
|
||||
var gfName = metadata.playData.characters.girlfriend;
|
||||
if (!p2isGF && gfName != "none") {
|
||||
@@ -67,7 +67,7 @@ class VSliceParser {
|
||||
|
||||
var timeChanges = metadata.timeChanges;
|
||||
result.meta.bpm = timeChanges[0].bpm;
|
||||
result.meta.needsVoices = true;
|
||||
result.meta.needsVoices = false;
|
||||
|
||||
for (note in data)
|
||||
{
|
||||
@@ -108,7 +108,7 @@ class VSliceParser {
|
||||
{
|
||||
case "FocusCamera":
|
||||
var isPosOnly = false;
|
||||
var arr:Array<Dynamic> = [switch(values.char) {
|
||||
var arr:Array<Dynamic> = [switch(values is Int ? values : values.char) {
|
||||
// more cases here in the future if they add em? (i hope not) - Nex
|
||||
case 0: 1;
|
||||
case 1: 0;
|
||||
@@ -177,16 +177,12 @@ class VSliceParser {
|
||||
var songID:String = songID == null ? songName.toLowerCase().replace(" ", "-") : songID;
|
||||
var firstTimeChange:SwagTimeChange = data.timeChanges[0];
|
||||
|
||||
result.name = songID;
|
||||
result.bpm = firstTimeChange.bpm.getDefault(Flags.DEFAULT_BPM);
|
||||
result.beatsPerMeasure = firstTimeChange.n.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
result.stepsPerBeat = firstTimeChange.d.getDefault(Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
result.displayName = songName;
|
||||
result.icon = Flags.DEFAULT_HEALTH_ICON;
|
||||
result.color = 0xFFFFFF;
|
||||
result.opponentModeAllowed = true;
|
||||
result.coopAllowed = true;
|
||||
result.difficulties = data.playData.difficulties.concat(data.playData.songVariations.getDefault([]));
|
||||
result.difficulties = data.playData.difficulties.copy();
|
||||
if (data.playData.songVariations != null) result.variants = data.playData.songVariations.copy();
|
||||
|
||||
if (result.customValues == null) result.customValues = {};
|
||||
result.customValues.artist = data.artist;
|
||||
|
||||
@@ -9,6 +9,10 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||
* Difficulty name
|
||||
*/
|
||||
public var difficulty:String;
|
||||
/**
|
||||
* Variation Name
|
||||
*/
|
||||
public var variant:String;
|
||||
/**
|
||||
* Whenever opponent mode is enabled or not.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ enum Control
|
||||
ACCEPT;
|
||||
BACK;
|
||||
PAUSE;
|
||||
CHANGE_MODE;
|
||||
//CHEAT;
|
||||
SWITCHMOD;
|
||||
|
||||
@@ -145,6 +146,11 @@ class Controls extends FlxActionSet
|
||||
@:pressed("reset") public var RESET_HOLD(get, set): Bool;
|
||||
@:justReleased("reset") public var RESET_R(get, set): Bool;
|
||||
|
||||
@:gamepad([FlxGamepadInputID.BACK]) // select button
|
||||
@:justPressed("change-mode") public var CHANGE_MODE(get, set): Bool;
|
||||
@:pressed("change-mode") public var CHANGE_MODE_HOLD(get, set): Bool;
|
||||
@:justReleased("change-mode") public var CHANGE_MODE_R(get, set): Bool;
|
||||
|
||||
//@:gamepad([])
|
||||
//@:justPressed("cheat") public var CHEAT(get, set): Bool;
|
||||
//@:pressed("cheat") public var CHEAT_HOLD(get, set): Bool;
|
||||
|
||||
@@ -127,6 +127,9 @@ class Flags {
|
||||
|
||||
public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500;
|
||||
public static var DEFAULT_NOTE_SCALE:Float = 0.7;
|
||||
#if MODCHARTING_FEATURES
|
||||
public static var DEFAULT_MODCHART_HOLD_SUBDIVISIONS:Int = 4;
|
||||
#end
|
||||
|
||||
@:also(funkin.game.Character.FALLBACK_DEAD_CHARACTER)
|
||||
public static var DEFAULT_GAMEOVER_CHARACTER:String = "bf-dead";
|
||||
@@ -196,6 +199,7 @@ class Flags {
|
||||
public static var DISABLE_WARNING_SCREEN:Bool = true;
|
||||
public static var DISABLE_TRANSITIONS:Bool = false;
|
||||
public static var DISABLE_LANGUAGES:Bool = false;
|
||||
public static var DISABLE_AUTOUPDATER:Bool = false;
|
||||
|
||||
@:also(funkin.backend.MusicBeatTransition.script)
|
||||
public static var DEFAULT_TRANSITION_SCRIPT:String = "";
|
||||
@@ -268,6 +272,8 @@ class Flags {
|
||||
@:bypass public static var customFlags:Map<String, String> = [];
|
||||
|
||||
public static function loadFromData(flags:Map<String, String>, data:String) {
|
||||
WINDOW_TITLE_USE_MOD_NAME = false;
|
||||
|
||||
if (!(data.length > 0)) return;
|
||||
var res = IniUtil.parseString(data);
|
||||
|
||||
@@ -285,7 +291,10 @@ class Flags {
|
||||
}
|
||||
}
|
||||
|
||||
if (!flags.exists("WINDOW_TITLE_USE_MOD_NAME")) flags.set("WINDOW_TITLE_USE_MOD_NAME", flags.exists('TITLE') ? 'false' : 'true');
|
||||
if (!flags.exists("WINDOW_TITLE_USE_MOD_NAME")) WINDOW_TITLE_USE_MOD_NAME = !flags.exists('TITLE') && flags.exists('MOD_NAME');
|
||||
else WINDOW_TITLE_USE_MOD_NAME = parseBool(flags.get("WINDOW_TITLE_USE_MOD_NAME"));
|
||||
|
||||
flags.remove("WINDOW_TITLE_USE_MOD_NAME");
|
||||
}
|
||||
|
||||
public static function loadFromDatas(datas:Array<String>) {
|
||||
@@ -301,6 +310,8 @@ class Flags {
|
||||
for(name=>value in flags)
|
||||
if(!parse(name, value))
|
||||
customFlags.set(name, value);
|
||||
|
||||
Options.modchartingHoldSubdivisions = DEFAULT_MODCHART_HOLD_SUBDIVISIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,13 +12,13 @@ import haxe.Exception;
|
||||
* Requires the `GITHUB_API` macro to be defined.
|
||||
* This has no authentication, so it's limited to unauthenticated requests, and rate limits.
|
||||
**/
|
||||
class GitHub {
|
||||
final class GitHub {
|
||||
/**
|
||||
* Gets all the releases from a specific GitHub repository using the GitHub API.
|
||||
* @param user The user/organization that owns the repository
|
||||
* @param repository The repository name
|
||||
* @param onError Error Callback
|
||||
* @return Releases
|
||||
* @param user The user/organization that owns the repository.
|
||||
* @param repository The repository name.
|
||||
* @param onError Error Callback.
|
||||
* @return Releases in Array.
|
||||
*/
|
||||
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
|
||||
#if GITHUB_API
|
||||
@@ -38,17 +38,16 @@ class GitHub {
|
||||
|
||||
/**
|
||||
* Gets the contributors list from a specific GitHub repository using the GitHub API.
|
||||
* @param user The user/organization that owns the repository
|
||||
* @param repository The repository name
|
||||
* @param onError Error Callback
|
||||
* @return Contributors List
|
||||
* @param user The user/organization that owns the repository.
|
||||
* @param repository The repository name.
|
||||
* @param onError Error Callback.
|
||||
* @return Contributors List as Array.
|
||||
*/
|
||||
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors'));
|
||||
if (!(data is Array))
|
||||
throw __parseGitHubException(data);
|
||||
if (!(data is Array)) throw __parseGitHubException(data);
|
||||
|
||||
return data;
|
||||
} catch(e) {
|
||||
@@ -61,16 +60,15 @@ class GitHub {
|
||||
|
||||
/**
|
||||
* Gets a specific GitHub organization using the GitHub API.
|
||||
* @param org The organization to get
|
||||
* @param onError Error Callback
|
||||
* @return Organization
|
||||
* @param org The organization to get.
|
||||
* @param onError Error Callback.
|
||||
* @return Organization.
|
||||
*/
|
||||
public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org'));
|
||||
if (Reflect.hasField(data, "documentation_url"))
|
||||
throw __parseGitHubException(data);
|
||||
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
|
||||
|
||||
return data;
|
||||
} catch(e) {
|
||||
@@ -84,16 +82,15 @@ class GitHub {
|
||||
/**
|
||||
* Gets the members list from a specific GitHub organization using the GitHub API.
|
||||
* NOTE: Members use Contributors' structure!
|
||||
* @param org The organization to get the members from
|
||||
* @param onError Error Callback
|
||||
* @return Members List
|
||||
* @param org The organization to get the members from.
|
||||
* @param onError Error Callback.
|
||||
* @return Members List as Array
|
||||
*/
|
||||
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
|
||||
if (Reflect.hasField(data, "documentation_url"))
|
||||
throw __parseGitHubException(data);
|
||||
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
|
||||
|
||||
return data;
|
||||
} catch(e) {
|
||||
@@ -107,18 +104,15 @@ class GitHub {
|
||||
/**
|
||||
* Gets a specific GitHub user/organization using the GitHub API.
|
||||
* NOTE: If organization, it will be returned with the structure of a normal user; use `getOrganization` if you specifically want an organization!
|
||||
* @param user The user/organization to get
|
||||
* @param onError Error Callback
|
||||
* @return User/Organization
|
||||
* @param user The user/organization to get.
|
||||
* @param onError Error Callback.
|
||||
* @return User/Organization.
|
||||
*/
|
||||
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
|
||||
#if GITHUB_API
|
||||
try {
|
||||
var url = 'https://api.github.com/users/$user';
|
||||
|
||||
var data = Json.parse(HttpUtil.requestText(url));
|
||||
if (Reflect.hasField(data, "documentation_url"))
|
||||
throw __parseGitHubException(data);
|
||||
var data = Json.parse(HttpUtil.requestText('https://api.github.com/users/$user'));
|
||||
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
|
||||
|
||||
return data;
|
||||
} catch(e) {
|
||||
|
||||
@@ -33,11 +33,9 @@ class FlagMacro {
|
||||
resetExprs.push(macro $i{"customFlags"} = $v{[]});
|
||||
|
||||
for (field in fields) {
|
||||
var skip = field.meta.hasMeta(":bypass");
|
||||
if (field.meta.hasMeta(":bypass")) continue;
|
||||
var hasLazy = field.meta.hasMeta(":lazy");
|
||||
|
||||
if (skip) continue;
|
||||
|
||||
switch (field.kind) {
|
||||
case FVar(type, expr):
|
||||
var isNullable = false;
|
||||
|
||||
@@ -43,7 +43,6 @@ class FunkinSaveMacro {
|
||||
* SAVE FUNCTION
|
||||
*/
|
||||
var saveFuncBlocks:Array<Expr> = [for(f in fieldNames) macro $i{saveFieldName}.data.$f = $i{f}];
|
||||
|
||||
saveFuncBlocks.push(macro $i{saveFieldName}.flush());
|
||||
|
||||
fields.push({
|
||||
@@ -59,6 +58,8 @@ class FunkinSaveMacro {
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
var loadFuncBlocks:Array<Expr> = [for(f in fieldNames) macro if ($i{saveFieldName}.data.$f != null) $i{f} = $i{saveFieldName}.data.$f];
|
||||
|
||||
/**
|
||||
* LOAD FUNCTION
|
||||
*/
|
||||
@@ -69,10 +70,7 @@ class FunkinSaveMacro {
|
||||
args: [],
|
||||
expr: {
|
||||
pos: Context.currentPos(),
|
||||
expr: EBlock([for(f in fieldNames)
|
||||
macro if ($i{saveFieldName}.data.$f != null)
|
||||
$i{f} = $i{saveFieldName}.data.$f
|
||||
])
|
||||
expr: EBlock(loadFuncBlocks)
|
||||
}
|
||||
}),
|
||||
access: [APublic, AStatic]
|
||||
|
||||
@@ -24,7 +24,11 @@ class UpdateAvailableScreen extends MusicBeatState {
|
||||
|
||||
public function new(check:UpdateCheckCallback) {
|
||||
super();
|
||||
|
||||
this.check = check;
|
||||
if (Date.now().getTime() - check.date.getTime() > 15000) {
|
||||
if ((check = UpdateUtil.checkForUpdates(true)).newUpdate) this.check = check;
|
||||
}
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
|
||||
@@ -2,60 +2,97 @@ package funkin.backend.system.updating;
|
||||
|
||||
import funkin.backend.system.github.GitHub;
|
||||
import funkin.backend.system.github.GitHubRelease;
|
||||
import haxe.io.Path;
|
||||
|
||||
import lime.app.Application;
|
||||
|
||||
import sys.thread.Mutex;
|
||||
import sys.thread.Thread;
|
||||
import sys.FileSystem;
|
||||
|
||||
import haxe.io.Path;
|
||||
|
||||
using funkin.backend.system.github.GitHub;
|
||||
|
||||
class UpdateUtil {
|
||||
public static var lastUpdateCheck:Null<UpdateCheckCallback>;
|
||||
|
||||
private static var __waitCallbacks:Array<UpdateCheckCallback->Void>;
|
||||
private static var __mutex:Mutex;
|
||||
|
||||
public static function init() {
|
||||
// deletes old bak file if it exists
|
||||
#if sys
|
||||
var bakPath = '${Path.withoutExtension(Sys.programPath())}.bak';
|
||||
if (FileSystem.exists(bakPath))
|
||||
FileSystem.deleteFile(bakPath);
|
||||
if (FileSystem.exists(bakPath)) FileSystem.deleteFile(bakPath);
|
||||
#end
|
||||
|
||||
__waitCallbacks = [];
|
||||
__mutex = new Mutex();
|
||||
Thread.create(checkForUpdates.bind(true, false));
|
||||
}
|
||||
|
||||
public static function checkForUpdates():UpdateCheckCallback {
|
||||
var curTag = 'v${Flags.VERSION}';
|
||||
trace("Current version: " + curTag);
|
||||
public static function waitForUpdates(force = false, callback:UpdateCheckCallback->Void, lazy = false) {
|
||||
if (__mutex.tryAcquire()) {
|
||||
__mutex.release();
|
||||
if (__shouldCheck(lazy) || force) {
|
||||
__waitCallbacks.push(callback);
|
||||
Thread.create(checkForUpdates.bind(force, false));
|
||||
}
|
||||
else
|
||||
callback(lastUpdateCheck);
|
||||
}
|
||||
else
|
||||
__waitCallbacks.push(callback);
|
||||
}
|
||||
|
||||
var error = false;
|
||||
public static function checkForUpdates(force = false, lazy = false):UpdateCheckCallback {
|
||||
var wasAcquired = !__mutex.tryAcquire();
|
||||
if (wasAcquired) __mutex.acquire();
|
||||
|
||||
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
|
||||
if ((!force || wasAcquired) && !__shouldCheck(lazy)) {
|
||||
__mutex.release();
|
||||
return lastUpdateCheck;
|
||||
}
|
||||
|
||||
lastUpdateCheck = __checkForUpdates();
|
||||
__mutex.release();
|
||||
|
||||
FlxG.signals.preUpdate.addOnce(__callWaitCallbacks);
|
||||
|
||||
return lastUpdateCheck;
|
||||
}
|
||||
|
||||
static function __checkForUpdates():UpdateCheckCallback {
|
||||
var curTag = 'v' + (Flags.VERSION == null ? Application.current.meta.get('version') : Flags.VERSION), error = false;
|
||||
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, (e) -> {
|
||||
error = true;
|
||||
}), curTag);
|
||||
|
||||
if (error) return {
|
||||
success: false,
|
||||
newUpdate: false
|
||||
};
|
||||
|
||||
if (newUpdates.length <= 0) {
|
||||
return {
|
||||
success: true,
|
||||
newUpdate: false
|
||||
};
|
||||
var updateCheck:UpdateCheckCallback = {
|
||||
success: !error,
|
||||
newUpdate: !error && newUpdates.length > 0,
|
||||
currentVersionTag: curTag,
|
||||
date: Date.now()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
newUpdate: true,
|
||||
currentVersionTag: curTag,
|
||||
newVersionTag: newUpdates.last().tag_name,
|
||||
updates: newUpdates
|
||||
};
|
||||
if (updateCheck.newUpdate) updateCheck.newVersionTag = (updateCheck.updates = newUpdates).last().tag_name;
|
||||
return updateCheck;
|
||||
}
|
||||
|
||||
static function __shouldCheck(lazy:Bool):Bool
|
||||
return lastUpdateCheck == null || !lazy && (!lastUpdateCheck.newUpdate || Date.now().getTime() - lastUpdateCheck.date.getTime() > 1800000);
|
||||
|
||||
static function __callWaitCallbacks() {
|
||||
for (callback in __waitCallbacks) callback(lastUpdateCheck);
|
||||
__waitCallbacks.resize(0);
|
||||
}
|
||||
|
||||
static var __curVersionPos = -2;
|
||||
static function __doReleaseFiltering(releases:Array<GitHubRelease>, currentVersionTag:String) {
|
||||
releases = releases.filterReleases(Options.betaUpdates, false);
|
||||
if (releases.length <= 0)
|
||||
return releases;
|
||||
|
||||
var newArray:Array<GitHubRelease> = [];
|
||||
var newArray:Array<GitHubRelease> = [], __curVersionPos = -2;
|
||||
|
||||
var skipNextBinaryChecks:Bool = false;
|
||||
for(index in 0...releases.length) {
|
||||
@@ -73,12 +110,9 @@ class UpdateUtil {
|
||||
}
|
||||
if (containsBinary) {
|
||||
skipNextBinaryChecks = true; // no need to check for older versions
|
||||
if (release.tag_name == currentVersionTag) {
|
||||
__curVersionPos = -1;
|
||||
}
|
||||
if (release.tag_name == currentVersionTag) __curVersionPos = -1;
|
||||
newArray.insert(0, release);
|
||||
if (__curVersionPos > -2)
|
||||
__curVersionPos++;
|
||||
if (__curVersionPos > -2) __curVersionPos++;
|
||||
}
|
||||
}
|
||||
if (__curVersionPos < -1)
|
||||
@@ -98,4 +132,6 @@ typedef UpdateCheckCallback = {
|
||||
@:optional var newVersionTag:String;
|
||||
|
||||
@:optional var updates:Array<GitHubRelease>;
|
||||
|
||||
@:optional var date:Date;
|
||||
}
|
||||
@@ -474,7 +474,7 @@ final class AudioAnalyzer {
|
||||
var n = Math.floor((endPos - startPos) * __toBits);
|
||||
if (startPos >= time && startPos < backend.bufferTimes[backend.bufferSizes.length - 1] * 1000) {
|
||||
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferSizes[i], c = 0;
|
||||
while (pos > size) {
|
||||
while (pos >= size) {
|
||||
if (++i >= backend.bufferSizes.length) {
|
||||
n = 0;
|
||||
break;
|
||||
@@ -484,6 +484,7 @@ final class AudioAnalyzer {
|
||||
size = backend.bufferSizes[i];
|
||||
}
|
||||
pos -= pos % __sampleSize;
|
||||
n -= pos % __sampleSize;
|
||||
|
||||
while (n > 0) {
|
||||
callback(getByte(buf, pos, __wordSize), c);
|
||||
|
||||
@@ -38,6 +38,6 @@ class FlxInterpolateColor {
|
||||
alpha = (color.alphaFloat - alpha) * ratio + alpha;
|
||||
}
|
||||
|
||||
public inline function fpsLerpTo(color:FlxColor, ratio:Float)
|
||||
lerpTo(color, ratio * 60 * FlxG.elapsed);
|
||||
public inline function fpsLerpTo(color:FlxColor, ratio:Float, ?delta:Float)
|
||||
lerpTo(color, CoolUtil.getFPSRatio(ratio, delta));
|
||||
}
|
||||
@@ -29,9 +29,6 @@ final class WindowUtils {
|
||||
public static inline function resetClosing() __triedClosing = false;
|
||||
|
||||
@:dox(hide) public static inline function init() {
|
||||
resetTitle();
|
||||
resetClosing();
|
||||
|
||||
Lib.application.window.onClose.add(function () {
|
||||
if (preventClosing && !__triedClosing) {
|
||||
Lib.application.window.onClose.cancel();
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef WeekCharacter = {
|
||||
|
||||
typedef WeekSong = {
|
||||
var name:String;
|
||||
var hide:Bool;
|
||||
var ?hide:Bool;
|
||||
var ?variation:String;
|
||||
var ?displayName:String;
|
||||
}
|
||||
@@ -56,9 +56,9 @@ LOGO_TEXT=""
|
||||
super.createPost();
|
||||
hadPopup = true;
|
||||
|
||||
var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [
|
||||
var substate = new UIWarningSubstate(TU.translate("modConfigWarning.warningTitle"), TU.translate("modConfigWarning.warningDesc"), [
|
||||
{
|
||||
label: "Not Now",
|
||||
label: TU.translate("editor.notNow"),
|
||||
color: 0x969533,
|
||||
onClick: function (_) {
|
||||
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
|
||||
@@ -66,13 +66,13 @@ LOGO_TEXT=""
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Yes",
|
||||
label: TU.translate("editor.yes"),
|
||||
onClick: function(_) {
|
||||
var path = '${library.folderPath}/data/config/modpack.ini';
|
||||
CoolUtil.safeSaveFile(path, defaultModConfigText);
|
||||
openSubState(new UIWarningSubstate("Created mod config!", "Your mod config file has been created at " + path + "!", [
|
||||
openSubState(new UIWarningSubstate(TU.translate("modConfigWarning.createdTitle"), TU.translate("modConfigWarning.createdDesc", [path]), [
|
||||
{
|
||||
label: "Ok",
|
||||
label: TU.translate("editor.ok"),
|
||||
onClick: function (_) {
|
||||
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
|
||||
FlxG.switchState(cast Type.createInstance(goToState, []));
|
||||
|
||||
@@ -209,6 +209,9 @@ class AlphabetEditor extends UIState {
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
|
||||
hasColorMode: false,
|
||||
colorMode: bigLetter.colorMode
|
||||
};
|
||||
|
||||
@@ -204,6 +204,9 @@ UPPERLETTER and LOWERLETTTER can be used in any backup prefix.";
|
||||
cos: 1.0,
|
||||
sin: 0.0,
|
||||
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
|
||||
hasColorMode: false,
|
||||
colorMode: 0
|
||||
}],
|
||||
|
||||
@@ -11,6 +11,9 @@ class GlyphInfoWindow extends UIWindow {
|
||||
public var angleBox:UINumericStepper;
|
||||
public var colorModeDrop:UIDropDown;
|
||||
|
||||
public var flipXBox:UICheckbox;
|
||||
public var flipYBox:UICheckbox;
|
||||
|
||||
public var outlineTitle:UIText;
|
||||
public var outlineSep:UISprite;
|
||||
public var outlineCheck:UICheckbox;
|
||||
@@ -145,6 +148,9 @@ class GlyphInfoWindow extends UIWindow {
|
||||
cos: compon.cos,
|
||||
sin: compon.sin,
|
||||
|
||||
flipX: compon.flipX,
|
||||
flipY: compon.flipY,
|
||||
|
||||
hasColorMode: compon.hasColorMode,
|
||||
colorMode: compon.colorMode
|
||||
};
|
||||
@@ -159,6 +165,22 @@ class GlyphInfoWindow extends UIWindow {
|
||||
}
|
||||
members.push(outlineCheck);
|
||||
|
||||
flipXBox = new UICheckbox(outlineCheck.x, outlineCheck.y - outlineCheck.height - 8, "Flip X?");
|
||||
flipXBox.onChecked = function(check:Bool) {
|
||||
compon.flipX = check;
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].flipX = check;
|
||||
}
|
||||
members.push(flipXBox);
|
||||
|
||||
flipYBox = new UICheckbox(flipXBox.x + flipXBox.width + itemMargin * 5, flipXBox.y, "Flip Y?");
|
||||
flipYBox.onChecked = function(check:Bool) {
|
||||
compon.flipY = check;
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].flipY = check;
|
||||
}
|
||||
members.push(flipYBox);
|
||||
|
||||
outlineTitle = new UIText(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin, 0, "Outline Data");
|
||||
members.push(outlineTitle);
|
||||
outlineSep = new UISprite(x + itemMargin, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + 19);
|
||||
@@ -239,6 +261,8 @@ class GlyphInfoWindow extends UIWindow {
|
||||
angleBox.value = com.angle;
|
||||
@:privateAccess colorModeDrop.index = com.hasColorMode ? com.colorMode : AlphabetEditor.instance.tape.colorMode;
|
||||
@:privateAccess colorModeDrop.label.text = colorModeDrop.options[colorModeDrop.index];
|
||||
flipXBox.checked = com.flipX;
|
||||
flipYBox.checked = com.flipY;
|
||||
|
||||
outlineCheck.checked = com.outIndex != null;
|
||||
outlineBox.label.text = "";
|
||||
|
||||
@@ -28,6 +28,7 @@ import flixel.util.FlxColor;
|
||||
class Charter extends UIState {
|
||||
public static var __song:String;
|
||||
static var __diff:String;
|
||||
static var __variant:String;
|
||||
static var __reload:Bool;
|
||||
|
||||
var chart(get, never):ChartData;
|
||||
@@ -114,11 +115,12 @@ class Charter extends UIState {
|
||||
private var SONGPOSINFO_BPM = TU.getRaw("songPosInfo.bpm");
|
||||
private var SONGPOSINFO_TIMESIGNATURE = TU.getRaw("songPosInfo.timeSignature");
|
||||
|
||||
public function new(song:String, diff:String, reload:Bool = true) {
|
||||
public function new(song:String, diff:String, variant:String, reload:Bool = true) {
|
||||
super();
|
||||
if (song != null) {
|
||||
__song = song;
|
||||
__diff = diff;
|
||||
__variant = variant;
|
||||
__reload = reload;
|
||||
}
|
||||
}
|
||||
@@ -578,7 +580,7 @@ class Charter extends UIState {
|
||||
dataDisplay.screenCenter(Y);
|
||||
dataDisplay.cameras = [charterCamera]; dataDisplay.x = -dataDisplay.width; add(dataDisplay);*/
|
||||
|
||||
DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")"]);
|
||||
DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")" + (__variant != null && __variant != "" ? " (" + __variant + ")" : "")]);
|
||||
}
|
||||
|
||||
override function destroy() {
|
||||
@@ -595,29 +597,32 @@ class Charter extends UIState {
|
||||
public function loadSong() {
|
||||
if (__reload) {
|
||||
EventsData.reloadEvents();
|
||||
PlayState.loadSong(__song, __diff, false, false);
|
||||
PlayState.loadSong(__song, __diff, __variant, false, false);
|
||||
__resetStatics();
|
||||
}
|
||||
|
||||
Conductor.setupSong(PlayState.SONG);
|
||||
noteTypes = PlayState.SONG.noteTypes;
|
||||
|
||||
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff)));
|
||||
if (PlayState.SONG.meta.needsVoices != false && Assets.exists(Paths.voices(__song, __diff))) // null or true
|
||||
vocals = FlxG.sound.load(Paths.voices(__song, __diff));
|
||||
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
|
||||
if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix)))
|
||||
vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix));
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
|
||||
vocals.muted = !PlayState.SONG.meta.needsVoices;
|
||||
vocals.group = FlxG.sound.defaultMusicGroup;
|
||||
|
||||
gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
|
||||
|
||||
for(strL in PlayState.SONG.strumLines)
|
||||
var noteCount:Int = 0;
|
||||
for (strL in PlayState.SONG.strumLines) {
|
||||
createStrumline(strumLines.members.length, strL, false, false);
|
||||
noteCount += strL.notes.length;
|
||||
}
|
||||
|
||||
// create notes
|
||||
notesGroup.autoSort = false;
|
||||
var noteCount:Int = 0;
|
||||
for (strL in PlayState.SONG.strumLines)
|
||||
noteCount += strL.notes.length;
|
||||
notesGroup.preallocate(noteCount);
|
||||
|
||||
var notesCreated:Int = 0;
|
||||
@@ -681,10 +686,10 @@ class Charter extends UIState {
|
||||
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [];
|
||||
|
||||
if (FlxG.sound.music.loaded)
|
||||
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
|
||||
wavesToGenerate.push({name: 'Inst${PlayState.SONG.meta.instSuffix}.ogg', sound: FlxG.sound.music});
|
||||
|
||||
if (PlayState.SONG.meta.needsVoices != false && vocals.loaded)
|
||||
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
|
||||
if (vocals.loaded)
|
||||
wavesToGenerate.push({name: 'Voices${PlayState.SONG.meta.vocalsSuffix}.ogg', sound: vocals});
|
||||
|
||||
for (strumLine in strumLines)
|
||||
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && strumLine.vocals.loaded)
|
||||
@@ -786,12 +791,13 @@ class Charter extends UIState {
|
||||
autoSaveTimer = Options.charterAutoSaveTime;
|
||||
if (!autoSaveNotif.cancelled) {
|
||||
buildChart();
|
||||
var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}';
|
||||
|
||||
if (Options.charterAutoSavesSeparateFolder)
|
||||
Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorCharterPrettyPrint});
|
||||
else // These two chart saves are particular, to avoid any kind of loss, stuff like meta, global and local events will be save all together - Nex
|
||||
Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, folder: 'autosaves', prettyPrint: Options.editorCharterPrettyPrint});
|
||||
else
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
undos.save();
|
||||
}
|
||||
autoSaveNotif.cancelled = false;
|
||||
@@ -1204,7 +1210,9 @@ class Charter extends UIState {
|
||||
|
||||
var strL = strumLines.members[strumLineID].strumLine;
|
||||
strumLines.members[strumLineID].destroy();
|
||||
strumLines.members.remove(strumLines.members[strumLineID]);
|
||||
strumLines.members[strumLineID] = strumLines.members[strumLines.members.length - 1];
|
||||
strumLines.members.pop();
|
||||
@:privateAccess strumLines.length--;
|
||||
strumLines.refreshStrumlineIDs();
|
||||
strumLines.snapStrums();
|
||||
|
||||
@@ -1474,8 +1482,8 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveChart(shouldBuild:Bool = true, withEvents:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}', !withEvents, shouldBuild);
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: withEvents, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
if (undos != null) undos.save();
|
||||
#else
|
||||
saveChartAs(shouldBuild, withEvents);
|
||||
@@ -1491,13 +1499,9 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveEvents(shouldBuild:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
|
||||
|
||||
var path = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json';
|
||||
if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null));
|
||||
else if (FileSystem.exists(path)) FileSystem.deleteFile(path); // Instead of replacing with a useless empty file, deletes the file directly - Nex
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
if (undos != null) undos.save();
|
||||
#else
|
||||
saveEventsAs(shouldBuild);
|
||||
#end
|
||||
@@ -1505,7 +1509,7 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveEventsAs(shouldBuild:Bool = true) {
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
|
||||
var data = {events: Chart.filterEventsForSaving(PlayState.SONG.events, false, true)};
|
||||
|
||||
saveAs(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
|
||||
defaultSaveFile: 'events.json'
|
||||
@@ -1514,12 +1518,8 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveMeta(shouldBuild:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json',
|
||||
Json.stringify(PlayState.SONG.meta == null ? {} : Chart.filterChartForSaving(PlayState.SONG, true, false, false).meta, null, Flags.JSON_PRETTY_PRINT)
|
||||
);
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, overrideExistingMeta: true, prettyPrint: true});
|
||||
#else
|
||||
saveMetaAs(shouldBuild);
|
||||
#end
|
||||
@@ -1543,9 +1543,7 @@ class Charter extends UIState {
|
||||
}, null, shouldBuild);
|
||||
}
|
||||
|
||||
public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map<String, Bool>, shouldBuild:Bool = true) {
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map<String, Bool>, shouldBuild:Bool = true) { if (shouldBuild && instance != null) instance.buildChart();
|
||||
var cur = FlxG.state;
|
||||
while(true) {
|
||||
if (instance != null || cur.subState == null) return cur.openSubState(new SaveSubstate(Json.stringify(data, replacer, space), options, saveOptions));
|
||||
@@ -1556,7 +1554,7 @@ class Charter extends UIState {
|
||||
#if sys
|
||||
public static function saveTo(path:String, separateEvents:Bool = false, shouldBuild:Bool = true) {
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: !separateEvents, songFolder: path, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
}
|
||||
#end
|
||||
#end
|
||||
@@ -1568,17 +1566,17 @@ class Charter extends UIState {
|
||||
else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();}
|
||||
}
|
||||
|
||||
function _file_save_all(_) saveEverything();
|
||||
function _file_save(_) saveChart();
|
||||
function _file_saveas(_) saveChartAs();
|
||||
function _file_events_save(_) saveEvents();
|
||||
function _file_events_saveas(_) saveEventsAs();
|
||||
function _file_save_no_events(_) saveChart(true, false);
|
||||
function _file_saveas_no_events(_) saveChartAs(true, false);
|
||||
function _file_meta_save(_) saveMeta();
|
||||
function _file_meta_saveas(_) saveMetaAs();
|
||||
function _file_saveas_fnflegacy(_) saveLegacyChartAs();
|
||||
function _file_saveas_psych(_) savePsychChartAs();
|
||||
function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
|
||||
function _edit_copy(_, playSFX=true) {
|
||||
if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
|
||||
|
||||
@@ -23,6 +23,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
public var iconSprite:HealthIcon;
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var needsVoicesCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
|
||||
@@ -68,6 +69,9 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54);
|
||||
add(denominatorStepper);
|
||||
|
||||
needsVoicesCheckbox = new UICheckbox(beatsPerMeasureStepper.x + 100, beatsPerMeasureStepper.y + 6, translate("needsVoices"), metadata.needsVoices);
|
||||
add(needsVoicesCheckbox);
|
||||
|
||||
add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, translate("menusData"), 28));
|
||||
|
||||
displayNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.displayName);
|
||||
@@ -146,20 +150,23 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
Reflect.setProperty(customVals, vals.propertyText.label.text, vals.valueText.label.text);
|
||||
}
|
||||
|
||||
PlayState.SONG.meta = {
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(16 / denominatorStepper.value),
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
customValues: customVals,
|
||||
};
|
||||
var meta = PlayState.SONG.meta;
|
||||
if (meta == null) meta = {name: songNameTextBox.label.text};
|
||||
else meta.name = songNameTextBox.label.text;
|
||||
|
||||
meta.bpm = bpmStepper.value;
|
||||
meta.needsVoices = needsVoicesCheckbox.checked;
|
||||
meta.beatsPerMeasure = Std.int(beatsPerMeasureStepper.value);
|
||||
meta.stepsPerBeat = Std.int(16 / denominatorStepper.value);
|
||||
meta.displayName = displayNameTextBox.label.text;
|
||||
meta.icon = iconTextBox.label.text;
|
||||
meta.color = colorWheel.curColor;
|
||||
meta.opponentModeAllowed = opponentModeCheckbox.checked;
|
||||
meta.coopAllowed = coopAllowedCheckbox.checked;
|
||||
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
|
||||
meta.customValues = customVals;
|
||||
|
||||
Charter.instance.updateBPMEvents();
|
||||
Charter.instance.vocals.muted = !needsVoicesCheckbox.checked;
|
||||
}
|
||||
}
|
||||
@@ -25,55 +25,72 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
public var songList:Array<String> = [];
|
||||
public var curSong:ChartMetaData;
|
||||
|
||||
inline public function makeChartOption(d:String, name:String):TextOption {
|
||||
return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d)));
|
||||
inline public function makeChartOption(d:String, v:String, name:String):TextOption {
|
||||
return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d, v)));
|
||||
}
|
||||
|
||||
inline public function makeVariationOption(s:ChartMetaData):TextOption {
|
||||
return new TextOption(s.variant, getID('acceptVariation'), " >", () -> openSongOption(s, false));
|
||||
}
|
||||
|
||||
public function openSongOption(s:ChartMetaData, first = true) {
|
||||
curSong = s;
|
||||
|
||||
var isVariant = s.variant != null && s.variant != '';
|
||||
var screen = new EditorTreeMenuScreen((first || !isVariant) ? (s.name + (isVariant ? ' (${s.variant})' : '')) : s.variant, getID('selectDifficulty'));
|
||||
|
||||
for (d in s.difficulties) if (d != '') screen.add(makeChartOption(d, isVariant ? s.variant : null, s.name));
|
||||
screen.add(new Separator());
|
||||
for (v in s.variants) if (s.metas.get(v) != null) screen.add(makeVariationOption(s.metas.get(v)));
|
||||
|
||||
#if sys
|
||||
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
|
||||
parent.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
|
||||
if (!first) screen.curSelected = 1;
|
||||
else {
|
||||
cast(screen.members[0], NewOption).itemHeight = 120;
|
||||
screen.insert(1, new NewOption(getID('newVariation'), getID('newVariationDesc'), () -> {
|
||||
parent.openSubState(new VariationCreationScreen(s, saveSong));
|
||||
}));
|
||||
screen.curSelected = 2;
|
||||
}
|
||||
#end
|
||||
|
||||
parent.addMenu(screen);
|
||||
}
|
||||
|
||||
public function makeSongOption(s:ChartMetaData):IconOption {
|
||||
songList.push(s.name.toLowerCase());
|
||||
|
||||
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> {
|
||||
curSong = s;
|
||||
|
||||
var screen = new EditorTreeMenuScreen(s.name, getID('selectDifficulty'), [
|
||||
for (d in s.difficulties) if (d != '') makeChartOption(d, s.name)
|
||||
]);
|
||||
|
||||
#if sys
|
||||
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
|
||||
parent.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
screen.curSelected = 1;
|
||||
#end
|
||||
|
||||
parent.addMenu(screen);
|
||||
});
|
||||
opt.suffix = ' >';
|
||||
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> openSongOption(s, true));
|
||||
opt.suffix = " >";
|
||||
opt.editorFlashColor = s.color.getDefault(FlxColor.WHITE);
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
public function new() {
|
||||
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', () -> {
|
||||
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', #if sys () -> {
|
||||
parent.openSubState(new SongCreationScreen(saveSong));
|
||||
});
|
||||
} #end);
|
||||
freeplayList = FreeplaySonglist.get(false);
|
||||
|
||||
for (i => s in freeplayList.songs) add(makeSongOption(s));
|
||||
}
|
||||
|
||||
#if sys
|
||||
public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) {
|
||||
var songAlreadyExists:Bool = songList.contains(creation.meta.name.toLowerCase());
|
||||
if (songAlreadyExists) {
|
||||
public function saveSong(creation:SongCreationData, ?callback:String -> Void) {
|
||||
var variant = creation.meta.variant != null && creation.meta.variant != "" ? creation.meta.variant : null;
|
||||
if (variant != null && curSong != null ? curSong.metas.exists(variant) : songList.contains(creation.meta.name.toLowerCase())) {
|
||||
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
|
||||
]));
|
||||
return;
|
||||
}
|
||||
|
||||
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${creation.meta.name}';
|
||||
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${variant != null && curSong != null ? curSong.name : creation.meta.name}';
|
||||
|
||||
#if sys
|
||||
// Make Directories
|
||||
@@ -82,24 +99,37 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
sys.FileSystem.createDirectory('$songFolder/charts');
|
||||
|
||||
// Save Files
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(creation.meta));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
var instSuffix = creation.meta.instSuffix != null ? creation.meta.instSuffix : '', vocalsSuffix = creation.meta.vocalsSuffix != null ? creation.meta.vocalsSuffix : '';
|
||||
CoolUtil.safeSaveFile('$songFolder/meta${variant != null ? "-" + variant : ""}.json', Json.stringify(Chart.filterMetaForSaving(creation.meta), null, Flags.JSON_PRETTY_PRINT));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst$instSuffix.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices$vocalsSuffix.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
|
||||
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player.${Flags.SOUND_EXT}', creation.playerVocals);
|
||||
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals);
|
||||
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player$vocalsSuffix.${Flags.SOUND_EXT}', creation.playerVocals);
|
||||
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent$vocalsSuffix.${Flags.SOUND_EXT}', creation.oppVocals);
|
||||
#end
|
||||
|
||||
if (callback != null) callback(songFolder, creation);
|
||||
if (callback != null) callback(songFolder);
|
||||
|
||||
// Add to List
|
||||
freeplayList.songs.insert(0, creation.meta);
|
||||
insert(1, makeSongOption(creation.meta));
|
||||
if (variant != null && curSong != null) {
|
||||
if (curSong.variants == null) curSong.variants = [];
|
||||
if (!curSong.variants.contains(variant)) curSong.variants.push(variant);
|
||||
|
||||
curSong.metas.set(variant, creation.meta);
|
||||
|
||||
parent.tree.last().add(makeVariationOption(creation.meta));
|
||||
|
||||
var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant == "" ? "-" + curSong.variant : ""}.json';
|
||||
CoolUtil.safeSaveFile(metaPath, Chart.makeMetaSaveable(curSong));
|
||||
}
|
||||
else {
|
||||
freeplayList.songs.insert(0, creation.meta);
|
||||
insert(1, makeSongOption(creation.meta));
|
||||
}
|
||||
}
|
||||
|
||||
public function saveChart(name:String, data:ChartData) {
|
||||
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
|
||||
if (difficultyAlreadyExists) {
|
||||
if (curSong.difficulties.contains(name)) {
|
||||
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
|
||||
]));
|
||||
@@ -113,14 +143,15 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
|
||||
// Add to List
|
||||
curSong.difficulties.push(name);
|
||||
parent.tree.last().insert(parent.tree.last().length - 1, makeChartOption(name, curSong.name));
|
||||
|
||||
var screen = parent.tree.last();
|
||||
var idx = 0;
|
||||
while (!(screen.members[idx] is Separator)) idx++;
|
||||
screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name));
|
||||
|
||||
// Add to Meta
|
||||
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
|
||||
if (meta.difficulties != null && !meta.difficulties.contains(name)) {
|
||||
meta.difficulties.push(name);
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(meta));
|
||||
}
|
||||
var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant == "" ? "-" + curSong.variant : ""}.json';
|
||||
CoolUtil.safeSaveFile(metaPath, Chart.makeMetaSaveable(curSong));
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class CharterStrumline extends UISprite {
|
||||
var maxRow = Math.floor((icons.length-1) / 4) + 1;
|
||||
for (i=>icon in icons) {
|
||||
var healthIcon = new HealthIcon(Character.getIconFromCharName(icon));
|
||||
healthIcon.scale.x = healthIcon.scale.y = Math.max((0.6 - (icons.length / 20)), 0.35);
|
||||
healthIcon.scale.x = healthIcon.scale.y = Math.max((0.6 - (icons.length / 20)), 0.35) * (150 / Math.max(healthIcon.frameWidth, healthIcon.frameHeight));
|
||||
healthIcon.updateHitbox();
|
||||
healthIcon.x = FlxMath.lerp(0, Math.min(icons.length * 20, 120), (maxCol-1 != 0 ? (i % 4) / (maxCol-1) : 0));
|
||||
healthIcon.y = (draggable ? 14 : 7) + FlxMath.lerp(0, Math.min(maxRow * 15, 60), (maxRow-1 != 0 ? Math.floor(i / 4) / (maxRow-1) : 0));
|
||||
|
||||
@@ -171,7 +171,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
}
|
||||
}
|
||||
|
||||
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
|
||||
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" && Assets.exists(Paths.voices(Charter.__song.toLowerCase(), "", strumLine.vocalsSuffix)) ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
|
||||
add(vocalsSuffixDropDown);
|
||||
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ package funkin.editors.charter;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.text.FlxText.FlxTextFormat;
|
||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||
import funkin.backend.chart.Chart;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.backend.chart.FNFLegacyParser.SwagSong;
|
||||
import funkin.backend.chart.PsychParser;
|
||||
import funkin.backend.chart.VSliceParser;
|
||||
import funkin.backend.utils.ZipUtil;
|
||||
import funkin.backend.utils.ZipUtil.ZipReader;
|
||||
import funkin.game.HealthIcon;
|
||||
import haxe.Json;
|
||||
@@ -22,7 +24,7 @@ typedef SongCreationData = {
|
||||
}
|
||||
|
||||
class SongCreationScreen extends UISubstateWindow {
|
||||
private var onSave:Null<SongCreationData> -> Null<String -> SongCreationData -> Void> -> Void = null;
|
||||
private var onSave:Null<SongCreationData> -> Null<String -> Void> -> Void = null;
|
||||
|
||||
public var songNameTextBox:UITextBox;
|
||||
public var bpmStepper:UINumericStepper;
|
||||
@@ -71,7 +73,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
public var curPage:Int = 0;
|
||||
|
||||
public function new(?onSave:SongCreationData -> Null<String -> SongCreationData -> Void> -> Void) {
|
||||
public function new(?onSave:SongCreationData -> Null<String -> Void> -> Void) {
|
||||
super();
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
@@ -244,7 +246,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
var pages = isImporting ? importPages : pages;
|
||||
if (curPage == pages.length-1) {
|
||||
saveSongInfo();
|
||||
close();
|
||||
if (subState == null) close();
|
||||
} else {
|
||||
curPage++;
|
||||
refreshPages();
|
||||
@@ -295,7 +297,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
if (curPage == 1) {
|
||||
importInstExplorer.selectable = importVoicesExplorer.selectable = !project;
|
||||
saveButton.selectable = project ? true : (importInstExplorer.file != null);
|
||||
saveButton.selectable = #if TEST_BUILD true #else project ? true : (importInstExplorer.file != null) #end;
|
||||
} else if (curPage == 2) {
|
||||
importIdTextBox.selectable = !project;
|
||||
importChartFile.fileType = project ? "fnfc" : "json";
|
||||
@@ -355,16 +357,23 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
iconSprite.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
// for variations
|
||||
function formatMeta(meta:ChartMetaData):ChartMetaData
|
||||
return Chart.defaultChartMetaFields(meta);
|
||||
|
||||
function getChartSavePath(meta:ChartMetaData, diff:String):String
|
||||
return 'charts/${meta.variant != null && meta.variant != "" ? meta.variant + "/" : ""}$diff.json';
|
||||
|
||||
function saveSongInfo() {
|
||||
if (isImporting)
|
||||
{
|
||||
try switch(engineDropdown.index)
|
||||
{
|
||||
case 0 /*"V-Slice Project (.fnfc)"*/:
|
||||
case 2 /*"V-Slice Project (.fnfc)"*/:
|
||||
var files:Map<String, Any> = [];
|
||||
for (field in new ZipReader(new BytesInput(importChartFile.file)).read()) {
|
||||
var fileName = field.fileName;
|
||||
var fileContent = field.data;
|
||||
var fileContent = ZipUtil.unzip(field);
|
||||
files.set(fileName, fileContent);
|
||||
}
|
||||
saveFromVSlice(files);
|
||||
@@ -378,35 +387,37 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
saveFromVSlice(files, songId);
|
||||
default /*"Psych/Legacy FNF"*/:
|
||||
var songId = importIdTextBox.label.text;
|
||||
var oldChart:SwagSong = Json.parse(cast importChartFile.file);
|
||||
var oldChart:SwagSong = Chart.cleanSongData(Json.parse(cast importChartFile.file));
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
noteTypes: [],
|
||||
events: [],
|
||||
meta: {name: null},
|
||||
meta: {name: songId},
|
||||
scrollSpeed: Flags.DEFAULT_SCROLL_SPEED,
|
||||
stage: Flags.DEFAULT_STAGE,
|
||||
codenameChart: true
|
||||
};
|
||||
PsychParser.parse(oldChart, base);
|
||||
var meta = formatMeta({
|
||||
name: songId,
|
||||
difficulties: ['normal'],
|
||||
bpm: oldChart.bpm,
|
||||
beatsPerMeasure: oldChart.beatsPerMeasure,
|
||||
stepsPerBeat: oldChart.stepsPerBeat,
|
||||
displayName: oldChart.song
|
||||
});
|
||||
if (onSave != null) onSave({
|
||||
meta: {
|
||||
name: songId,
|
||||
difficulties: [songId],
|
||||
bpm: oldChart.bpm,
|
||||
beatsPerMeasure: oldChart.beatsPerMeasure,
|
||||
stepsPerBeat: oldChart.stepsPerBeat,
|
||||
displayName: oldChart.song
|
||||
},
|
||||
meta: meta,
|
||||
instBytes: importInstExplorer.file,
|
||||
voicesBytes: importVoicesExplorer.file,
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
}, (songFolder:String) -> {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
|
||||
CoolUtil.safeSaveFile('$songFolder/${getChartSavePath(meta, "normal")}', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
} catch (e) {
|
||||
openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", Std.string(e), [
|
||||
} catch (e:haxe.Exception) {
|
||||
trace(e.stack, e.message);
|
||||
openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", e.details(), [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
}
|
||||
@@ -414,7 +425,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper])
|
||||
@:privateAccess stepper.__onChange(stepper.label.text);
|
||||
|
||||
var meta:ChartMetaData = {
|
||||
var meta:ChartMetaData = formatMeta({
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
@@ -424,8 +435,8 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
};
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()]
|
||||
});
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
@@ -441,19 +452,23 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
var vslicechart:NewSwagSong = Json.parse(files.get('${songId}-chart.json'));
|
||||
var playData = vslicemeta.playData;
|
||||
|
||||
var meta:ChartMetaData = {name: songId};
|
||||
var diffCharts:Array<ChartDataWithInfo> = [];
|
||||
var meta:ChartMetaData = formatMeta({name: songId, needsVoices: files.get('Voices.${Flags.SOUND_EXT}') != null});
|
||||
var diffCharts:Array<ChartDataWithInfo> = [], events:Array<ChartEvent> = null;
|
||||
VSliceParser.parse(vslicemeta, vslicechart, meta, diffCharts, songId);
|
||||
|
||||
if (diffCharts.length != 0) events = diffCharts[0].chart.events;
|
||||
for (diff in diffCharts) diff.chart.events = [];
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
instBytes: files.get('Inst.${Flags.SOUND_EXT}'),
|
||||
voicesBytes: files.get('Voices.${Flags.SOUND_EXT}'), //it may exist
|
||||
playerVocals: files.get('Voices-${playData.characters.playerVocals != null ? playData.characters.playerVocals[0] : playData.characters.player}.${Flags.SOUND_EXT}'),
|
||||
oppVocals: files.get('Voices-${playData.characters.opponentVocals != null ? playData.characters.opponentVocals[0] : playData.characters.opponent}.${Flags.SOUND_EXT}'),
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
}, (songFolder:String) -> {
|
||||
#if sys
|
||||
for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/charts/${diff.diffName}.json', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT));
|
||||
for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/${getChartSavePath(meta, diff.diffName)}', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT));
|
||||
if (events != null) CoolUtil.safeSaveFile('$songFolder/events${meta.variant != null && meta.variant != "" ? "-" + meta.variant : ""}.json', Json.stringify({events: events}, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import flixel.text.FlxText.FlxTextFormat;
|
||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.editors.charter.SongCreationScreen;
|
||||
|
||||
class VariationCreationScreen extends SongCreationScreen {
|
||||
public var parentMeta:ChartMetaData;
|
||||
|
||||
public function new(parent:ChartMetaData, ?onSave:SongCreationData -> Null<String -> Void> -> Void) {
|
||||
super(onSave);
|
||||
this.parentMeta = parent;
|
||||
}
|
||||
|
||||
override function create() {
|
||||
super.create();
|
||||
|
||||
songNameTextBox.label.text = translateMeta("variation");
|
||||
cast(songNameTextBox.members[songNameTextBox.members.length - 1], UIText).text = translateMeta("variation");
|
||||
|
||||
cast(importIdTextBox.members[importIdTextBox.members.length - 1], UIText).applyMarkup(
|
||||
translateMeta("variation"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]
|
||||
);
|
||||
|
||||
bpmStepper.value = parentMeta.bpm;
|
||||
beatsPerMeasureStepper.value = parentMeta.beatsPerMeasure;
|
||||
denominatorStepper.value = Std.int(16 / parentMeta.stepsPerBeat);
|
||||
displayNameTextBox.label.text = parentMeta.displayName;
|
||||
iconTextBox.label.text = parentMeta.icon;
|
||||
opponentModeCheckbox.checked = parentMeta.opponentModeAllowed;
|
||||
coopAllowedCheckbox.checked = parentMeta.coopAllowed;
|
||||
colorWheel.setColor(parentMeta.color);
|
||||
difficultiesTextBox.label.text = parentMeta.difficulties.join(', ');
|
||||
}
|
||||
|
||||
override function formatMeta(meta:ChartMetaData):ChartMetaData {
|
||||
meta.variant = meta.name;
|
||||
meta.vocalsSuffix = meta.instSuffix = '-${meta.variant}';
|
||||
meta.name = parentMeta.name;
|
||||
meta.icon = parentMeta.icon;
|
||||
meta.color = parentMeta.color;
|
||||
return super.formatMeta(meta);
|
||||
}
|
||||
}
|
||||
@@ -40,11 +40,11 @@ class StageCreationScreen extends UISubstateWindow {
|
||||
var stageInfo:UIText;
|
||||
add(stageInfo = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("title"), 28));
|
||||
|
||||
add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, translate("stage-name")));
|
||||
addLabelOn(stageNameTextBox, translate("stage-name"));
|
||||
add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, translate("stageName")));
|
||||
addLabelOn(stageNameTextBox, translate("stageName"));
|
||||
|
||||
add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, translate("stage-path")));
|
||||
addLabelOn(stagePathTextBox, translate("stage-path"));
|
||||
add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, translate("stagePath")));
|
||||
addLabelOn(stagePathTextBox, translate("stagePath"));
|
||||
|
||||
add(saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveStageInfo();
|
||||
|
||||
@@ -31,7 +31,6 @@ class UIColorwheel extends UISliceSprite {
|
||||
hue = curColor.hue; saturation = curColor.saturation; brightness = curColor.brightness;
|
||||
|
||||
colorPickerShader = new CustomShader("engine/colorPicker");
|
||||
colorPickerShader.hset("hue", hue);
|
||||
|
||||
colorPicker = new FlxSprite(x + 12.5, (y + 125/2) - (100/2)).makeGraphic(100, 100, FlxColor.TRANSPARENT);
|
||||
colorPicker.drawRoundRect(0, 0, 100, 100, 10, 10, FlxColor.WHITE);
|
||||
@@ -91,6 +90,11 @@ class UIColorwheel extends UISliceSprite {
|
||||
updateWheel(false);
|
||||
}
|
||||
|
||||
public function setColor(color:FlxColor) {
|
||||
hue = curColor.hue; saturation = curColor.saturation; brightness = curColor.brightness;
|
||||
updateWheel(false);
|
||||
}
|
||||
|
||||
inline function updateColorPickerSelector()
|
||||
colorPickerSelector.selector.setPosition(colorPicker.x + (colorPicker.width *saturation) - 8, colorPicker.y + (colorPicker.height + (colorPicker.height * -brightness))- 8);
|
||||
|
||||
|
||||
@@ -178,9 +178,6 @@ class HealthIcon extends FunkinSprite
|
||||
|
||||
loadGraphic(iconAsset, true, Std.int(Math.min(iconSize, assetW)), Std.int(Math.min(iconSize, assetH)));
|
||||
|
||||
setGraphicSize(150);
|
||||
updateHitbox();
|
||||
|
||||
animation.add(char, [for(i in 0...iconAmt) i], 0, false, isPlayer != iconIsPlayer);
|
||||
animation.play(char);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,13 @@ class Note extends FlxSprite
|
||||
*/
|
||||
public var nextSustain:Note;
|
||||
|
||||
/**
|
||||
* The parent of the sustain.
|
||||
*
|
||||
* If this note is not sustain, this will be null.
|
||||
*/
|
||||
public var sustainParent:Null<Note>;
|
||||
|
||||
/**
|
||||
* Name of the splash.
|
||||
*/
|
||||
@@ -125,6 +132,10 @@ class Note extends FlxSprite
|
||||
for(field in Reflect.fields(noteData)) if(!DEFAULT_FIELDS.contains(field))
|
||||
this.extra.set(field, Reflect.field(noteData, field));
|
||||
|
||||
// work around to set the `sustainParent`
|
||||
if (isSustainNote)
|
||||
sustainParent = prevNote.isSustainNote ? prevNote.sustainParent : prevNote;
|
||||
|
||||
x += 50;
|
||||
// MAKE SURE ITS DEFINITELY OFF SCREEN?
|
||||
y -= 2000;
|
||||
|
||||
@@ -61,10 +61,18 @@ class PlayState extends MusicBeatState
|
||||
* The remaining songs in the Story Mode playlist.
|
||||
*/
|
||||
public static var storyPlaylist:Array<String> = [];
|
||||
/**
|
||||
* The remaining variations to play with the Story Mode
|
||||
*/
|
||||
public static var storyVariations:Array<String> = [];
|
||||
/**
|
||||
* The selected difficulty name.
|
||||
*/
|
||||
public static var difficulty:String = Flags.DEFAULT_DIFFICULTY;
|
||||
/**
|
||||
* The selected variation name. (It can be null)
|
||||
*/
|
||||
public static var variation:Null<String>;
|
||||
/**
|
||||
* Whenever the week is coming from the mods folder or not.
|
||||
*/
|
||||
@@ -552,7 +560,7 @@ class PlayState extends MusicBeatState
|
||||
@:noCompletion @:dox(hide) private var _endSongCalled:Bool = false;
|
||||
|
||||
@:dox(hide)
|
||||
var __vocalSyncTimer:Float = 0;
|
||||
var __vocalSyncTimer:Float = 1;
|
||||
|
||||
private function get_accuracy():Float {
|
||||
if (accuracyPressedNotes <= 0) return -1;
|
||||
@@ -657,7 +665,7 @@ class PlayState extends MusicBeatState
|
||||
persistentDraw = true;
|
||||
|
||||
if (SONG == null)
|
||||
SONG = Chart.parse('tutorial', 'normal');
|
||||
SONG = Chart.parse('tutorial', difficulty = 'normal', variation = null);
|
||||
|
||||
scrollSpeed = SONG.scrollSpeed;
|
||||
|
||||
@@ -1113,9 +1121,11 @@ class PlayState extends MusicBeatState
|
||||
curSong = songData.meta.name.toLowerCase();
|
||||
curSongID = curSong.replace(" ", "-");
|
||||
|
||||
FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty))));
|
||||
if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
|
||||
vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(Paths.voices(SONG.meta.name, difficulty)) : Paths.voices(SONG.meta.name, difficulty));
|
||||
FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix))));
|
||||
|
||||
var vocalsPath = Paths.voices(SONG.meta.name, difficulty, SONG.meta.vocalsSuffix);
|
||||
if (SONG.meta.needsVoices && Assets.exists(vocalsPath))
|
||||
vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath);
|
||||
else
|
||||
vocals = new FlxSound();
|
||||
|
||||
@@ -1224,7 +1234,7 @@ class PlayState extends MusicBeatState
|
||||
var time = Conductor.songPosition + Conductor.songOffset;
|
||||
for (strumLine in strumLines.members) strumLine.vocals.play(true, time);
|
||||
vocals.play(true, time);
|
||||
inst.play(true, time);
|
||||
if (!inst.playing) inst.play(true, time);
|
||||
|
||||
gameAndCharsCall("onVocalsResync");
|
||||
}
|
||||
@@ -1350,7 +1360,7 @@ class PlayState extends MusicBeatState
|
||||
updateRatingStuff();
|
||||
|
||||
if (canAccessDebugMenus && chartingMode && controls.DEV_ACCESS)
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false));
|
||||
|
||||
if (Options.camZoomOnBeat && camZooming) {
|
||||
var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset);
|
||||
@@ -1375,20 +1385,19 @@ class PlayState extends MusicBeatState
|
||||
updateIconPositions();
|
||||
|
||||
if (startingSong) {
|
||||
if (startedCountdown) {
|
||||
Conductor.songPosition += Conductor.songOffset + elapsed * 1000;
|
||||
if (Conductor.songPosition >= 0)
|
||||
startSong();
|
||||
if (startedCountdown && (Conductor.songPosition += Conductor.songOffset + elapsed * 1000) >= 0) {
|
||||
Conductor.songPosition = Conductor.songOffset;
|
||||
startSong();
|
||||
}
|
||||
}
|
||||
else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) {
|
||||
__vocalSyncTimer = 1;
|
||||
|
||||
var instTime = FlxG.sound.music.getActualTime();
|
||||
var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 30;
|
||||
var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 100;
|
||||
if (!isOffsync) {
|
||||
for (strumLine in strumLines.members) {
|
||||
if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 30)) break;
|
||||
if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 100)) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1686,22 +1695,17 @@ class PlayState extends MusicBeatState
|
||||
*/
|
||||
public function endSong():Void
|
||||
{
|
||||
if (gameAndCharsEvent("onSongEnd", new CancellableEvent()).cancelled) return;
|
||||
endingSong = true;
|
||||
gameAndCharsCall("onSongEnd");
|
||||
canPause = false;
|
||||
inst.volume = 0;
|
||||
vocals.volume = 0;
|
||||
for (strumLine in strumLines.members) {
|
||||
strumLine.vocals.volume = 0;
|
||||
strumLine.vocals.pause();
|
||||
}
|
||||
inst.pause();
|
||||
vocals.pause();
|
||||
|
||||
if (validScore)
|
||||
{
|
||||
for (strumLine in strumLines.members) strumLine.vocals.stop();
|
||||
inst.stop();
|
||||
vocals.stop();
|
||||
|
||||
if (validScore) {
|
||||
#if !switch
|
||||
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, {
|
||||
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, variation, {
|
||||
score: songScore,
|
||||
misses: misses,
|
||||
accuracy: accuracy,
|
||||
@@ -1727,20 +1731,19 @@ class PlayState extends MusicBeatState
|
||||
* Immediately switches to the next song, or goes back to the Story/Freeplay menu.
|
||||
*/
|
||||
public function nextSong() {
|
||||
if (isStoryMode)
|
||||
{
|
||||
if (isStoryMode) {
|
||||
campaignScore += songScore;
|
||||
campaignMisses += misses;
|
||||
campaignAccuracyTotal += accuracy;
|
||||
campaignAccuracyCount++;
|
||||
storyPlaylist.shift();
|
||||
storyVariations.shift();
|
||||
|
||||
if (storyPlaylist.length <= 0)
|
||||
{
|
||||
if (storyPlaylist.length <= 0) {
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
|
||||
if (validScore)
|
||||
{
|
||||
if (validScore) {
|
||||
#if !switch
|
||||
// TODO: more week info saving
|
||||
FunkinSave.setWeekHighscore(storyWeek.id, difficulty, {
|
||||
score: campaignScore,
|
||||
@@ -1749,29 +1752,23 @@ class PlayState extends MusicBeatState
|
||||
hits: [],
|
||||
date: Date.now().toString()
|
||||
});
|
||||
#end
|
||||
}
|
||||
FlxG.save.flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty)', "PlayState");
|
||||
else {
|
||||
Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty/${storyVariations[0]})', "PlayState");
|
||||
|
||||
registerSmoothTransition();
|
||||
|
||||
FlxG.sound.music.stop();
|
||||
|
||||
__loadSong(storyPlaylist[0], difficulty);
|
||||
|
||||
__loadSong(storyPlaylist[0], difficulty, storyVariations[0]);
|
||||
FlxG.switchState(new PlayState());
|
||||
}
|
||||
}
|
||||
else if (chartingMode)
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false));
|
||||
else
|
||||
{
|
||||
if (chartingMode)
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
|
||||
else
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
|
||||
public function registerSmoothTransition() {
|
||||
@@ -1943,6 +1940,8 @@ class PlayState extends MusicBeatState
|
||||
}
|
||||
|
||||
if (event.deleteNote) strumLine.deleteNote(note);
|
||||
|
||||
gameAndCharsEvent("onPostNoteHit", event);
|
||||
}
|
||||
|
||||
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
|
||||
@@ -2142,43 +2141,51 @@ class PlayState extends MusicBeatState
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
storyWeek = weekData;
|
||||
storyPlaylist = [for (e in weekData.songs) e.name];
|
||||
storyVariations = [for (e in weekData.songs) e.variation];
|
||||
isStoryMode = true;
|
||||
campaignScore = 0;
|
||||
campaignMisses = 0;
|
||||
campaignAccuracyTotal = 0;
|
||||
campaignAccuracyCount = 0;
|
||||
chartingMode = false;
|
||||
opponentMode = coopMode = false;
|
||||
__loadSong(storyPlaylist[0], difficulty);
|
||||
chartingMode = coopMode = opponentMode = false;
|
||||
__loadSong(storyPlaylist[0], difficulty, storyVariations[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a song into PlayState
|
||||
* @param name Song name
|
||||
* @param difficulty Chart difficulty (if invalid, will load an empty chart)
|
||||
* @param variation Song Variation
|
||||
* @param opponentMode Whenever opponent mode is on
|
||||
* @param coopMode Whenever co-op mode is on.
|
||||
*/
|
||||
public static function loadSong(_name:String, ?_difficulty:String, _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
public static function loadSong(_name:String, ?_difficulty:String, ?_variation:Dynamic, _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
if (_difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
isStoryMode = false;
|
||||
if (_variation is Bool) { // vackward cumpatibshit
|
||||
_coopMode = _opponentMode;
|
||||
_opponentMode = cast _variation;
|
||||
_variation = null;
|
||||
}
|
||||
else if (!(_variation is String)) _variation = null;
|
||||
opponentMode = _opponentMode;
|
||||
chartingMode = false;
|
||||
coopMode = _coopMode;
|
||||
__loadSong(_name, _difficulty);
|
||||
isStoryMode = chartingMode = false;
|
||||
__loadSong(_name, _difficulty, cast _variation);
|
||||
}
|
||||
|
||||
/**
|
||||
* (INTERNAL) Loads a song without resetting story mode/opponent mode/coop mode values.
|
||||
* @param name Song name
|
||||
* @param difficulty Song difficulty
|
||||
* @param difficulty Chart difficulty
|
||||
* @param variation Song Variation
|
||||
*/
|
||||
public static function __loadSong(_name:String, _difficulty:String) {
|
||||
public static function __loadSong(_name:String, _difficulty:String, _variation:String) {
|
||||
difficulty = _difficulty;
|
||||
variation = _variation;
|
||||
seenCutscene = false;
|
||||
deathCounter = 0;
|
||||
|
||||
SONG = Chart.parse(_name, _difficulty);
|
||||
SONG = Chart.parse(_name, _difficulty, _variation);
|
||||
fromMods = SONG.fromMods;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,21 @@ class FreeplayState extends MusicBeatState
|
||||
*/
|
||||
public var songs:Array<ChartMetaData> = [];
|
||||
|
||||
/**
|
||||
* Current song metadata
|
||||
*/
|
||||
public var curSong:Null<ChartMetaData>;
|
||||
|
||||
/**
|
||||
* Current song difficulties
|
||||
*/
|
||||
public var curDifficulties:Array<String>;
|
||||
|
||||
/**
|
||||
* songs[curSelected].metas.get(curDiffMetaIndices[curDifficulty])
|
||||
*/
|
||||
public var curDiffMetaKeys:Array<String> = [];
|
||||
|
||||
/**
|
||||
* Currently selected song
|
||||
*/
|
||||
@@ -43,7 +58,7 @@ class FreeplayState extends MusicBeatState
|
||||
public var diffText:FlxText;
|
||||
|
||||
/**
|
||||
* Text containing the current coop/opponent mode ([TAB] Co-Op mode)
|
||||
* Text containing the current coop/opponent mode ([KEYBINDS] Co-Op mode)
|
||||
*/
|
||||
public var coopText:FlxText;
|
||||
|
||||
@@ -107,14 +122,15 @@ class FreeplayState extends MusicBeatState
|
||||
curSelected = k;
|
||||
}
|
||||
}
|
||||
if (songs[curSelected] != null) {
|
||||
for(k=>diff in songs[curSelected].difficulties) {
|
||||
if (diff == Options.freeplayLastDifficulty) {
|
||||
curDifficulty = k;
|
||||
}
|
||||
}
|
||||
|
||||
updateCurDifficulties();
|
||||
for(i=>diff in curDifficulties) {
|
||||
if (curDiffMetaKeys[i] == Options.freeplayLastVariation && diff == Options.freeplayLastDifficulty)
|
||||
curDifficulty = i;
|
||||
}
|
||||
|
||||
updateCurSong();
|
||||
|
||||
DiscordUtil.call("onMenuLoaded", ["Freeplay"]);
|
||||
|
||||
super.create();
|
||||
@@ -139,8 +155,7 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
var icon:HealthIcon = new HealthIcon(songs[i].icon);
|
||||
icon.sprTracker = songText;
|
||||
icon.setUnstretchedGraphicSize(150, 150, true);
|
||||
icon.updateHitbox();
|
||||
if (Math.max(icon.width, icon.height) > 150) icon.setUnstretchedGraphicSize(150, 150);
|
||||
|
||||
// using a FlxGroup is too much fuss!
|
||||
iconArray.push(icon);
|
||||
@@ -169,7 +184,6 @@ class FreeplayState extends MusicBeatState
|
||||
add(scoreText);
|
||||
|
||||
changeSelection(0, true);
|
||||
changeDiff(0, true);
|
||||
changeCoopMode(0, true);
|
||||
|
||||
interpColor = new FlxInterpolateColor(bg.color);
|
||||
@@ -225,7 +239,7 @@ class FreeplayState extends MusicBeatState
|
||||
if (canSelect) {
|
||||
changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
|
||||
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
|
||||
changeCoopMode((FlxG.keys.justPressed.TAB ? 1 : 0)); // TODO: make this configurable
|
||||
changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable
|
||||
// putting it before so that its actually smooth
|
||||
updateOptionsAlpha();
|
||||
}
|
||||
@@ -238,14 +252,14 @@ class FreeplayState extends MusicBeatState
|
||||
scoreText.x = coopText.x = scoreBG.x + 4;
|
||||
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
|
||||
|
||||
interpColor.fpsLerpTo(songs[curSelected].color, 0.0625);
|
||||
interpColor.fpsLerpTo(curSong.color, 0.0625);
|
||||
bg.color = interpColor.color;
|
||||
|
||||
#if PRELOAD_ALL
|
||||
var dontPlaySongThisFrame = false;
|
||||
autoplayElapsed += elapsed;
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
|
||||
var streamed = false;
|
||||
if (Options.streamedMusic) {
|
||||
var sound = Assets.getMusic(curPlayingInst, true, false);
|
||||
@@ -253,7 +267,7 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
if (streamed && autoplayShouldPlay) {
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
|
||||
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +282,7 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
if (sound != null && autoplayShouldPlay) {
|
||||
FlxG.sound.playMusic(sound, 0);
|
||||
Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
|
||||
Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
|
||||
}
|
||||
}
|
||||
if (!disableAsyncLoading) Main.execAsync(huh);
|
||||
@@ -302,7 +316,6 @@ class FreeplayState extends MusicBeatState
|
||||
function updateCoopModes() {
|
||||
__opponentMode = false;
|
||||
__coopMode = false;
|
||||
var curSong = songs[curSelected];
|
||||
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
|
||||
__opponentMode = curCoopMode % 2 == 1;
|
||||
__coopMode = curCoopMode >= 2;
|
||||
@@ -319,9 +332,9 @@ class FreeplayState extends MusicBeatState
|
||||
public function select() {
|
||||
updateCoopModes();
|
||||
|
||||
if (songs[curSelected].difficulties.length <= 0) return;
|
||||
if (curDifficulties.length == 0) return;
|
||||
|
||||
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], __opponentMode, __coopMode));
|
||||
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, __opponentMode, __coopMode));
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
@@ -329,17 +342,18 @@ class FreeplayState extends MusicBeatState
|
||||
autoplayShouldPlay = false;
|
||||
#end
|
||||
|
||||
Options.freeplayLastSong = songs[curSelected].name;
|
||||
Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty];
|
||||
Options.freeplayLastSong = curSong.name;
|
||||
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
|
||||
Options.freeplayLastVariation = curSong.variant;
|
||||
|
||||
PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode);
|
||||
PlayState.loadSong(event.song, event.difficulty, event.variant, event.opponentMode, event.coopMode);
|
||||
FlxG.switchState(new PlayState());
|
||||
}
|
||||
|
||||
public function convertChart() {
|
||||
trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...');
|
||||
var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]);
|
||||
Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty]);
|
||||
trace('Converting ${curSong.name} ${curDifficulties[curDifficulty]} ${curSong.variant} to Codename format...');
|
||||
var chart = Chart.parse(curSong.name, curDifficulties[curDifficulty], curSong.variant);
|
||||
Chart.save(chart, curDifficulties[curDifficulty], curSong.variant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,28 +361,35 @@ class FreeplayState extends MusicBeatState
|
||||
* @param change How much to change.
|
||||
* @param force Force the change if `change` is equal to 0
|
||||
*/
|
||||
public function changeDiff(change:Int = 0, force:Bool = false)
|
||||
{
|
||||
public function changeDiff(change:Int = 0, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
|
||||
var curSong = songs[curSelected];
|
||||
var validDifficulties = curSong.difficulties.length > 0;
|
||||
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curSong.difficulties.length-1) : 0, change));
|
||||
var validDifficulties = curDifficulties.length > 0;
|
||||
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curDifficulties.length-1) : 0, change));
|
||||
|
||||
if (event.cancelled) return;
|
||||
if (event.cancelled) {
|
||||
if (force) updateCurSong();
|
||||
return;
|
||||
}
|
||||
|
||||
var prevSong = curSong;
|
||||
curDifficulty = event.value;
|
||||
|
||||
updateCurSong();
|
||||
updateScore();
|
||||
|
||||
if (curSong.difficulties.length > 1)
|
||||
diffText.text = '< ${curSong.difficulties[curDifficulty].toUpperCase()} >';
|
||||
else
|
||||
diffText.text = validDifficulties ? curSong.difficulties[curDifficulty].toUpperCase() : "-";
|
||||
#if PRELOAD_ALL
|
||||
if (curSong != prevSong) {
|
||||
autoplayElapsed = 0;
|
||||
songInstPlaying = false;
|
||||
}
|
||||
#end
|
||||
|
||||
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
|
||||
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
|
||||
}
|
||||
|
||||
function updateScore() {
|
||||
if (songs[curSelected].difficulties.length <= 0) {
|
||||
if (curDifficulties.length == 0) {
|
||||
intendedScore = 0;
|
||||
return;
|
||||
}
|
||||
@@ -376,7 +397,7 @@ class FreeplayState extends MusicBeatState
|
||||
var changes:Array<HighscoreChange> = [];
|
||||
if (__coopMode) changes.push(CCoopMode);
|
||||
if (__opponentMode) changes.push(COpponentMode);
|
||||
var saveData = FunkinSave.getSongHighscore(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], changes);
|
||||
var saveData = FunkinSave.getSongHighscore(curSong.name, curDifficulties[curDifficulty], curSong.variant, changes);
|
||||
intendedScore = saveData.score;
|
||||
}
|
||||
|
||||
@@ -397,9 +418,9 @@ class FreeplayState extends MusicBeatState
|
||||
*/
|
||||
public function changeCoopMode(change:Int = 0, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
if (!songs[curSelected].coopAllowed && !songs[curSelected].opponentModeAllowed) return;
|
||||
if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
|
||||
|
||||
var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
|
||||
var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed;
|
||||
var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change));
|
||||
|
||||
if (event.cancelled) return;
|
||||
@@ -408,12 +429,16 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
updateScore();
|
||||
|
||||
var key = "[TAB] "; // TODO: make this configurable
|
||||
var coopBinds = [CoolUtil.keyToString(Options.P1_CHANGE_MODE[0]), CoolUtil.keyToString(Options.P2_CHANGE_MODE[0])].filter(x -> x != "---");
|
||||
if (coopBinds.length == 2 && coopBinds[1] == coopBinds[0]) coopBinds.pop();
|
||||
else if (coopBinds.length == 0) coopBinds.push("---");
|
||||
|
||||
var key = '[${coopBinds.join(" / ")}] ';
|
||||
|
||||
if (bothEnabled) {
|
||||
coopText.text = key + coopLabels[curCoopMode];
|
||||
} else {
|
||||
coopText.text = key + coopLabels[curCoopMode * (songs[curSelected].coopAllowed ? 2 : 1)];
|
||||
coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,17 +447,23 @@ class FreeplayState extends MusicBeatState
|
||||
* @param change How much to change
|
||||
* @param force Force the change, even if `change` is equal to 0.
|
||||
*/
|
||||
public function changeSelection(change:Int = 0, force:Bool = false)
|
||||
{
|
||||
public function changeSelection(change:Int = 0, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
|
||||
var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
|
||||
var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change));
|
||||
if (event.cancelled) return;
|
||||
|
||||
curSelected = event.value;
|
||||
if (event.playMenuSFX) CoolUtil.playMenuSFX(SCROLL, 0.7);
|
||||
|
||||
var prevDiff = curDifficulties[curDifficulty], prevVariant = curDiffMetaKeys[curDifficulty];
|
||||
updateCurDifficulties();
|
||||
|
||||
for (i => diff in curDifficulties) if (diff == prevDiff && curDiffMetaKeys[i] == prevVariant) {
|
||||
curDifficulty = i;
|
||||
break;
|
||||
}
|
||||
|
||||
changeDiff(0, true);
|
||||
|
||||
#if PRELOAD_ALL
|
||||
@@ -440,7 +471,7 @@ class FreeplayState extends MusicBeatState
|
||||
songInstPlaying = false;
|
||||
#end
|
||||
|
||||
coopText.visible = songs[curSelected].coopAllowed || songs[curSelected].opponentModeAllowed;
|
||||
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
|
||||
}
|
||||
|
||||
function updateOptionsAlpha() {
|
||||
@@ -465,6 +496,27 @@ class FreeplayState extends MusicBeatState
|
||||
item.alpha = selectedAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurDifficulties() {
|
||||
curDiffMetaKeys.resize(0);
|
||||
curDifficulties = songs[curSelected].difficulties.copy();
|
||||
for (i in 0...curDifficulties.length) curDiffMetaKeys.push(null);
|
||||
|
||||
if (songs[curSelected].variants != null) {
|
||||
var meta:ChartMetaData;
|
||||
for (variant in songs[curSelected].variants) if ((meta = songs[curSelected].metas.get(variant)) != null) {
|
||||
curDifficulties = curDifficulties.concat(meta.difficulties);
|
||||
for (i in 0...meta.difficulties.length) curDiffMetaKeys.push(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurSong() {
|
||||
var song = songs[curSelected];
|
||||
if (song == null) curSong = null;
|
||||
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
|
||||
curSong = song;
|
||||
}
|
||||
}
|
||||
|
||||
class FreeplaySonglist {
|
||||
@@ -485,7 +537,7 @@ class FreeplaySonglist {
|
||||
}
|
||||
if (songsFound == null) songsFound = Paths.getFolderDirectories("songs", false, source);
|
||||
if (songsFound.length > 0) {
|
||||
for (s in songsFound) songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS));
|
||||
for (s in songsFound) songs.push(Chart.loadChartMeta(s, source == MODS));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -167,7 +167,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||
case "Change Options":
|
||||
FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState())));
|
||||
case "Exit to charter":
|
||||
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
|
||||
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, PlayState.variation, false));
|
||||
case "Exit to menu":
|
||||
if (PlayState.chartingMode && Charter.undos.unsaved)
|
||||
game.saveWarn(false);
|
||||
|
||||
@@ -129,7 +129,7 @@ class TitleState extends MusicBeatState
|
||||
|
||||
if (pressedEnter && transitioning && skippedIntro) {
|
||||
FlxG.camera.stopFX();// FlxG.camera.visible = false;
|
||||
goToMainMenu();
|
||||
goToMainMenu(false);
|
||||
}
|
||||
|
||||
if (pressedEnter && !transitioning && skippedIntro)
|
||||
@@ -152,22 +152,24 @@ class TitleState extends MusicBeatState
|
||||
transitioning = true;
|
||||
// FlxG.sound.music.stop();
|
||||
|
||||
new FlxTimer().start(2, (_) -> goToMainMenu());
|
||||
new FlxTimer().start(2, (_) -> goToMainMenu(false));
|
||||
}
|
||||
|
||||
function goToMainMenu() {
|
||||
function goToMainMenu(force = true) {
|
||||
#if UPDATE_CHECKING
|
||||
var report = hasCheckedUpdates ? null : funkin.backend.system.updating.UpdateUtil.checkForUpdates();
|
||||
hasCheckedUpdates = true;
|
||||
if (!force && !Flags.DISABLE_AUTOUPDATER) {
|
||||
funkin.backend.system.updating.UpdateUtil.waitForUpdates(false, (report) -> {
|
||||
hasCheckedUpdates = true;
|
||||
if (FlxG.state != this) return;
|
||||
|
||||
if (report != null && report.newUpdate) {
|
||||
FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
|
||||
} else {
|
||||
if (!report.newUpdate) goToMainMenu(true);
|
||||
else FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
|
||||
}, true);
|
||||
}
|
||||
else
|
||||
#end {
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
#else
|
||||
FlxG.switchState(new MainMenuState());
|
||||
#end
|
||||
}
|
||||
|
||||
public function createCoolText(textArray:Array<String>)
|
||||
|
||||
@@ -37,6 +37,9 @@ final class AlphabetComponent {
|
||||
public var scaleX:Float;
|
||||
public var scaleY:Float;
|
||||
|
||||
public var flipX:Bool;
|
||||
public var flipY:Bool;
|
||||
|
||||
public var hasColorMode:Bool;
|
||||
public var colorMode:ColorMode;
|
||||
}
|
||||
@@ -346,9 +349,9 @@ class Alphabet extends FlxSprite {
|
||||
|
||||
_matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5);
|
||||
if (__useDrawScale)
|
||||
_matrix.scale(__drawScale.x, __drawScale.y);
|
||||
_matrix.scale(__drawScale.x * (__component.flipX ? -1 : 1), __drawScale.y * (__component.flipY ? -1 : 1));
|
||||
else
|
||||
_matrix.scale(__component.scaleX, __component.scaleY);
|
||||
_matrix.scale(__component.scaleX * (__component.flipX ? -1 : 1), __component.scaleY * (__component.flipY ? -1 : 1));
|
||||
if(__component.shouldRotate)
|
||||
_matrix.rotateWithTrig(__component.cos, __component.sin);
|
||||
_matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5);
|
||||
@@ -558,6 +561,9 @@ class Alphabet extends FlxSprite {
|
||||
cos: angleCos,
|
||||
sin: angleSin,
|
||||
|
||||
flipX: node.get("flipX") == "true",
|
||||
flipY: node.get("flipY") == "true",
|
||||
|
||||
hasColorMode: false,
|
||||
colorMode: 0
|
||||
}],
|
||||
@@ -593,6 +599,9 @@ class Alphabet extends FlxSprite {
|
||||
|
||||
var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode"));
|
||||
|
||||
var xFlip = component.get("flipX") == "true";
|
||||
var yFlip = component.get("flipY") == "true";
|
||||
|
||||
if (component.get("hasOutline") == "true") {
|
||||
components.insert(startIndex, {
|
||||
refIndex: componentsPushed,
|
||||
@@ -608,6 +617,9 @@ class Alphabet extends FlxSprite {
|
||||
cos: angleCos,
|
||||
sin: angleSin,
|
||||
|
||||
flipX: xFlip,
|
||||
flipY: yFlip,
|
||||
|
||||
hasColorMode: colorMode >= 0,
|
||||
colorMode: colorMode,
|
||||
});
|
||||
@@ -628,6 +640,9 @@ class Alphabet extends FlxSprite {
|
||||
cos: angleCos,
|
||||
sin: angleSin,
|
||||
|
||||
flipX: xFlip,
|
||||
flipY: yFlip,
|
||||
|
||||
hasColorMode: colorMode >= 0,
|
||||
colorMode: colorMode,
|
||||
});
|
||||
@@ -659,6 +674,9 @@ class Alphabet extends FlxSprite {
|
||||
var yScale:Float = Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0);
|
||||
var advance:Float = Std.parseFloat(node.get("advance"));
|
||||
|
||||
var xFlip = node.get("flipX") == "true";
|
||||
var yFlip = node.get("flipY") == "true";
|
||||
|
||||
var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode"));
|
||||
|
||||
var components:Array<AlphabetComponent> = [];
|
||||
@@ -677,6 +695,9 @@ class Alphabet extends FlxSprite {
|
||||
cos: angleCos,
|
||||
sin: angleSin,
|
||||
|
||||
flipX: xFlip,
|
||||
flipY: yFlip,
|
||||
|
||||
hasColorMode: colorMode >= 0,
|
||||
colorMode: colorMode,
|
||||
});
|
||||
@@ -696,6 +717,9 @@ class Alphabet extends FlxSprite {
|
||||
cos: angleCos,
|
||||
sin: angleSin,
|
||||
|
||||
flipX: xFlip,
|
||||
flipY: yFlip,
|
||||
|
||||
hasColorMode: colorMode >= 0,
|
||||
colorMode: colorMode,
|
||||
});
|
||||
@@ -758,7 +782,7 @@ class Alphabet extends FlxSprite {
|
||||
}
|
||||
|
||||
private static var alphabetProperties:Array<String> = ["fps", "advance", "lineGap", "forceCasing", "useColorOffsets", "antialiasing"];
|
||||
private static var componentProperties:Array<String> = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY"];
|
||||
private static var componentProperties:Array<String> = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY", "flipX", "flipY"];
|
||||
|
||||
public function buildXML():Xml {
|
||||
var xml = Xml.createElement("alphabet");
|
||||
|
||||
@@ -73,6 +73,7 @@ class Options
|
||||
*/
|
||||
public static var freeplayLastSong:String = null;
|
||||
public static var freeplayLastDifficulty:String = "normal";
|
||||
public static var freeplayLastVariation:String = null;
|
||||
public static var contributors:Array<funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor> = [];
|
||||
public static var mainDevs:Array<Int> = []; // IDs
|
||||
public static var lastUpdated:Null<Float>;
|
||||
@@ -117,6 +118,7 @@ class Options
|
||||
public static var P1_ACCEPT:Array<FlxKey> = [ENTER];
|
||||
public static var P1_BACK:Array<FlxKey> = [BACKSPACE];
|
||||
public static var P1_PAUSE:Array<FlxKey> = [ENTER];
|
||||
public static var P1_CHANGE_MODE:Array<FlxKey> = [TAB];
|
||||
|
||||
// Misc
|
||||
public static var P1_RESET:Array<FlxKey> = [R];
|
||||
@@ -148,6 +150,7 @@ class Options
|
||||
public static var P2_ACCEPT:Array<FlxKey> = [SPACE];
|
||||
public static var P2_BACK:Array<FlxKey> = [ESCAPE];
|
||||
public static var P2_PAUSE:Array<FlxKey> = [ESCAPE];
|
||||
public static var P2_CHANGE_MODE:Array<FlxKey> = [];
|
||||
|
||||
// Misc
|
||||
public static var P2_RESET:Array<FlxKey> = [];
|
||||
@@ -179,6 +182,7 @@ class Options
|
||||
public static var SOLO_ACCEPT(get, null):Array<FlxKey>;
|
||||
public static var SOLO_BACK(get, null):Array<FlxKey>;
|
||||
public static var SOLO_PAUSE(get, null):Array<FlxKey>;
|
||||
public static var SOLO_CHANGE_MODE(get, null):Array<FlxKey>;
|
||||
|
||||
// Misc
|
||||
public static var SOLO_RESET(get, null):Array<FlxKey>;
|
||||
|
||||
@@ -151,6 +151,11 @@ class OptionsMenu extends TreeMenu {
|
||||
var options:Array<FlxSprite> = [];
|
||||
|
||||
for(node in xml.elements) {
|
||||
switch(node.name) {
|
||||
case "separator":
|
||||
options.push(new Separator(node.has.height ? Std.parseFloat(node.att.height) : 67));
|
||||
}
|
||||
|
||||
if (!node.has.name) {
|
||||
Logs.warn("An option node requires a name attribute.");
|
||||
continue;
|
||||
@@ -165,7 +170,6 @@ class OptionsMenu extends TreeMenu {
|
||||
continue;
|
||||
}
|
||||
options.push(new Checkbox(name, desc, node.att.id, null, FlxG.save.data));
|
||||
|
||||
case "number":
|
||||
if (!node.has.id) {
|
||||
Logs.warn("A number option requires an \"id\" for option saving.");
|
||||
@@ -195,7 +199,7 @@ class OptionsMenu extends TreeMenu {
|
||||
continue;
|
||||
}
|
||||
var v:Dynamic = Std.parseFloat(node.att.value);
|
||||
options.push(new RadioButton(screen, name, desc, node.att.id, v != null ? v : node.att.value, null, FlxG.save.data, node.att.forId));
|
||||
options.push(new RadioButton(screen, name, desc, node.att.id, v != null ? v : node.att.value, null, FlxG.save.data, node.has.forId ? node.att.forId : null));
|
||||
case 'slider':
|
||||
if (!node.has.id) {
|
||||
Logs.warn("A slider option requires an \"id\" for option saving.");
|
||||
@@ -204,10 +208,6 @@ class OptionsMenu extends TreeMenu {
|
||||
var step = node.has.change ? Std.parseFloat(node.att.change) : (node.has.step ? Std.parseFloat(node.att.step) : null);
|
||||
var segments = node.has.segments ? Std.parseInt(node.att.segments) : 5;
|
||||
options.push(new SliderOption(name, desc, Std.parseFloat(node.att.min), Std.parseFloat(node.att.max), step, segments, node.att.id, Std.parseInt(node.att.barWidth), null, FlxG.save.data));
|
||||
|
||||
case 'separator':
|
||||
options.push(new Separator(Std.parseInt(node.att.height)));
|
||||
|
||||
case "menu":
|
||||
options.push(new TextOption(name, desc, ' >', () -> {
|
||||
var screen = new TreeMenuScreen(name, desc);
|
||||
|
||||
@@ -128,11 +128,12 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
var i = curSelected, y = initY, object:FlxSprite = null, itemHeight:Float = 0;
|
||||
|
||||
inline function updateItem() {
|
||||
object.y = CoolUtil.fpsLerp(object.y, y - (itemHeight = object.height) * 0.5, r);
|
||||
object.y = CoolUtil.fpsLerp(object.y, y - itemHeight * 0.5, r);
|
||||
object.x = x + 100 - Math.pow(Math.abs((object.y - (FlxG.height - itemHeight) * 0.5) / itemHeight / FlxG.height * FlxG.initialHeight), 1.6) * 15;
|
||||
}
|
||||
|
||||
while (i < length) if ((object = members[i++]) != null) {
|
||||
itemHeight = object.height;
|
||||
updateItem();
|
||||
y += itemHeight;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
y = initY;
|
||||
i = curSelected;
|
||||
while (i-- > 0) if ((object = members[i]) != null) {
|
||||
y -= itemHeight;
|
||||
y -= (itemHeight = object.height);
|
||||
updateItem();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,4 @@ class AdvancedAppearanceOptions extends TreeMenuScreen {
|
||||
private function __changeAntialiasing() {
|
||||
FlxG.game.stage.quality = (FlxG.enableAntialiasing = Options.antialiasing) ? BEST : LOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class MiscOptions extends TreeMenuScreen {
|
||||
#if UPDATE_CHECKING
|
||||
add(new Checkbox(getNameID('betaUpdates'), getDescID('betaUpdates'), 'betaUpdates'));
|
||||
add(new TextOption(getNameID('checkForUpdates'), getDescID('checkForUpdates'), () -> {
|
||||
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
|
||||
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates(true);
|
||||
if (report.newUpdate) FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
|
||||
else {
|
||||
CoolUtil.playMenuSFX(CANCEL);
|
||||
|
||||
@@ -97,17 +97,21 @@ class KeybindsOptions extends MusicBeatSubstate {
|
||||
name: translate("ui.pause"),
|
||||
control: 'PAUSE'
|
||||
},
|
||||
{
|
||||
name: translate("ui.changeMode"),
|
||||
control: 'CHANGE_MODE'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: translate("category.volume"),
|
||||
settings: [
|
||||
{
|
||||
name: translate("up"),
|
||||
name: translate("volume.up"),
|
||||
control: 'VOLUME_UP'
|
||||
},
|
||||
{
|
||||
name: translate("down"),
|
||||
name: translate("volume.down"),
|
||||
control: 'VOLUME_DOWN'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -16,10 +16,9 @@ class IconOption extends TextOption {
|
||||
|
||||
iconSpr = new HealthIcon(icon, false);
|
||||
iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2);
|
||||
iconSpr.setUnstretchedGraphicSize(150, 150, true);
|
||||
iconSpr.updateHitbox();
|
||||
iconSpr.sprTracker = __text;
|
||||
iconSpr.sprTrackerAlignment = LEFT;
|
||||
if (Math.max(iconSpr.width, iconSpr.height) > 150) iconSpr.setUnstretchedGraphicSize(150, 150);
|
||||
add(iconSpr);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class NewOption extends TextOption {
|
||||
|
||||
public function new(name:String, desc:String, callback:Void->Void) {
|
||||
super(name, desc, callback);
|
||||
itemHeight = 160;
|
||||
itemHeight = 150;
|
||||
|
||||
__text.color = FlxColor.LIME;
|
||||
__text.x = 100;
|
||||
|
||||
@@ -6,7 +6,7 @@ import flixel.FlxBasic;
|
||||
|
||||
class Separator extends FlxSprite {
|
||||
var separatorHeight:Float;
|
||||
public function new(height = 67) {
|
||||
public function new(height = 67.0) {
|
||||
super();
|
||||
separatorHeight = height;
|
||||
}
|
||||
|
||||
@@ -3,25 +3,24 @@ package funkin.savedata;
|
||||
import flixel.util.FlxSave;
|
||||
import lime.app.Application;
|
||||
import openfl.Lib;
|
||||
import haxe.Serializer;
|
||||
import haxe.Unserializer;
|
||||
|
||||
/**
|
||||
* Class used for saves WITHOUT going through the struggle of type checks
|
||||
* Just add your save variables the way you would do in the Options.hx file.
|
||||
* The macro will automatically generate the `flush` and `load` functions.
|
||||
*/
|
||||
@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "flush", "load"))
|
||||
@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "__flush", "__load"))
|
||||
class FunkinSave {
|
||||
public static var highscores:Map<HighscoreEntry, SongScore> = [];
|
||||
|
||||
@:doNotSave public static var highscores:Map<HighscoreEntry, SongScore> = [];
|
||||
|
||||
/**
|
||||
* ONLY OPEN IF YOU WANT TO EDIT FUNCTIONS RELATED TO SAVING, LOADING OR HIGHSCORES.
|
||||
*/
|
||||
#if REGION
|
||||
@:dox(hide) @:doNotSave
|
||||
private static var __eventAdded = false;
|
||||
@:doNotSave
|
||||
public static var save:FlxSave;
|
||||
@:dox(hide) @:doNotSave private static var __eventAdded = false;
|
||||
@:doNotSave public static var save:FlxSave;
|
||||
|
||||
public static function init() {
|
||||
var path = Flags.SAVE_PATH, name = Flags.SAVE_NAME;
|
||||
@@ -46,20 +45,64 @@ class FunkinSave {
|
||||
}
|
||||
}
|
||||
|
||||
public static function load() {
|
||||
__load();
|
||||
if (save.data.highscores != null) {
|
||||
var temp;
|
||||
for (entryData in Reflect.fields(save.data.highscores))
|
||||
if ((temp = __getHighscoreEntry(entryData)) != null && Reflect.field(save.data.highscores, entryData) != null)
|
||||
highscores.set(temp, Reflect.field(save.data.highscores, entryData));
|
||||
}
|
||||
}
|
||||
|
||||
public static function flush() {
|
||||
if (save.data.highscores == null) save.data.highscores = {};
|
||||
for (entry => score in highscores) Reflect.setField(save.data.highscores, __formatHighscoreEntry(entry), score);
|
||||
__flush();
|
||||
}
|
||||
|
||||
static function __getHighscoreEntry(data:String):HighscoreEntry {
|
||||
try {
|
||||
var d = Unserializer.run(data);
|
||||
if (d.song is String)
|
||||
return HSongEntry(d.song, d.diff, d.variation, d.changes);
|
||||
else if (d.week is String)
|
||||
return HWeekEntry(d.week, d.diff);
|
||||
}
|
||||
catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function __formatHighscoreEntry(entry:HighscoreEntry):String {
|
||||
switch (entry) {
|
||||
case HWeekEntry(weekName, difficulty):
|
||||
return Serializer.run({week: weekName, diff: difficulty});
|
||||
case HSongEntry(songName, difficulty, variation, changes):
|
||||
var d:Dynamic = {
|
||||
song: songName,
|
||||
diff: difficulty,
|
||||
changes: changes
|
||||
};
|
||||
if (variation != null && variation != '') d.variation = variation;
|
||||
return Serializer.run(d);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the high-score for a song.
|
||||
* @param name Song name
|
||||
* @param diff Song difficulty
|
||||
* @param changes Changes made to that song in freeplay.
|
||||
*/
|
||||
public static inline function getSongHighscore(name:String, diff:String, ?changes:Array<HighscoreChange>) {
|
||||
public static inline function getSongHighscore(name:String, diff:String, ?variation:String, ?changes:Array<HighscoreChange>) {
|
||||
if (changes == null) changes = [];
|
||||
return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes));
|
||||
return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes));
|
||||
}
|
||||
|
||||
public static inline function setSongHighscore(name:String, diff:String, highscore:SongScore, ?changes:Array<HighscoreChange>) {
|
||||
public static inline function setSongHighscore(name:String, diff:String, ?variation:String, highscore:SongScore, ?changes:Array<HighscoreChange>) {
|
||||
if (changes == null) changes = [];
|
||||
if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes), highscore)) {
|
||||
if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes), highscore)) {
|
||||
flush();
|
||||
return true;
|
||||
}
|
||||
@@ -103,7 +146,7 @@ class FunkinSave {
|
||||
|
||||
enum HighscoreEntry {
|
||||
HWeekEntry(weekName:String, difficulty:String);
|
||||
HSongEntry(songName:String, difficulty:String, changes:Array<HighscoreChange>);
|
||||
HSongEntry(songName:String, difficulty:String, variation:Null<String>, changes:Array<HighscoreChange>);
|
||||
}
|
||||
|
||||
enum HighscoreChange {
|
||||
|
||||
@@ -7,6 +7,10 @@ class Config {
|
||||
"flixel",
|
||||
|
||||
"funkin",
|
||||
#if MODCHARTING_FEATURES
|
||||
"modchart.engine",
|
||||
"modchart.backend.standalone",
|
||||
#end
|
||||
#end
|
||||
];
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ import lime.utils.ArrayBufferView;
|
||||
@:access(lime.media.AudioBuffer)
|
||||
@:access(lime.utils.ArrayBufferView)
|
||||
class NativeAudioSource {
|
||||
private static var STREAM_BUFFER_SAMPLES:Int = 0x1000;
|
||||
private static var STREAM_MAX_BUFFERS:Int = 32;
|
||||
private static var STREAM_BUFFER_SAMPLES:Int = 0x4000;
|
||||
private static var STREAM_MAX_BUFFERS:Int = 8;
|
||||
private static var STREAM_TIMER_FREQUENCY:Int = 100;
|
||||
private static var STREAM_BUFFER_FREQUENCY:Int = 6;
|
||||
private static var STREAM_BUFFER_FREQUENCY:Int = 3;
|
||||
|
||||
private var parent:AudioSource;
|
||||
private var disposed:Bool;
|
||||
@@ -161,9 +161,9 @@ class NativeAudioSource {
|
||||
}
|
||||
|
||||
private function complete() {
|
||||
completed = true;
|
||||
if (!completed) parent.onComplete.dispatch();
|
||||
stop();
|
||||
parent.onComplete.dispatch();
|
||||
completed = true;
|
||||
}
|
||||
|
||||
// Streaming, atleast for vorbis for now.
|
||||
|
||||
Reference in New Issue
Block a user