Author SHA1 Message Date
Ralty e8a185c404 Revert "Fix wmic (#642)"
This reverts commit 0ee90abe98.
2026-06-05 17:48:54 +07:00
SrtHero278 80ca43b46e shits break combo because read desc
this engine is spam and get away with it central like i struggled to hit a shit no joke

and i did get owner approval to do this
2026-05-11 19:44:59 -07:00
ADA_FunniandGitHub 8f0583958d Change the default value of "Multiplicative?" in the "Camera Zoom" event to be false. (#974) 2026-05-03 10:28:50 +07:00
Ralty f82c8d7fb7 Remove linux-selfhosted, no longer needed. 2026-04-07 10:44:25 +07:00
Ralty 2a413071cd Disable Streamed Audio for now, better late than broken 2026-04-07 10:41:35 +07:00
Ralty 3fa3fbda14 Fix CoolUtil.playMenuSong volume 2026-04-07 10:41:34 +07:00
Nebula S. NovaandGitHub cd7c9f1afc Fix Commit Number in Main Menu (#950)
* Update checkout action to fetch full history

* Update linux.yml

* Update macos.yml

* Update release.yml

* Update windows.yml
2026-04-04 20:44:30 +07:00
rufiusandGitHub e458e868a4 Add ability to set custom default alternate animation suffix (#882)
The alt. animation toggle event currently does not support setting the
suffix directly,
but that's outside of the scope of adding this feature at the moment.
2026-04-04 13:32:24 +07:00
HEIHUAaandGitHub 6f8f43d3dc Optimize Note & Strum Rendering (#941)
* optimize

* Delete the unnecessary

* Delete __posPoint

* 更新 Note.hx

* @:dox(hide)

* 更新 Strum.hx

* Fix the bug
2026-04-03 21:35:27 +07:00
BittoandGitHub a8a5ae7a89 Fix game crash and fix(?) == to != (#943) 2026-04-02 06:27:47 +07:00
BittoandGitHub e0b5369eb5 "Delete Stacked Notes" functionality for the chart editor (#935)
* Delete Stacked Notes

* unstack selection

* Attempts to translate to italian and polish

i do not know these languages so if they're wrong im sorry
2026-03-30 04:51:57 +07:00
BittoandGitHub d874ea0348 Fix an oversight with the alphabet getting the wrong width of an component (#937) 2026-03-30 04:49:11 +07:00
FrakitsandGitHub d80b7b95cf selfhosted linux workflow because ubuntu stinks 2026-03-29 13:49:08 +03:00
270ed6b9b5 Note.noteAngle rewrite, fixes minus scrollSpeed (#857)
* fix 90% of issues with noteAngle and scrollSpeed of notes

cool  , sustains now dont look like ass when rotated and negative scroll speed doesnt mess tem up anymore

although the consequence of this is that noteAngle will go the other way around if on downscroll but i cant rlly get myself to fix that ;-;

* downscroll is done (hopefully

---------

Co-authored-by: Ralty <78720179+Raltyro@users.noreply.github.com>
2026-03-29 13:36:16 +07:00
HEIHUAaandGitHub 39968efb19 (Charter) Add an ability to copy selected items by Alt-Dragging (#918) 2026-03-29 05:03:58 +07:00
HeroandGitHub 64ea9db1c1 EULER, smoothStep, inverseLerp, fract in MathUtil (#913) 2026-03-29 05:02:42 +07:00
ItsLJcoolandGitHub 5957a55b96 Add Text displaying if the user is running an Experimental Version of CodenameEngine (#933)
* cmon github actions, do something

* this will make it easier to know if a user is running an experimental version :trollface:
2026-03-29 04:18:46 +07:00
Ralty 3266e22562 Correctly change the library rerefences 2026-03-28 17:41:53 +07:00
ItsLJcoolandGitHub a7b040152d Fixed / Refactored SysZip to actually work, and store compressed entries without uncompressing an entire zip (#761)
* well boys I fixed it. you can now have Zipped Mods!!!!!

* comment update

* quick fixes + optimizations

* fixed stream audio bug????

* found the issue ffs

* removing traces oops

* fixed videos from not being able to play. Inspired from VideoCutscene's fix for ZIP videos, and also removed that fix in favor for just fixing the issue entirely

* oop just in case no special characters that kill themself on other shit

* Implemented preloading every video (caching) when loading a ZipFolderLibrary.

Since videos have to be decompressed and be saved as a file anyways this reduces the time to do that, and what not.

* [UNTESTED] Allowed Zip Extensions for allowing any extension in the list to be passed as a `.zip`

* After some more testing loading Zip Addons works as well.

* Added the long awaited .cnemod file type!!!!! (it's just a folder with .cnemod with a .zip inside LMAO)

* Chalking up the Streamed Audio Bug to a mod issue, and also added capabilites to detect if a library is considered compressed

* Added warning to those who try to use the editor with compressed libraries

* ok instead of making it a .cnemod, the zip name will be `cnemod.zip` or what not.

* removing unnessesary traces oops

* clean Library accessor ig then

* fixed small issues and removed an idiot idea

* writing todos and fixing some context that left the room in comments

* Fixed an issue where if you had a file of 0 bytes (nothing) in a zipped library it would FUCKING CRASH the game

* Actually fixed the issue of decompressing 0 byte files

Also stopped saving the data for Folders in Zipped files, as they are 0 bytes anyways.

* Added the features that needed to exist, and removed a stupid line I saw in `ModsFolder` lol

For some reason animation offsets for characters are breaking and I can't tell if its my drive killing itself or cne so uh

* fix an issue that will force the game to crash if first ever mod being loaded is a ZipModLibrary
2026-03-15 14:24:47 +07:00
sillyborjaandGitHub a951e38dbb add "onPostEvent" call (#914)
* add preventSustainClip

* add "onPostEvent" call

idk why this wasn't here before but i added it
2026-03-01 21:19:37 +07:00
TheZoroForce240andGitHub 4fe3ba76e3 Charter/Editor additions and fixes (#793)
* camera highlights again

* option to mute all hitsounds

* context menus inside context menus and wip sliders in context menu

* wip volume slider stuff

* strumline preview fix with resizable editors

* more slider stuff

* editor sfx option should now actually work for everything

* fixed strumline vocals volume, fixed note type option keybinds and moved bookmarks and note types into submenus

* fix for saving difficulty specific meta json

* some extra misc changes

fixed camera highlights not updating when zooming in/out, optimized some of the highlight draw code so it doesn't do a full loop, and added the note scale flag into the strumline preview

* fix compiling after merge
2026-03-01 21:13:33 +07:00
FrakitsandGitHub f44d0192af my first ever commit in 2026
Removed the noCustomClass metadata from CancellableEvent.
2026-03-01 15:21:32 +02:00
viviandGitHub f8a06bf94c Improved text handling and keyboard input functionality for UITextBox (#900)
* Refactor UITextBox for improved text handling and keyboard input functionality

* use cut paste copy sounds
2026-03-01 16:25:19 +07:00
HEIHUAaandGitHub ec2a39995b optimize __notePerStrum (#901)
* Update StrumLine.hx

* Update StrumLine.hx

* Update StrumLine.hx

* Update StrumLine.hx
2026-03-01 16:25:02 +07:00
Ralty 3c15805c80 Switch to old library versions for this branch 2026-03-01 14:14:26 +07:00
Ralty 221d9dba1d Fork Flixel Animate to Fix frameOffsetAngle, layer 2026-03-01 14:06:24 +07:00
Mario HernandoandGitHub d87ac417f7 stress but cool (#911) 2026-02-28 09:44:24 +07:00
BittoandGitHub 3e084397a8 that was supposed to be an flxobject ok (#908)
* mouseOverlaps

* This makes more sense yeah

thinking of not even allowing disjointed overlap checks
keeping it for now

* oops forgot to define that var as flxcamera

* h
2026-02-20 20:15:53 +01:00
BittoandGitHub 370d5ede81 Better mouse overlapping function for CoolUtil (#813)
* mouseOverlaps

* This makes more sense yeah

thinking of not even allowing disjointed overlap checks
keeping it for now

* oops forgot to define that var as flxcamera
2026-02-20 19:10:05 +01:00
drfiffiandGitHub d7dbe5038d flixel-animate fix for cutscenes (#907)
* fixed flixel-animate name changes

* fixed guns

* fixed stress

still needs camera work
2026-02-20 16:19:39 +01:00
axelandGitHub 6c25431c75 Fix NaN check in RadioButton options, again (#899)
Comparing to NaN (even with itself) its always false, use Math.isNaN insted
2026-02-03 21:19:12 +07:00
💜 Rodney, An Imaginative Furball 💙andGitHub 37eb193e53 Fix Character uses one 'script' instead of multiple 'scripts' (#871) 2026-02-03 12:30:08 +07:00
ItsLJcoolandGitHub 0eeef88ac7 Re-implement onDraw functionality for FunkinSprite (#897)
As FlxAnimate didn't have the functionality before, so we have to do the check in FunkinSprite ourselves, as it itself stop's calling the `super.draw();` when it is a `isAnimate`
2026-02-03 12:28:11 +07:00
47a3526178 Allow string values for radio buttons in options menu (#896)
* Allow string values for radio buttons in options menu :D

NOT sure why this wasn't already a thing.... But it's here now

* Fix NaN check

---------

Co-authored-by: Ralty <78720179+Raltyro@users.noreply.github.com>
2026-02-03 12:21:09 +07:00
⍚~NexandGitHub bbb16f4c5c new shitty (thanks yosh :p) 2026-01-30 18:38:16 +01:00
axelandGitHub 5115067b49 Always use lastest FunkinModchart's release version (#895)
To avoid having to do PR every time I update FM, I suggest simply using the latest and most stable version (currently 1.2.5), which fixes many bugs, including a crash that occurred every single time you exited a song.
2026-01-30 17:30:40 +01:00
4d84e65cb5 flixel-animate implementation (#893)
* added flixel-animate to libs

* I BROKE THE OFFSETS FUCK

.

* yes it does blehtty wdym??

* Removed flxanimate entirely + Fixed transition + Fixed offsets

* Deleted caches (relaunching actions)

* frameOffset for atlases (need testing)

* useRenderTexture added to xml

* Fixed atlases for mods

* a simple redirect so old mods wont break that badly (#879)

* Update FunkinSprite.hx

* remove trace

* Fixed ghosts for atlases

* usable character editor (not entirely fixed)

* fixed zoomFactor + temp fix for atlas popping

* early structure for character editor atlas anim display

* zoomFactor for atlases

* Animate Atlas character editor previews

* FlxAnimateSettings specification

* fixed animation swapping on atlas characters

* OH YEA BABY NEW FLXANIMATE UPDATE THAT MADE CODE 50X SIMPLER **AGAIN** 💖 /pos

* Added label checkbox in Character Editor

* Fixed culling issues with FlxAnimate (fr this time)

* fixed some atlases

* HOW THE FUCK

* gf sad fix + AliasMacro (for backwards comp, manual impl for now)

* Fixed pop-ins

* Update Character.hx

* applyStageMatrix

* FlxAnimate pull request merge (empty commit)

* flixel-animate** i'm clumsy asf i meant the lib not the class name fuck

* Update libs.xml

* Merge changes from main (#892)

* repair ScriptableShader.shader = null (#877)

* optimize updateInput (#878)

* optimize updateInput

* resize optimize

* __notePerStrum optimize

* Update StrumLine.hx

* NoteGroup optimize (#881)

* NoteGroup optimize

* CoolUtil.bound faster

* makes commit with github desktop W FRAKITS

* Use fastSinCos for notes (#889)

* Update Strum.hx

* Update Strum.hx

---------

Co-authored-by: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Co-authored-by: Frakits <90983918+Frakits@users.noreply.github.com>

---------

Co-authored-by: Kolze <53307233+KolzeYT@users.noreply.github.com>
Co-authored-by: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com>
Co-authored-by: Frakits <90983918+Frakits@users.noreply.github.com>
2026-01-28 20:29:32 +01:00
HEIHUAaandGitHub f8f9d6eb4a Use fastSinCos for notes (#889)
* Update Strum.hx

* Update Strum.hx
2026-01-25 12:18:18 +01:00
Frakits 6d3243b256 Merge branch 'main' of github.com:CodenameCrew/CodenameEngine 2026-01-22 18:54:33 +02:00
Frakits 0cfb3e52c5 makes commit with github desktop W FRAKITS 2026-01-22 18:54:14 +02:00
123 changed files with 25525 additions and 228215 deletions
+3 -1
View File
@@ -12,6 +12,8 @@ jobs:
steps: steps:
- name: Pulling the new commit - name: Pulling the new commit
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setting up Haxe - name: Setting up Haxe
uses: krdlab/setup-haxe@v2 uses: krdlab/setup-haxe@v2
with: with:
@@ -35,7 +37,7 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game - name: Building the game
run: | run: |
haxelib run lime build linux haxelib run lime build linux -DCOMPILE_EXPERIMENTAL
# - name: Tar files # - name: Tar files
# run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin . # run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin .
- name: Uploading artifact (executable) - name: Uploading artifact (executable)
+3 -1
View File
@@ -12,6 +12,8 @@ jobs:
steps: steps:
- name: Pulling the new commit - name: Pulling the new commit
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setting up Haxe - name: Setting up Haxe
uses: krdlab/setup-haxe@v2 uses: krdlab/setup-haxe@v2
with: with:
@@ -32,7 +34,7 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s haxe -cp commandline -D analyzer-optimize --run Main setup -s
- name: Building the game - name: Building the game
run: | run: |
arch -x86_64 haxelib run lime build mac arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL
- name: Tar files - name: Tar files
run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin . run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin .
- name: Uploading artifact (executable) - name: Uploading artifact (executable)
+2
View File
@@ -22,6 +22,8 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download Windows Full Build - name: Download Windows Full Build
uses: dawidd6/action-download-artifact@v6 uses: dawidd6/action-download-artifact@v6
+3 -1
View File
@@ -12,6 +12,8 @@ jobs:
steps: steps:
- name: Pulling the new commit - name: Pulling the new commit
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setting up Haxe - name: Setting up Haxe
uses: krdlab/setup-haxe@v2 uses: krdlab/setup-haxe@v2
with: with:
@@ -32,7 +34,7 @@ jobs:
haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck
- name: Building the game - name: Building the game
run: | run: |
haxelib run lime build windows haxelib run lime build windows -DCOMPILE_EXPERIMENTAL
- name: Uploading artifact (executable) - name: Uploading artifact (executable)
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
+3 -3
View File
@@ -71,11 +71,11 @@ _**QOL = Quality of Life**_
- Characters are located in `data/weeks/characters`. - Characters are located in `data/weeks/characters`.
- Weeks are located in `data/weeks/weeks/` - Weeks are located in `data/weeks/weeks/`
- If you need to rearrange the weeks in-game, you can use the `data/weeks/weeks.txt` file. - If you need to rearrange the weeks in-game, you can use the `data/weeks/weeks.txt` file.
- Editors for Charts and Characters - Editors for Charts and Characters (Stage coming soon)
- Undos/Redos supported - Undos/Redos supported
- Warning on closing unsaved work - Warning on closing unsaved work.
- Clean UI (for ocd freaks) - Clean UI (for ocd freaks)
- Mature Chart editor - Mature Chart editor (Character editor rework soon)
- Features not found in other editors! - Features not found in other editors!
- Every single state & substate can be modified via HScript (`data/states/StateName.hx`) - 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.** - **Instances launched via `lime test windows` will automatically use assets from source.**
-7
View File
@@ -2,13 +2,6 @@
<character x="115" y="325" sprite="gf-christmas" icon="gf" color="#A5004D"> <character x="115" y="325" sprite="gf-christmas" icon="gf" color="#A5004D">
<anim name="danceLeft" anim="GF Dancing Beat" x="0" y="-9" fps="24" loop="false" indices="0..14"/> <anim name="danceLeft" anim="GF Dancing Beat" x="0" y="-9" fps="24" loop="false" indices="0..14"/>
<anim name="danceRight" anim="GF Dancing Beat" x="0" y="-9" fps="24" loop="false" indices="15..30"/> <anim name="danceRight" anim="GF Dancing Beat" x="0" y="-9" fps="24" loop="false" indices="15..30"/>
<anim name="singUP" anim="GF Up Note" x="0" y="-9" fps="24" loop="false"/>
<anim name="singDOWN" anim="GF Down Note" x="0" y="-9" fps="24" loop="false"/>
<anim name="singLEFT" anim="GF left note" x="0" y="-9" fps="24" loop="false"/>
<anim name="singRIGHT" anim="GF Right Note" x="0" y="-9" fps="24" loop="false"/>
<anim name="scared" anim="GF FEAR" x="0" y="-9" fps="24" loop="true"/>
<anim name="hairBlow" anim="GF Dancing Beat Hair blowing" x="0" y="-9" fps="24" loop="false" indices="0..3"/>
<anim name="hairFall" anim="GF Dancing Beat Hair Landing" x="0" y="-9" fps="24" loop="false" indices="0..11"/>
<anim name="cheer" anim="GF Cheer" x="0" y="-9" fps="24" loop="false"/> <anim name="cheer" anim="GF Cheer" x="0" y="-9" fps="24" loop="false"/>
<anim name="sad" anim="gf sad" x="-51" y="306" fps="24" loop="true" indices="0..12"/> <anim name="sad" anim="gf sad" x="-51" y="306" fps="24" loop="true" indices="0..12"/>
</character> </character>
+12 -12
View File
@@ -1,14 +1,14 @@
<!DOCTYPE codename-engine-character> <!DOCTYPE codename-engine-character>
<character x="100" y="275" sprite="gf" icon="gf" color="#A5004D"> <character x="100" y="275" color="#A5004D">
<anim name="cheer" anim="GF Cheer" x="0" y="0" fps="24" loop="false"/> <anim name="cheer" anim="GF Cheer"/>
<anim name="singUP" anim="GF Up Note" x="0" y="0" fps="24" loop="false"/> <anim name="singUP" anim="GF Up Note"/>
<anim name="singLEFT" anim="GF left note" x="0" y="0" fps="24" loop="false"/> <anim name="singLEFT" anim="GF left note"/>
<anim name="singRIGHT" anim="GF Right Note" x="0" y="0" fps="24" loop="false"/> <anim name="singRIGHT" anim="GF Right Note"/>
<anim name="singDOWN" anim="GF Down Note" x="0" y="0" fps="24" loop="false"/> <anim name="singDOWN" anim="GF Down Note"/>
<anim name="danceLeft" anim="GF Dancing Beat" x="0" y="0" fps="24" loop="false" indices="0..14"/> <anim name="danceLeft" anim="GF Dancing Beat" indices="0..14"/>
<anim name="danceRight" anim="GF Dancing Beat" x="0" y="0" fps="24" loop="false" indices="15..30"/> <anim name="danceRight" anim="GF Dancing Beat" indices="15..30"/>
<anim name="hairBlow" anim="GF Dancing Beat Hair blowing" x="0" y="0" fps="24" loop="true" indices="0,1,2,3"/> <anim name="hairBlow" anim="GF Dancing Beat Hair blowing" loop="true" indices="0..3"/>
<anim name="hairFall" anim="GF Dancing Beat Hair Landing" x="0" y="0" fps="24" loop="false" indices="0..11"/> <anim name="hairFall" anim="GF Dancing Beat Hair Landing" indices="0..11"/>
<anim name="scared" anim="GF FEAR" x="0" y="0" fps="24" loop="true"/> <anim name="scared" anim="GF FEAR " loop="true"/>
<anim name="sad" anim="gf sad" x="0" y="0" fps="24" loop="true" indices="0..12"/> <anim name="sad" anim="gf sad" x="-51" y="315" loop="true" indices="0..12"/>
</character> </character>
-3
View File
@@ -1,6 +1,3 @@
# THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT AND WHITELIST FREEPLAY SONGS
test test
tutorial tutorial
-8
View File
@@ -1,8 +0,0 @@
# THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT AND WHITELIST GAME MODES
solo
opponent
coop
coop-switched
botplay
-4
View File
@@ -1,4 +0,0 @@
{
"displayName": "Botplay Mode",
"modeID": "codename.botplay"
}
-2
View File
@@ -1,2 +0,0 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Botplay Mode" id="codename.botplay"/>
-5
View File
@@ -1,5 +0,0 @@
{
"displayName": "Co-Op Mode (Switched)",
"modeID": "codename.coop-opponent",
"scripts": ["opponent", "coop"]
}
-11
View File
@@ -1,11 +0,0 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Co-Op Mode (Switched)" id="codename.coop-opponent">
<script>coop</script>
<script>opponent</script>
<!--
just a small example of what you could do with fields!!
<field name="switchStrums" type="bool" val="true"/>
<field name="strumSize" type="float" val="0.6"/>
-->
</mode>
-4
View File
@@ -1,4 +0,0 @@
{
"displayName": "Co-Op Mode",
"modeID": "codename.coop"
}
-2
View File
@@ -1,2 +0,0 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Co-Op Mode" id="codename.coop"/>
-5
View File
@@ -1,5 +0,0 @@
solo
opponent
coop
coop-switched
botplay
-4
View File
@@ -1,4 +0,0 @@
{
"displayName": "Opponent Mode",
"modeID": "codename.opponent"
}
-2
View File
@@ -1,2 +0,0 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Opponent Mode" id="codename.opponent"/>
-4
View File
@@ -1,4 +0,0 @@
{
"displayName": "Solo Mode",
"modeID": "codename.solo"
}
-2
View File
@@ -1,2 +0,0 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Solo Mode" id="codename.solo"/>
+1 -1
View File
@@ -11,7 +11,7 @@
<sprite name="fgSnow" x="-600" y="680" sprite="fgSnow"/> <sprite name="fgSnow" x="-600" y="680" sprite="fgSnow"/>
<girlfriend x="540" y="110"/> <girlfriend x="440" y="110"/>
<dad/> <dad/>
<boyfriend x="1030" camyoffset="-150" camxoffset="-150"/> <boyfriend x="1030" camyoffset="-150" camxoffset="-150"/>
@@ -1,5 +1,5 @@
# THIS FILE ISN'T NECESSARY, # THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT AND WHITELIST WEEKS # BUT YOU CAN USE IT TO SORT WEEKS
tutorial tutorial
week1 week1
File diff suppressed because it is too large Load Diff
+26 -26
View File
@@ -1,28 +1,28 @@
{"ATLAS": {"SPRITES":[ {"ATLAS":{"SPRITES":[
{"SPRITE" : {"name": "0000","x":1636,"y":275,"w":183,"h":115,"rotated": false}}, {"SPRITE":{"name":"0","x":1662,"y":282,"w":194,"h":117,"rotated":false}},
{"SPRITE" : {"name": "0001","x":215,"y":360,"w":84,"h":140,"rotated": false}}, {"SPRITE":{"name":"1","x":1790,"y":417,"w":92,"h":136,"rotated":false}},
{"SPRITE" : {"name": "0002","x":419,"y":358,"w":251,"h":121,"rotated": false}}, {"SPRITE":{"name":"2","x":1193,"y":539,"w":259,"h":123,"rotated":false}},
{"SPRITE" : {"name": "0003","x":1636,"y":394,"w":120,"h":65,"rotated": false}}, {"SPRITE":{"name":"3","x":483,"y":553,"w":125,"h":68,"rotated":false}},
{"SPRITE" : {"name": "0004","x":4,"y":360,"w":108,"h":126,"rotated": false}}, {"SPRITE":{"name":"4","x":1662,"y":417,"w":110,"h":127,"rotated":false}},
{"SPRITE" : {"name": "0005","x":674,"y":358,"w":102,"h":147,"rotated": false}}, {"SPRITE":{"name":"5","x":1886,"y":0,"w":105,"h":149,"rotated":false}},
{"SPRITE" : {"name": "0006","x":116,"y":360,"w":95,"h":130,"rotated": false}}, {"SPRITE":{"name":"6","x":1886,"y":167,"w":97,"h":130,"rotated":false}},
{"SPRITE" : {"name": "0007","x":2038,"y":4,"w":1,"h":4,"rotated": false}}, {"SPRITE":{"name":"7","x":1398,"y":0,"w":470,"h":264,"rotated":false}},
{"SPRITE" : {"name": "0008","x":4,"y":4,"w":411,"h":352,"rotated": false}}, {"SPRITE":{"name":"8","x":867,"y":0,"w":513,"h":254,"rotated":false}},
{"SPRITE" : {"name": "0009","x":419,"y":4,"w":398,"h":350,"rotated": false}}, {"SPRITE":{"name":"9","x":0,"y":372,"w":465,"h":270,"rotated":false}},
{"SPRITE" : {"name": "0010","x":1303,"y":275,"w":329,"h":162,"rotated": false}}, {"SPRITE":{"name":"10","x":1900,"y":315,"w":101,"h":118,"rotated":false}},
{"SPRITE" : {"name": "0011","x":303,"y":360,"w":90,"h":103,"rotated": false}}, {"SPRITE":{"name":"11","x":867,"y":272,"w":308,"h":367,"rotated":false}},
{"SPRITE" : {"name": "0012","x":1823,"y":4,"w":211,"h":236,"rotated": false}}, {"SPRITE":{"name":"12","x":483,"y":371,"w":338,"h":164,"rotated":false}},
{"SPRITE" : {"name": "0013","x":1823,"y":244,"w":208,"h":235,"rotated": false}}, {"SPRITE":{"name":"13","x":0,"y":0,"w":422,"h":354,"rotated":false}},
{"SPRITE" : {"name": "0014","x":821,"y":258,"w":478,"h":262,"rotated": false}}, {"SPRITE":{"name":"14","x":440,"y":0,"w":409,"h":353,"rotated":false}},
{"SPRITE" : {"name": "0015","x":821,"y":4,"w":521,"h":250,"rotated": false}}, {"SPRITE":{"name":"15","x":1900,"y":451,"w":92,"h":103,"rotated":false}},
{"SPRITE" : {"name": "0016","x":1346,"y":4,"w":473,"h":267,"rotated": false}} {"SPRITE":{"name":"16","x":1193,"y":282,"w":218,"h":239,"rotated":false}},
]}, {"SPRITE":{"name":"17","x":1429,"y":282,"w":215,"h":237,"rotated":false}}]},
"meta": { "meta":{
"app": "Adobe Animate", "app":"Adobe Animate (Better TA Extension)",
"version": "22.0.5.191", "version":"24.0.2.12",
"image": "spritemap1.png", "image":"spritemap1.png",
"format": "RGBA8888", "format":"RGBA8888",
"size": {"w":2043,"h":524}, "size":{"w":2031,"h":662},
"resolution": "1" "resolution":"1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 134 KiB

File diff suppressed because it is too large Load Diff
@@ -1,58 +1,40 @@
{"ATLAS": {"SPRITES":[ {"ATLAS":{"SPRITES":[
{"SPRITE" : {"name": "0000","x":994,"y":1029,"w":187,"h":118,"rotated": false}}, {"SPRITE":{"name":"0","x":349,"y":867,"w":254,"h":136,"rotated":false}},
{"SPRITE" : {"name": "0001","x":1953,"y":524,"w":84,"h":140,"rotated": false}}, {"SPRITE":{"name":"1","x":1648,"y":328,"w":287,"h":363,"rotated":false}},
{"SPRITE" : {"name": "0002","x":471,"y":430,"w":258,"h":137,"rotated": false}}, {"SPRITE":{"name":"2","x":1571,"y":709,"w":185,"h":117,"rotated":false}},
{"SPRITE" : {"name": "0003","x":1300,"y":641,"w":122,"h":66,"rotated": false}}, {"SPRITE":{"name":"3","x":1686,"y":855,"w":86,"h":136,"rotated":false}},
{"SPRITE" : {"name": "0004","x":1825,"y":1078,"w":108,"h":126,"rotated": false}}, {"SPRITE":{"name":"4","x":349,"y":1021,"w":122,"h":68,"rotated":false}},
{"SPRITE" : {"name": "0005","x":360,"y":695,"w":103,"h":149,"rotated": false}}, {"SPRITE":{"name":"5","x":1774,"y":709,"w":110,"h":128,"rotated":false}},
{"SPRITE" : {"name": "0006","x":1945,"y":867,"w":95,"h":130,"rotated": false}}, {"SPRITE":{"name":"6","x":621,"y":867,"w":105,"h":148,"rotated":false}},
{"SPRITE" : {"name": "0007","x":2034,"y":4,"w":1,"h":4,"rotated": false}}, {"SPRITE":{"name":"7","x":1571,"y":844,"w":97,"h":131,"rotated":false}},
{"SPRITE" : {"name": "0008","x":943,"y":650,"w":309,"h":375,"rotated": false}}, {"SPRITE":{"name":"8","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0009","x":4,"y":4,"w":778,"h":422,"rotated": false}}, {"SPRITE":{"name":"9","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0010","x":786,"y":4,"w":761,"h":393,"rotated": false}}, {"SPRITE":{"name":"10","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0011","x":1551,"y":4,"w":411,"h":352,"rotated": false}}, {"SPRITE":{"name":"11","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0012","x":1551,"y":360,"w":398,"h":350,"rotated": false}}, {"SPRITE":{"name":"12","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0013","x":4,"y":1028,"w":329,"h":162,"rotated": false}}, {"SPRITE":{"name":"13","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0014","x":1945,"y":1001,"w":90,"h":103,"rotated": false}}, {"SPRITE":{"name":"14","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0015","x":1300,"y":401,"w":211,"h":236,"rotated": false}}, {"SPRITE":{"name":"15","x":800,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0016","x":782,"y":1029,"w":208,"h":235,"rotated": false}}, {"SPRITE":{"name":"16","x":1363,"y":1,"w":543,"h":308,"rotated":false}},
{"SPRITE" : {"name": "0017","x":782,"y":910,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"17","x":1,"y":444,"w":771,"h":404,"rotated":false}},
{"SPRITE" : {"name": "0018","x":994,"y":1151,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"18","x":791,"y":700,"w":302,"h":369,"rotated":false}},
{"SPRITE" : {"name": "0019","x":1937,"y":1108,"w":103,"h":83,"rotated": false}}, {"SPRITE":{"name":"19","x":1111,"y":957,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0020","x":1115,"y":1151,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"20","x":1790,"y":855,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0021","x":1654,"y":714,"w":287,"h":360,"rotated": false}}, {"SPRITE":{"name":"21","x":1790,"y":959,"w":106,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0022","x":471,"y":571,"w":179,"h":67,"rotated": false}}, {"SPRITE":{"name":"22","x":1249,"y":957,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0023","x":654,"y":571,"w":118,"h":75,"rotated": false}}, {"SPRITE":{"name":"23","x":0,"y":0,"w":781,"h":425,"rotated":false}},
{"SPRITE" : {"name": "0024","x":1966,"y":223,"w":55,"h":112,"rotated": false}}, {"SPRITE":{"name":"24","x":0,"y":867,"w":331,"h":164,"rotated":false}},
{"SPRITE" : {"name": "0025","x":1966,"y":4,"w":64,"h":106,"rotated": false}}, {"SPRITE":{"name":"25","x":799,"y":328,"w":413,"h":354,"rotated":false}},
{"SPRITE" : {"name": "0026","x":1185,"y":1029,"w":67,"h":70,"rotated": false}}, {"SPRITE":{"name":"26","x":1230,"y":328,"w":400,"h":353,"rotated":false}},
{"SPRITE" : {"name": "0027","x":337,"y":1182,"w":122,"h":62,"rotated": false}}, {"SPRITE":{"name":"27","x":1387,"y":954,"w":90,"h":103,"rotated":false}},
{"SPRITE" : {"name": "0028","x":1647,"y":1078,"w":174,"h":97,"rotated": false}}, {"SPRITE":{"name":"28","x":1111,"y":700,"w":213,"h":239,"rotated":false}},
{"SPRITE" : {"name": "0029","x":360,"y":870,"w":82,"h":14,"rotated": false}}, {"SPRITE":{"name":"29","x":1342,"y":699,"w":211,"h":237,"rotated":false}}]},
{"SPRITE" : {"name": "0030","x":1647,"y":1179,"w":128,"h":61,"rotated": false}}, "meta":{
{"SPRITE" : {"name": "0031","x":463,"y":1182,"w":124,"h":58,"rotated": false}}, "app":"Adobe Animate (Better TA Extension)",
{"SPRITE" : {"name": "0032","x":1966,"y":114,"w":59,"h":105,"rotated": false}}, "version":"24.0.2.12",
{"SPRITE" : {"name": "0033","x":1953,"y":360,"w":87,"h":160,"rotated": false}}, "image":"spritemap1.png",
{"SPRITE" : {"name": "0034","x":1945,"y":714,"w":92,"h":149,"rotated": false}}, "format":"RGBA8888",
{"SPRITE" : {"name": "0035","x":1426,"y":641,"w":104,"h":42,"rotated": false}}, "size":{"w":1965,"h":1089},
{"SPRITE" : {"name": "0036","x":1953,"y":692,"w":73,"h":18,"rotated": false}}, "resolution":"1"
{"SPRITE" : {"name": "0037","x":1426,"y":687,"w":72,"h":19,"rotated": false}},
{"SPRITE" : {"name": "0038","x":360,"y":848,"w":72,"h":18,"rotated": false}},
{"SPRITE" : {"name": "0039","x":1953,"y":668,"w":73,"h":20,"rotated": false}},
{"SPRITE" : {"name": "0040","x":471,"y":650,"w":468,"h":256,"rotated": false}},
{"SPRITE" : {"name": "0041","x":786,"y":401,"w":510,"h":245,"rotated": false}},
{"SPRITE" : {"name": "0042","x":4,"y":430,"w":463,"h":261,"rotated": false}},
{"SPRITE" : {"name": "0043","x":360,"y":910,"w":418,"h":268,"rotated": false}},
{"SPRITE" : {"name": "0044","x":1256,"y":1008,"w":387,"h":271,"rotated": false}},
{"SPRITE" : {"name": "0045","x":1256,"y":714,"w":394,"h":290,"rotated": false}},
{"SPRITE" : {"name": "0046","x":4,"y":695,"w":352,"h":329,"rotated": false}}
]},
"meta": {
"app": "Adobe Animate",
"version": "22.0.5.191",
"image": "spritemap1.png",
"format": "RGBA8888",
"size": {"w":2044,"h":1283},
"resolution": "1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 453 KiB

File diff suppressed because it is too large Load Diff
+53 -55
View File
@@ -1,57 +1,55 @@
{"ATLAS": {"SPRITES":[ {"ATLAS":{"SPRITES":[
{"SPRITE" : {"name": "0000","x":1623,"y":602,"w":287,"h":360,"rotated": false}}, {"SPRITE":{"name":"0","x":1129,"y":977,"w":86,"h":17,"rotated":false}},
{"SPRITE" : {"name": "0001","x":1310,"y":269,"w":309,"h":375,"rotated": false}}, {"SPRITE":{"name":"1","x":1690,"y":1012,"w":86,"h":17,"rotated":false}},
{"SPRITE" : {"name": "0002","x":4,"y":4,"w":420,"h":359,"rotated": false}}, {"SPRITE":{"name":"2","x":1656,"y":852,"w":126,"h":64,"rotated":false}},
{"SPRITE" : {"name": "0003","x":428,"y":4,"w":406,"h":358,"rotated": false}}, {"SPRITE":{"name":"3","x":1915,"y":841,"w":121,"h":77,"rotated":false}},
{"SPRITE" : {"name": "0004","x":4,"y":639,"w":336,"h":165,"rotated": false}}, {"SPRITE":{"name":"4","x":1382,"y":980,"w":131,"h":64,"rotated":false}},
{"SPRITE" : {"name": "0005","x":125,"y":808,"w":90,"h":103,"rotated": false}}, {"SPRITE":{"name":"5","x":1132,"y":283,"w":420,"h":270,"rotated":false}},
{"SPRITE" : {"name": "0006","x":1819,"y":4,"w":215,"h":242,"rotated": false}}, {"SPRITE":{"name":"6","x":1570,"y":277,"w":389,"h":273,"rotated":false}},
{"SPRITE" : {"name": "0007","x":1217,"y":648,"w":213,"h":240,"rotated": false}}, {"SPRITE":{"name":"7","x":0,"y":743,"w":397,"h":292,"rotated":false}},
{"SPRITE" : {"name": "0008","x":606,"y":660,"w":191,"h":120,"rotated": false}}, {"SPRITE":{"name":"8","x":431,"y":267,"w":354,"h":332,"rotated":false}},
{"SPRITE" : {"name": "0009","x":606,"y":784,"w":90,"h":151,"rotated": false}}, {"SPRITE":{"name":"9","x":1592,"y":568,"w":190,"h":117,"rotated":false}},
{"SPRITE" : {"name": "0010","x":344,"y":660,"w":258,"h":124,"rotated": false}}, {"SPRITE":{"name":"10","x":1444,"y":826,"w":86,"h":136,"rotated":false}},
{"SPRITE" : {"name": "0011","x":448,"y":859,"w":122,"h":67,"rotated": false}}, {"SPRITE":{"name":"11","x":723,"y":852,"w":260,"h":123,"rotated":false}},
{"SPRITE" : {"name": "0012","x":1914,"y":755,"w":108,"h":126,"rotated": false}}, {"SPRITE":{"name":"12","x":1548,"y":947,"w":124,"h":68,"rotated":false}},
{"SPRITE" : {"name": "0013","x":1914,"y":602,"w":103,"h":149,"rotated": false}}, {"SPRITE":{"name":"13","x":1001,"y":852,"w":110,"h":128,"rotated":false}},
{"SPRITE" : {"name": "0014","x":700,"y":784,"w":95,"h":130,"rotated": false}}, {"SPRITE":{"name":"14","x":1933,"y":0,"w":106,"h":148,"rotated":false}},
{"SPRITE" : {"name": "0015","x":2038,"y":4,"w":1,"h":4,"rotated": false}}, {"SPRITE":{"name":"15","x":1129,"y":828,"w":97,"h":131,"rotated":false}},
{"SPRITE" : {"name": "0016","x":838,"y":253,"w":468,"h":256,"rotated": false}}, {"SPRITE":{"name":"16","x":803,"y":283,"w":311,"h":369,"rotated":false}},
{"SPRITE" : {"name": "0017","x":838,"y":4,"w":510,"h":245,"rotated": false}}, {"SPRITE":{"name":"17","x":1592,"y":703,"w":167,"h":101,"rotated":false}},
{"SPRITE" : {"name": "0018","x":1352,"y":4,"w":463,"h":261,"rotated": false}}, {"SPRITE":{"name":"18","x":1835,"y":852,"w":57,"h":105,"rotated":false}},
{"SPRITE" : {"name": "0019","x":4,"y":367,"w":418,"h":268,"rotated": false}}, {"SPRITE":{"name":"19","x":1977,"y":270,"w":66,"h":104,"rotated":false}},
{"SPRITE" : {"name": "0020","x":826,"y":513,"w":387,"h":271,"rotated": false}}, {"SPRITE":{"name":"20","x":1910,"y":936,"w":70,"h":73,"rotated":false}},
{"SPRITE" : {"name": "0021","x":428,"y":366,"w":394,"h":290,"rotated": false}}, {"SPRITE":{"name":"21","x":1244,"y":828,"w":182,"h":69,"rotated":false}},
{"SPRITE" : {"name": "0022","x":1623,"y":269,"w":352,"h":329,"rotated": false}}, {"SPRITE":{"name":"22","x":1690,"y":934,"w":127,"h":60,"rotated":false}},
{"SPRITE" : {"name": "0023","x":1143,"y":788,"w":67,"h":70,"rotated": false}}, {"SPRITE":{"name":"23","x":418,"y":617,"w":287,"h":363,"rotated":false}},
{"SPRITE" : {"name": "0024","x":1033,"y":875,"w":122,"h":62,"rotated": false}}, {"SPRITE":{"name":"24","x":1244,"y":915,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0025","x":1217,"y":513,"w":64,"h":106,"rotated": false}}, {"SPRITE":{"name":"25","x":1777,"y":748,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0026","x":1979,"y":359,"w":55,"h":112,"rotated": false}}, {"SPRITE":{"name":"26","x":1933,"y":166,"w":106,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0027","x":799,"y":788,"w":98,"h":118,"rotated": false}}, {"SPRITE":{"name":"27","x":1915,"y":737,"w":120,"h":86,"rotated":false}},
{"SPRITE" : {"name": "0028","x":1979,"y":250,"w":59,"h":105,"rotated": false}}, {"SPRITE":{"name":"28","x":1800,"y":568,"w":89,"h":162,"rotated":false}},
{"SPRITE" : {"name": "0029","x":1434,"y":749,"w":87,"h":160,"rotated": false}}, {"SPRITE":{"name":"29","x":1907,"y":568,"w":95,"h":151,"rotated":false}},
{"SPRITE" : {"name": "0030","x":1525,"y":749,"w":92,"h":149,"rotated": false}}, {"SPRITE":{"name":"30","x":1977,"y":392,"w":61,"h":107,"rotated":false}},
{"SPRITE" : {"name": "0031","x":219,"y":887,"w":104,"h":42,"rotated": false}}, {"SPRITE":{"name":"31","x":415,"y":998,"w":107,"h":45,"rotated":false}},
{"SPRITE" : {"name": "0032","x":527,"y":811,"w":73,"h":18,"rotated": false}}, {"SPRITE":{"name":"32","x":727,"y":993,"w":77,"h":22,"rotated":false}},
{"SPRITE" : {"name": "0033","x":527,"y":788,"w":72,"h":19,"rotated": false}}, {"SPRITE":{"name":"33","x":634,"y":998,"w":75,"h":23,"rotated":false}},
{"SPRITE" : {"name": "0034","x":527,"y":833,"w":72,"h":18,"rotated": false}}, {"SPRITE":{"name":"34","x":822,"y":993,"w":75,"h":22,"rotated":false}},
{"SPRITE" : {"name": "0035","x":1217,"y":623,"w":73,"h":20,"rotated": false}}, {"SPRITE":{"name":"35","x":540,"y":998,"w":76,"h":24,"rotated":false}},
{"SPRITE" : {"name": "0036","x":344,"y":788,"w":179,"h":67,"rotated": false}}, {"SPRITE":{"name":"36","x":1445,"y":0,"w":470,"h":259,"rotated":false}},
{"SPRITE" : {"name": "0037","x":219,"y":808,"w":118,"h":75,"rotated": false}}, {"SPRITE":{"name":"37","x":431,"y":0,"w":513,"h":249,"rotated":false}},
{"SPRITE" : {"name": "0038","x":901,"y":875,"w":128,"h":61,"rotated": false}}, {"SPRITE":{"name":"38","x":962,"y":0,"w":465,"h":265,"rotated":false}},
{"SPRITE" : {"name": "0039","x":1914,"y":885,"w":124,"h":58,"rotated": false}}, {"SPRITE":{"name":"39","x":723,"y":670,"w":331,"h":164,"rotated":false}},
{"SPRITE" : {"name": "0040","x":901,"y":788,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"40","x":0,"y":0,"w":413,"h":354,"rotated":false}},
{"SPRITE" : {"name": "0041","x":1022,"y":788,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"41","x":0,"y":372,"w":400,"h":353,"rotated":false}},
{"SPRITE" : {"name": "0042","x":341,"y":859,"w":103,"h":83,"rotated": false}}, {"SPRITE":{"name":"42","x":1548,"y":826,"w":90,"h":103,"rotated":false}},
{"SPRITE" : {"name": "0043","x":4,"y":808,"w":117,"h":83,"rotated": false}}, {"SPRITE":{"name":"43","x":1132,"y":571,"w":213,"h":239,"rotated":false}},
{"SPRITE" : {"name": "0044","x":1434,"y":648,"w":174,"h":97,"rotated": false}}, {"SPRITE":{"name":"44","x":1363,"y":571,"w":211,"h":237,"rotated":false}}]},
{"SPRITE" : {"name": "0045","x":1819,"y":250,"w":82,"h":14,"rotated": false}} "meta":{
]}, "app":"Adobe Animate (Better TA Extension)",
"meta": { "version":"24.0.2.12",
"app": "Adobe Animate", "image":"spritemap1.png",
"version": "21.0.1.37179", "format":"RGBA8888",
"image": "spritemap1.png", "size":{"w":2064,"h":1065},
"format": "RGBA8888", "resolution":"1"
"size": {"w":2043,"h":966},
"resolution": "1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 265 KiB

File diff suppressed because it is too large Load Diff
@@ -1,60 +1,60 @@
{"ATLAS": {"SPRITES":[ {"ATLAS":{"SPRITES":[
{"SPRITE" : {"name": "0000","x":4076,"y":2,"w":9,"h":12,"rotated": false}}, {"SPRITE":{"name":"0","x":2834,"y":332,"w":12,"h":15,"rotated":false}},
{"SPRITE" : {"name": "0001","x":1957,"y":2,"w":524,"h":501,"rotated": false}}, {"SPRITE":{"name":"1","x":1476,"y":532,"w":527,"h":503,"rotated":false}},
{"SPRITE" : {"name": "0002","x":716,"y":2,"w":721,"h":621,"rotated": false}}, {"SPRITE":{"name":"2","x":733,"y":0,"w":725,"h":623,"rotated":false}},
{"SPRITE" : {"name": "0003","x":2331,"y":505,"w":139,"h":109,"rotated": false}}, {"SPRITE":{"name":"3","x":0,"y":659,"w":741,"h":304,"rotated":false}},
{"SPRITE" : {"name": "0004","x":3244,"y":725,"w":303,"h":170,"rotated": false}}, {"SPRITE":{"name":"4","x":2311,"y":0,"w":349,"h":540,"rotated":false}},
{"SPRITE" : {"name": "0005","x":1691,"y":516,"w":248,"h":277,"rotated": false}}, {"SPRITE":{"name":"5","x":759,"y":641,"w":583,"h":373,"rotated":false}},
{"SPRITE" : {"name": "0006","x":2495,"y":843,"w":173,"h":229,"rotated": false}}, {"SPRITE":{"name":"6","x":2021,"y":0,"w":272,"h":702,"rotated":false}},
{"SPRITE" : {"name": "0007","x":2,"y":642,"w":262,"h":221,"rotated": false}}, {"SPRITE":{"name":"7","x":1476,"y":0,"w":518,"h":514,"rotated":false}},
{"SPRITE" : {"name": "0008","x":2331,"y":616,"w":118,"h":117,"rotated": false}}, {"SPRITE":{"name":"8","x":0,"y":0,"w":715,"h":641,"rotated":false}},
{"SPRITE" : {"name": "0009","x":3549,"y":865,"w":261,"h":139,"rotated": false}}, {"SPRITE":{"name":"9","x":619,"y":1032,"w":205,"h":122,"rotated":false}},
{"SPRITE" : {"name": "0010","x":3209,"y":897,"w":202,"h":122,"rotated": false}}, {"SPRITE":{"name":"10","x":842,"y":1032,"w":205,"h":122,"rotated":false}},
{"SPRITE" : {"name": "0011","x":580,"y":913,"w":202,"h":122,"rotated": false}}, {"SPRITE":{"name":"11","x":2311,"y":558,"w":115,"h":112,"rotated":false}},
{"SPRITE" : {"name": "0012","x":2831,"y":304,"w":45,"h":42,"rotated": false}}, {"SPRITE":{"name":"12","x":2801,"y":947,"w":305,"h":172,"rotated":false}},
{"SPRITE" : {"name": "0013","x":1957,"y":505,"w":372,"h":311,"rotated": false}}, {"SPRITE":{"name":"13","x":2678,"y":0,"w":375,"h":314,"rotated":false}},
{"SPRITE" : {"name": "0014","x":1439,"y":516,"w":250,"h":325,"rotated": false}}, {"SPRITE":{"name":"14","x":2870,"y":332,"w":252,"h":328,"rotated":false}},
{"SPRITE" : {"name": "0015","x":716,"y":625,"w":235,"h":286,"rotated": false}}, {"SPRITE":{"name":"15","x":3071,"y":0,"w":239,"h":290,"rotated":false}},
{"SPRITE" : {"name": "0016","x":953,"y":625,"w":245,"h":249,"rotated": false}}, {"SPRITE":{"name":"16","x":3135,"y":678,"w":220,"h":246,"rotated":false}},
{"SPRITE" : {"name": "0017","x":3644,"y":373,"w":136,"h":133,"rotated": false}}, {"SPRITE":{"name":"17","x":3597,"y":248,"w":173,"h":231,"rotated":false}},
{"SPRITE" : {"name": "0018","x":2331,"y":735,"w":150,"h":91,"rotated": false}}, {"SPRITE":{"name":"18","x":3769,"y":0,"w":225,"h":190,"rotated":false}},
{"SPRITE" : {"name": "0019","x":3644,"y":508,"w":151,"h":95,"rotated": false}}, {"SPRITE":{"name":"19","x":1290,"y":1053,"w":131,"h":81,"rotated":false}},
{"SPRITE" : {"name": "0020","x":784,"y":913,"w":140,"h":90,"rotated": false}}, {"SPRITE":{"name":"20","x":3905,"y":814,"w":132,"h":85,"rotated":false}},
{"SPRITE" : {"name": "0021","x":3644,"y":605,"w":149,"h":92,"rotated": false}}, {"SPRITE":{"name":"21","x":1733,"y":1053,"w":122,"h":80,"rotated":false}},
{"SPRITE" : {"name": "0022","x":2670,"y":932,"w":144,"h":87,"rotated": false}}, {"SPRITE":{"name":"22","x":3905,"y":917,"w":129,"h":82,"rotated":false}},
{"SPRITE" : {"name": "0023","x":2670,"y":843,"w":152,"h":87,"rotated": false}}, {"SPRITE":{"name":"23","x":1589,"y":1053,"w":126,"h":78,"rotated":false}},
{"SPRITE" : {"name": "0024","x":4087,"y":2,"w":1,"h":4,"rotated": false}}, {"SPRITE":{"name":"24","x":1439,"y":1053,"w":132,"h":77,"rotated":false}},
{"SPRITE" : {"name": "0025","x":2831,"y":373,"w":411,"h":352,"rotated": false}}, {"SPRITE":{"name":"25","x":3778,"y":814,"w":109,"h":107,"rotated":false}},
{"SPRITE" : {"name": "0026","x":3244,"y":373,"w":398,"h":350,"rotated": false}}, {"SPRITE":{"name":"26","x":3124,"y":1123,"w":3,"h":7,"rotated":false}},
{"SPRITE" : {"name": "0027","x":266,"y":642,"w":329,"h":162,"rotated": false}}, {"SPRITE":{"name":"27","x":3145,"y":1123,"w":3,"h":7,"rotated":false}},
{"SPRITE" : {"name": "0028","x":597,"y":642,"w":90,"h":103,"rotated": false}}, {"SPRITE":{"name":"28","x":3673,"y":497,"w":260,"h":141,"rotated":false}},
{"SPRITE" : {"name": "0029","x":1200,"y":625,"w":211,"h":236,"rotated": false}}, {"SPRITE":{"name":"29","x":3788,"y":208,"w":237,"h":161,"rotated":false}},
{"SPRITE" : {"name": "0030","x":2831,"y":727,"w":208,"h":235,"rotated": false}}, {"SPRITE":{"name":"30","x":3542,"y":660,"w":218,"h":144,"rotated":false}},
{"SPRITE" : {"name": "0031","x":2483,"y":2,"w":738,"h":300,"rotated": false}}, {"SPRITE":{"name":"31","x":3124,"y":947,"w":301,"h":158,"rotated":false}},
{"SPRITE" : {"name": "0032","x":2483,"y":304,"w":346,"h":537,"rotated": false}}, {"SPRITE":{"name":"32","x":3371,"y":498,"w":284,"h":144,"rotated":false}},
{"SPRITE" : {"name": "0033","x":2,"y":865,"w":235,"h":158,"rotated": false}}, {"SPRITE":{"name":"33","x":0,"y":981,"w":295,"h":150,"rotated":false}},
{"SPRITE" : {"name": "0034","x":3812,"y":865,"w":216,"h":141,"rotated": false}}, {"SPRITE":{"name":"34","x":313,"y":981,"w":288,"h":145,"rotated":false}},
{"SPRITE" : {"name": "0035","x":2000,"y":818,"w":299,"h":155,"rotated": false}}, {"SPRITE":{"name":"35","x":3443,"y":942,"w":182,"h":212,"rotated":false}},
{"SPRITE" : {"name": "0036","x":1200,"y":863,"w":179,"h":208,"rotated": false}}, {"SPRITE":{"name":"36","x":3371,"y":255,"w":208,"h":225,"rotated":false}},
{"SPRITE" : {"name": "0037","x":1691,"y":818,"w":307,"h":154,"rotated": false}}, {"SPRITE":{"name":"37","x":3373,"y":660,"w":151,"h":264,"rotated":false}},
{"SPRITE" : {"name": "0038","x":3644,"y":702,"w":320,"h":161,"rotated": false}}, {"SPRITE":{"name":"38","x":3778,"y":656,"w":206,"h":140,"rotated":false}},
{"SPRITE" : {"name": "0039","x":266,"y":806,"w":312,"h":155,"rotated": false}}, {"SPRITE":{"name":"39","x":3557,"y":0,"w":194,"h":230,"rotated":false}},
{"SPRITE" : {"name": "0040","x":239,"y":963,"w":224,"h":108,"rotated": false}}, {"SPRITE":{"name":"40","x":2678,"y":485,"w":47,"h":44,"rotated":false}},
{"SPRITE" : {"name": "0041","x":3223,"y":2,"w":580,"h":369,"rotated": false}}, {"SPRITE":{"name":"41","x":2870,"y":678,"w":247,"h":251,"rotated":false}},
{"SPRITE" : {"name": "0042","x":3805,"y":2,"w":269,"h":698,"rotated": false}}, {"SPRITE":{"name":"42","x":2678,"y":332,"w":138,"h":135,"rotated":false}},
{"SPRITE" : {"name": "0043","x":1413,"y":843,"w":205,"h":222,"rotated": false}}, {"SPRITE":{"name":"43","x":1065,"y":1053,"w":207,"h":101,"rotated":false}},
{"SPRITE" : {"name": "0044","x":3041,"y":727,"w":166,"h":292,"rotated": false}}, {"SPRITE":{"name":"44","x":2452,"y":947,"w":331,"h":164,"rotated":false}},
{"SPRITE" : {"name": "0045","x":953,"y":876,"w":203,"h":137,"rotated": false}}, {"SPRITE":{"name":"45","x":2021,"y":720,"w":413,"h":354,"rotated":false}},
{"SPRITE" : {"name": "0046","x":1439,"y":2,"w":516,"h":512,"rotated": false}}, {"SPRITE":{"name":"46","x":2452,"y":558,"w":400,"h":353,"rotated":false}},
{"SPRITE" : {"name": "0047","x":2,"y":2,"w":712,"h":638,"rotated": false}}, {"SPRITE":{"name":"47","x":3951,"y":387,"w":90,"h":103,"rotated":false}},
{"SPRITE" : {"name": "0048","x":2301,"y":843,"w":192,"h":227,"rotated": false}} {"SPRITE":{"name":"48","x":3140,"y":308,"w":213,"h":239,"rotated":false}},
]}, {"SPRITE":{"name":"49","x":3328,"y":0,"w":211,"h":237,"rotated":false}}]},
"meta": { "meta":{
"app": "Adobe Animate", "app":"Adobe Animate (Better TA Extension)",
"version": "21.0.1.37179", "version":"24.0.2.12",
"image": "spritemap1.png", "image":"spritemap1.png",
"format": "RGBA8888", "format":"RGBA8888",
"size": {"w":4090,"h":1074}, "size":{"w":4061,"h":1154},
"resolution": "1" "resolution":"1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 535 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because it is too large Load Diff
@@ -1,145 +1,241 @@
{"ATLAS": {"SPRITES":[ {"ATLAS":{"SPRITES":[
{"SPRITE" : {"name": "0000","x":2847,"y":969,"w":327,"h":397,"rotated": false}}, {"SPRITE":{"name":"0","x":2564,"y":2,"w":23,"h":144,"rotated":false}},
{"SPRITE" : {"name": "0001","x":2441,"y":894,"w":110,"h":159,"rotated": false}}, {"SPRITE":{"name":"1","x":3024,"y":522,"w":42,"h":85,"rotated":false}},
{"SPRITE" : {"name": "0002","x":1709,"y":1631,"w":197,"h":185,"rotated": false}}, {"SPRITE":{"name":"2","x":2598,"y":1211,"w":67,"h":60,"rotated":false}},
{"SPRITE" : {"name": "0003","x":408,"y":1773,"w":92,"h":155,"rotated": false}}, {"SPRITE":{"name":"3","x":3964,"y":932,"w":93,"h":51,"rotated":false}},
{"SPRITE" : {"name": "0004","x":236,"y":1639,"w":274,"h":132,"rotated": false}}, {"SPRITE":{"name":"4","x":3691,"y":1016,"w":91,"h":43,"rotated":false}},
{"SPRITE" : {"name": "0005","x":3427,"y":1495,"w":133,"h":74,"rotated": false}}, {"SPRITE":{"name":"5","x":3877,"y":270,"w":111,"h":95,"rotated":false}},
{"SPRITE" : {"name": "0006","x":3270,"y":540,"w":121,"h":162,"rotated": false}}, {"SPRITE":{"name":"6","x":3449,"y":1124,"w":113,"h":127,"rotated":false}},
{"SPRITE" : {"name": "0007","x":359,"y":649,"w":518,"h":294,"rotated": false}}, {"SPRITE":{"name":"7","x":367,"y":1129,"w":382,"h":123,"rotated":false}},
{"SPRITE" : {"name": "0008","x":1957,"y":2,"w":775,"h":483,"rotated": false}}, {"SPRITE":{"name":"8","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
{"SPRITE" : {"name": "0009","x":1014,"y":2,"w":941,"h":607,"rotated": false}}, {"SPRITE":{"name":"9","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
{"SPRITE" : {"name": "0010","x":2,"y":2,"w":1010,"h":645,"rotated": false}}, {"SPRITE":{"name":"10","x":2437,"y":1136,"w":31,"h":31,"rotated":false}},
{"SPRITE" : {"name": "0011","x":4059,"y":2,"w":20,"h":142,"rotated": false}}, {"SPRITE":{"name":"11","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
{"SPRITE" : {"name": "0012","x":4035,"y":616,"w":40,"h":83,"rotated": false}}, {"SPRITE":{"name":"12","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
{"SPRITE" : {"name": "0013","x":2755,"y":1508,"w":64,"h":57,"rotated": false}}, {"SPRITE":{"name":"13","x":2670,"y":1175,"w":142,"h":94,"rotated":false}},
{"SPRITE" : {"name": "0014","x":3176,"y":1171,"w":91,"h":48,"rotated": false}}, {"SPRITE":{"name":"14","x":3877,"y":366,"w":122,"h":82,"rotated":false}},
{"SPRITE" : {"name": "0015","x":3245,"y":1737,"w":88,"h":41,"rotated": false}}, {"SPRITE":{"name":"15","x":1162,"y":1112,"w":436,"h":151,"rotated":false}},
{"SPRITE" : {"name": "0016","x":3064,"y":1368,"w":109,"h":93,"rotated": false}}, {"SPRITE":{"name":"16","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE" : {"name": "0017","x":3402,"y":2,"w":655,"h":434,"rotated": false}}, {"SPRITE":{"name":"17","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE" : {"name": "0018","x":1957,"y":487,"w":626,"h":405,"rotated": false}}, {"SPRITE":{"name":"18","x":779,"y":692,"w":440,"h":344,"rotated":false}},
{"SPRITE" : {"name": "0019","x":3402,"y":438,"w":631,"h":438,"rotated": false}}, {"SPRITE":{"name":"19","x":1550,"y":711,"w":388,"h":273,"rotated":false}},
{"SPRITE" : {"name": "0020","x":1170,"y":1211,"w":393,"h":287,"rotated": false}}, {"SPRITE":{"name":"20","x":2106,"y":2,"w":376,"h":155,"rotated":false}},
{"SPRITE" : {"name": "0021","x":2847,"y":540,"w":421,"h":427,"rotated": false}}, {"SPRITE":{"name":"21","x":2,"y":2,"w":932,"h":689,"rotated":false}},
{"SPRITE" : {"name": "0022","x":2734,"y":2,"w":666,"h":536,"rotated": false}}, {"SPRITE":{"name":"22","x":3907,"y":1171,"w":48,"h":36,"rotated":false}},
{"SPRITE" : {"name": "0023","x":542,"y":1480,"w":224,"h":221,"rotated": false}}, {"SPRITE":{"name":"23","x":3907,"y":1171,"w":48,"h":36,"rotated":false}},
{"SPRITE" : {"name": "0024","x":3657,"y":1736,"w":186,"h":175,"rotated": false}}, {"SPRITE":{"name":"24","x":3858,"y":1206,"w":48,"h":40,"rotated":false}},
{"SPRITE" : {"name": "0025","x":4035,"y":764,"w":56,"h":52,"rotated": false}}, {"SPRITE":{"name":"25","x":3762,"y":1093,"w":48,"h":64,"rotated":false}},
{"SPRITE" : {"name": "0026","x":502,"y":1876,"w":126,"h":104,"rotated": false}}, {"SPRITE":{"name":"26","x":3713,"y":1060,"w":48,"h":66,"rotated":false}},
{"SPRITE" : {"name": "0027","x":1709,"y":1818,"w":135,"h":104,"rotated": false}}, {"SPRITE":{"name":"27","x":3885,"y":1095,"w":48,"h":67,"rotated":false}},
{"SPRITE" : {"name": "0028","x":2,"y":1440,"w":232,"h":236,"rotated": false}}, {"SPRITE":{"name":"28","x":2106,"y":158,"w":48,"h":69,"rotated":false}},
{"SPRITE" : {"name": "0029","x":3938,"y":1437,"w":139,"h":109,"rotated": false}}, {"SPRITE":{"name":"29","x":2481,"y":498,"w":48,"h":71,"rotated":false}},
{"SPRITE" : {"name": "0030","x":2755,"y":1627,"w":235,"h":158,"rotated": false}}, {"SPRITE":{"name":"30","x":1162,"y":1037,"w":48,"h":73,"rotated":false}},
{"SPRITE" : {"name": "0031","x":1147,"y":1500,"w":198,"h":233,"rotated": false}}, {"SPRITE":{"name":"31","x":4018,"y":188,"w":48,"h":76,"rotated":false}},
{"SPRITE" : {"name": "0032","x":2,"y":1860,"w":118,"h":117,"rotated": false}}, {"SPRITE":{"name":"32","x":3455,"y":856,"w":48,"h":78,"rotated":false}},
{"SPRITE" : {"name": "0033","x":1565,"y":1398,"w":262,"h":221,"rotated": false}}, {"SPRITE":{"name":"33","x":3785,"y":1011,"w":48,"h":81,"rotated":false}},
{"SPRITE" : {"name": "0034","x":3845,"y":1736,"w":222,"h":145,"rotated": false}}, {"SPRITE":{"name":"34","x":4002,"y":1164,"w":48,"h":82,"rotated":false}},
{"SPRITE" : {"name": "0035","x":979,"y":1480,"w":166,"h":292,"rotated": false}}, {"SPRITE":{"name":"35","x":3953,"y":1083,"w":48,"h":87,"rotated":false}},
{"SPRITE" : {"name": "0036","x":3657,"y":1592,"w":276,"h":142,"rotated": false}}, {"SPRITE":{"name":"36","x":3904,"y":1004,"w":48,"h":90,"rotated":false}},
{"SPRITE" : {"name": "0037","x":925,"y":1774,"w":203,"h":138,"rotated": false}}, {"SPRITE":{"name":"37","x":4013,"y":1070,"w":48,"h":93,"rotated":false}},
{"SPRITE" : {"name": "0038","x":236,"y":1467,"w":304,"h":170,"rotated": false}}, {"SPRITE":{"name":"38","x":3964,"y":984,"w":48,"h":98,"rotated":false}},
{"SPRITE" : {"name": "0039","x":4081,"y":2,"w":1,"h":4,"rotated": false}}, {"SPRITE":{"name":"39","x":1550,"y":985,"w":48,"h":103,"rotated":false}},
{"SPRITE" : {"name": "0040","x":2441,"y":1055,"w":135,"h":127,"rotated": false}}, {"SPRITE":{"name":"40","x":3848,"y":894,"w":50,"h":109,"rotated":false}},
{"SPRITE" : {"name": "0041","x":2,"y":1678,"w":195,"h":180,"rotated": false}}, {"SPRITE":{"name":"41","x":3797,"y":894,"w":50,"h":116,"rotated":false}},
{"SPRITE" : {"name": "0042","x":2751,"y":1787,"w":160,"h":144,"rotated": false}}, {"SPRITE":{"name":"42","x":2777,"y":452,"w":50,"h":121,"rotated":false}},
{"SPRITE" : {"name": "0043","x":713,"y":945,"w":243,"h":261,"rotated": false}}, {"SPRITE":{"name":"43","x":4002,"y":585,"w":50,"h":119,"rotated":false}},
{"SPRITE" : {"name": "0044","x":2375,"y":1508,"w":378,"h":121,"rotated": false}}, {"SPRITE":{"name":"44","x":3913,"y":886,"w":50,"h":117,"rotated":false}},
{"SPRITE" : {"name": "0045","x":3064,"y":1495,"w":361,"h":128,"rotated": false}}, {"SPRITE":{"name":"45","x":3059,"y":2,"w":52,"h":129,"rotated":false}},
{"SPRITE" : {"name": "0046","x":713,"y":1211,"w":455,"h":267,"rotated": false}}, {"SPRITE":{"name":"46","x":3964,"y":886,"w":123,"h":45,"rotated":false}},
{"SPRITE" : {"name": "0047","x":2,"y":1112,"w":272,"h":326,"rotated": false}}, {"SPRITE":{"name":"47","x":1770,"y":228,"w":123,"h":43,"rotated":false}},
{"SPRITE" : {"name": "0048","x":3710,"y":878,"w":249,"h":557,"rotated": false}}, {"SPRITE":{"name":"48","x":3424,"y":486,"w":99,"h":35,"rotated":false}},
{"SPRITE" : {"name": "0049","x":2585,"y":540,"w":260,"h":701,"rotated": false}}, {"SPRITE":{"name":"49","x":2979,"y":1141,"w":99,"h":34,"rotated":false}},
{"SPRITE" : {"name": "0050","x":1014,"y":611,"w":291,"h":598,"rotated": false}}, {"SPRITE":{"name":"50","x":2979,"y":1141,"w":99,"h":34,"rotated":false}},
{"SPRITE" : {"name": "0051","x":1307,"y":611,"w":315,"h":540,"rotated": false}}, {"SPRITE":{"name":"51","x":2590,"y":360,"w":99,"h":36,"rotated":false}},
{"SPRITE" : {"name": "0052","x":2,"y":649,"w":355,"h":461,"rotated": false}}, {"SPRITE":{"name":"52","x":3709,"y":783,"w":35,"h":21,"rotated":false}},
{"SPRITE" : {"name": "0053","x":359,"y":945,"w":352,"h":373,"rotated": false}}, {"SPRITE":{"name":"53","x":1220,"y":711,"w":329,"h":400,"rotated":false}},
{"SPRITE" : {"name": "0054","x":3064,"y":1625,"w":347,"h":110,"rotated": false}}, {"SPRITE":{"name":"54","x":3042,"y":979,"w":112,"h":161,"rotated":false}},
{"SPRITE" : {"name": "0055","x":2039,"y":1248,"w":379,"h":216,"rotated": false}}, {"SPRITE":{"name":"55","x":2929,"y":163,"w":199,"h":188,"rotated":false}},
{"SPRITE" : {"name": "0056","x":2827,"y":1368,"w":235,"h":257,"rotated": false}}, {"SPRITE":{"name":"56","x":2175,"y":574,"w":96,"h":158,"rotated":false}},
{"SPRITE" : {"name": "0057","x":2100,"y":1879,"w":140,"h":92,"rotated": false}}, {"SPRITE":{"name":"57","x":1623,"y":570,"w":277,"h":134,"rotated":false}},
{"SPRITE" : {"name": "0058","x":776,"y":1886,"w":119,"h":79,"rotated": false}}, {"SPRITE":{"name":"58","x":1550,"y":1089,"w":38,"h":22,"rotated":false}},
{"SPRITE" : {"name": "0059","x":1565,"y":1249,"w":434,"h":147,"rotated": false}}, {"SPRITE":{"name":"59","x":3666,"y":152,"w":124,"h":165,"rotated":false}},
{"SPRITE" : {"name": "0060","x":276,"y":1320,"w":433,"h":145,"rotated": false}}, {"SPRITE":{"name":"60","x":1623,"y":272,"w":309,"h":297,"rotated":false}},
{"SPRITE" : {"name": "0061","x":3270,"y":878,"w":438,"h":342,"rotated": false}}, {"SPRITE":{"name":"61","x":3801,"y":2,"w":121,"h":91,"rotated":false}},
{"SPRITE" : {"name": "0062","x":3176,"y":1222,"w":385,"h":271,"rotated": false}}, {"SPRITE":{"name":"62","x":3281,"y":522,"w":320,"h":77,"rotated":false}},
{"SPRITE" : {"name": "0063","x":3563,"y":1437,"w":373,"h":153,"rotated": false}}, {"SPRITE":{"name":"63","x":3921,"y":755,"w":17,"h":22,"rotated":false}},
{"SPRITE" : {"name": "0064","x":3245,"y":1786,"w":202,"h":122,"rotated": false}}, {"SPRITE":{"name":"64","x":1351,"y":272,"w":162,"h":74,"rotated":false}},
{"SPRITE" : {"name": "0065","x":3449,"y":1786,"w":202,"h":122,"rotated": false}}, {"SPRITE":{"name":"65","x":3801,"y":94,"w":69,"h":50,"rotated":false}},
{"SPRITE" : {"name": "0066","x":3064,"y":1861,"w":150,"h":91,"rotated": false}}, {"SPRITE":{"name":"66","x":2273,"y":574,"w":277,"h":174,"rotated":false}},
{"SPRITE" : {"name": "0067","x":3938,"y":1548,"w":151,"h":95,"rotated": false}}, {"SPRITE":{"name":"67","x":2261,"y":1136,"w":175,"h":123,"rotated":false}},
{"SPRITE" : {"name": "0068","x":3845,"y":1883,"w":140,"h":90,"rotated": false}}, {"SPRITE":{"name":"68","x":4058,"y":932,"w":32,"h":49,"rotated":false}},
{"SPRITE" : {"name": "0069","x":2913,"y":1861,"w":149,"h":92,"rotated": false}}, {"SPRITE":{"name":"69","x":3791,"y":270,"w":66,"h":38,"rotated":false}},
{"SPRITE" : {"name": "0070","x":630,"y":1886,"w":144,"h":87,"rotated": false}}, {"SPRITE":{"name":"70","x":3079,"y":1141,"w":71,"h":40,"rotated":false}},
{"SPRITE" : {"name": "0071","x":3935,"y":1645,"w":152,"h":87,"rotated": false}}, {"SPRITE":{"name":"71","x":2690,"y":360,"w":82,"h":29,"rotated":false}},
{"SPRITE" : {"name": "0072","x":2441,"y":1243,"w":384,"h":263,"rotated": false}}, {"SPRITE":{"name":"72","x":3602,"y":516,"w":88,"h":69,"rotated":false}},
{"SPRITE" : {"name": "0073","x":1624,"y":611,"w":248,"h":277,"rotated": false}}, {"SPRITE":{"name":"73","x":4022,"y":806,"w":69,"h":27,"rotated":false}},
{"SPRITE" : {"name": "0074","x":1624,"y":894,"w":413,"h":353,"rotated": false}}, {"SPRITE":{"name":"74","x":1894,"y":228,"w":30,"h":40,"rotated":false}},
{"SPRITE" : {"name": "0075","x":2039,"y":894,"w":400,"h":352,"rotated": false}}, {"SPRITE":{"name":"75","x":3987,"y":654,"w":10,"h":13,"rotated":false}},
{"SPRITE" : {"name": "0076","x":1829,"y":1466,"w":330,"h":163,"rotated": false}}, {"SPRITE":{"name":"76","x":3896,"y":1084,"w":7,"h":7,"rotated":false}},
{"SPRITE" : {"name": "0077","x":3987,"y":1883,"w":90,"h":103,"rotated": false}}, {"SPRITE":{"name":"77","x":3834,"y":1074,"w":9,"h":9,"rotated":false}},
{"SPRITE" : {"name": "0078","x":2161,"y":1466,"w":212,"h":238,"rotated": false}}, {"SPRITE":{"name":"78","x":3885,"y":1084,"w":10,"h":10,"rotated":false}},
{"SPRITE" : {"name": "0079","x":768,"y":1480,"w":209,"h":236,"rotated": false}}, {"SPRITE":{"name":"79","x":3441,"y":736,"w":12,"h":12,"rotated":false}},
{"SPRITE" : {"name": "0080","x":2242,"y":1879,"w":86,"h":84,"rotated": false}}, {"SPRITE":{"name":"80","x":3811,"y":1128,"w":19,"h":17,"rotated":false}},
{"SPRITE" : {"name": "0081","x":879,"y":649,"w":126,"h":148,"rotated": false}}, {"SPRITE":{"name":"81","x":3971,"y":654,"w":15,"h":14,"rotated":false}},
{"SPRITE" : {"name": "0082","x":1908,"y":1631,"w":216,"h":168,"rotated": false}}, {"SPRITE":{"name":"82","x":3858,"y":1150,"w":7,"h":7,"rotated":false}},
{"SPRITE" : {"name": "0083","x":2575,"y":1631,"w":174,"h":192,"rotated": false}}, {"SPRITE":{"name":"83","x":3794,"y":452,"w":5,"h":5,"rotated":false}},
{"SPRITE" : {"name": "0084","x":1130,"y":1901,"w":106,"h":75,"rotated": false}}, {"SPRITE":{"name":"84","x":3326,"y":751,"w":32,"h":57,"rotated":false}},
{"SPRITE" : {"name": "0085","x":3961,"y":1367,"w":129,"h":63,"rotated": false}}, {"SPRITE":{"name":"85","x":2583,"y":147,"w":6,"h":7,"rotated":false}},
{"SPRITE" : {"name": "0086","x":1829,"y":1398,"w":158,"h":60,"rotated": false}}, {"SPRITE":{"name":"86","x":935,"y":617,"w":198,"h":66,"rotated":false}},
{"SPRITE" : {"name": "0087","x":3270,"y":704,"w":123,"h":148,"rotated": false}}, {"SPRITE":{"name":"87","x":426,"y":692,"w":352,"h":436,"rotated":false}},
{"SPRITE" : {"name": "0088","x":3961,"y":878,"w":123,"h":148,"rotated": false}}, {"SPRITE":{"name":"88","x":3814,"y":452,"w":156,"h":69,"rotated":false}},
{"SPRITE" : {"name": "0089","x":199,"y":1773,"w":207,"h":142,"rotated": false}}, {"SPRITE":{"name":"89","x":2829,"y":452,"w":194,"h":197,"rotated":false}},
{"SPRITE" : {"name": "0090","x":1238,"y":1901,"w":91,"h":53,"rotated": false}}, {"SPRITE":{"name":"90","x":2670,"y":733,"w":181,"h":216,"rotated":false}},
{"SPRITE" : {"name": "0091","x":276,"y":1112,"w":65,"h":96,"rotated": false}}, {"SPRITE":{"name":"91","x":3791,"y":150,"w":93,"h":119,"rotated":false}},
{"SPRITE" : {"name": "0092","x":276,"y":1210,"w":65,"h":96,"rotated": false}}, {"SPRITE":{"name":"92","x":4000,"y":372,"w":86,"h":75,"rotated":false}},
{"SPRITE" : {"name": "0093","x":2518,"y":1825,"w":136,"h":102,"rotated": false}}, {"SPRITE":{"name":"93","x":3587,"y":320,"w":113,"h":195,"rotated":false}},
{"SPRITE" : {"name": "0094","x":1537,"y":1857,"w":136,"h":102,"rotated": false}}, {"SPRITE":{"name":"94","x":2483,"y":2,"w":80,"h":151,"rotated":false}},
{"SPRITE" : {"name": "0095","x":3427,"y":1592,"w":228,"h":192,"rotated": false}}, {"SPRITE":{"name":"95","x":3493,"y":2,"w":177,"h":149,"rotated":false}},
{"SPRITE" : {"name": "0096","x":1541,"y":1621,"w":166,"h":234,"rotated": false}}, {"SPRITE":{"name":"96","x":3327,"y":183,"w":9,"h":8,"rotated":false}},
{"SPRITE" : {"name": "0097","x":512,"y":1703,"w":205,"h":171,"rotated": false}}, {"SPRITE":{"name":"97","x":3787,"y":452,"w":6,"h":6,"rotated":false}},
{"SPRITE" : {"name": "0098","x":2126,"y":1706,"w":205,"h":171,"rotated": false}}, {"SPRITE":{"name":"98","x":4002,"y":705,"w":57,"h":48,"rotated":false}},
{"SPRITE" : {"name": "0099","x":719,"y":1718,"w":204,"h":166,"rotated": false}}, {"SPRITE":{"name":"99","x":2574,"y":147,"w":8,"h":8,"rotated":false}},
{"SPRITE" : {"name": "0100","x":1147,"y":1735,"w":206,"h":164,"rotated": false}}, {"SPRITE":{"name":"100","x":3814,"y":605,"w":8,"h":12,"rotated":false}},
{"SPRITE" : {"name": "0101","x":1874,"y":611,"w":79,"h":185,"rotated": false}}, {"SPRITE":{"name":"101","x":1589,"y":1089,"w":9,"h":10,"rotated":false}},
{"SPRITE" : {"name": "0102","x":3176,"y":969,"w":83,"h":200,"rotated": false}}, {"SPRITE":{"name":"102","x":3701,"y":318,"w":85,"h":140,"rotated":false}},
{"SPRITE" : {"name": "0103","x":3563,"y":1222,"w":86,"h":180,"rotated": false}}, {"SPRITE":{"name":"103","x":2,"y":692,"w":423,"h":429,"rotated":false}},
{"SPRITE" : {"name": "0104","x":3961,"y":1028,"w":89,"h":186,"rotated": false}}, {"SPRITE":{"name":"104","x":3533,"y":152,"w":132,"h":167,"rotated":false}},
{"SPRITE" : {"name": "0105","x":2375,"y":1631,"w":198,"h":183,"rotated": false}}, {"SPRITE":{"name":"105","x":3249,"y":961,"w":66,"h":16,"rotated":false}},
{"SPRITE" : {"name": "0106","x":1908,"y":1801,"w":190,"h":120,"rotated": false}}, {"SPRITE":{"name":"106","x":3576,"y":808,"w":52,"h":43,"rotated":false}},
{"SPRITE" : {"name": "0107","x":2656,"y":1825,"w":86,"h":145,"rotated": false}}, {"SPRITE":{"name":"107","x":3858,"y":1163,"w":46,"h":42,"rotated":false}},
{"SPRITE" : {"name": "0108","x":1355,"y":1724,"w":180,"h":170,"rotated": false}}, {"SPRITE":{"name":"108","x":3834,"y":1011,"w":13,"h":62,"rotated":false}},
{"SPRITE" : {"name": "0109","x":3961,"y":1216,"w":111,"h":149,"rotated": false}}, {"SPRITE":{"name":"109","x":3654,"y":1025,"w":34,"h":29,"rotated":false}},
{"SPRITE" : {"name": "0110","x":4035,"y":516,"w":39,"h":98,"rotated": false}}, {"SPRITE":{"name":"110","x":3811,"y":1150,"w":46,"h":65,"rotated":false}},
{"SPRITE" : {"name": "0111","x":1307,"y":1153,"w":118,"h":47,"rotated": false}}, {"SPRITE":{"name":"111","x":3701,"y":459,"w":112,"h":46,"rotated":false}},
{"SPRITE" : {"name": "0112","x":1427,"y":1153,"w":120,"h":43,"rotated": false}}, {"SPRITE":{"name":"112","x":2035,"y":898,"w":226,"h":223,"rotated":false}},
{"SPRITE" : {"name": "0113","x":2441,"y":1184,"w":120,"h":41,"rotated": false}}, {"SPRITE":{"name":"113","x":3233,"y":351,"w":190,"h":170,"rotated":false}},
{"SPRITE" : {"name": "0114","x":3216,"y":1910,"w":95,"h":33,"rotated": false}}, {"SPRITE":{"name":"114","x":3654,"y":1060,"w":58,"h":55,"rotated":false}},
{"SPRITE" : {"name": "0115","x":3563,"y":1404,"w":95,"h":31,"rotated": false}}, {"SPRITE":{"name":"115","x":3885,"y":94,"w":112,"h":94,"rotated":false}},
{"SPRITE" : {"name": "0116","x":3563,"y":1404,"w":95,"h":31,"rotated": false}}, {"SPRITE":{"name":"116","x":3576,"y":706,"w":132,"h":101,"rotated":false}},
{"SPRITE" : {"name": "0117","x":2330,"y":1906,"w":95,"h":34,"rotated": false}}, {"SPRITE":{"name":"117","x":1939,"y":493,"w":235,"h":239,"rotated":false}},
{"SPRITE" : {"name": "0118","x":2585,"y":487,"w":131,"h":47,"rotated": false}}, {"SPRITE":{"name":"118","x":3242,"y":1150,"w":141,"h":111,"rotated":false}},
{"SPRITE" : {"name": "0119","x":4059,"y":258,"w":31,"h":18,"rotated": false}}, {"SPRITE":{"name":"119","x":2822,"y":2,"w":236,"h":160,"rotated":false}},
{"SPRITE" : {"name": "0120","x":1355,"y":1896,"w":122,"h":66,"rotated": false}}, {"SPRITE":{"name":"120","x":2475,"y":749,"w":194,"h":236,"rotated":false}},
{"SPRITE" : {"name": "0121","x":2992,"y":1737,"w":251,"h":122,"rotated": false}}, {"SPRITE":{"name":"121","x":3455,"y":736,"w":120,"h":119,"rotated":false}},
{"SPRITE" : {"name": "0122","x":879,"y":799,"w":114,"h":133,"rotated": false}}, {"SPRITE":{"name":"122","x":2551,"y":398,"w":225,"h":189,"rotated":false}},
{"SPRITE" : {"name": "0123","x":1347,"y":1500,"w":192,"h":222,"rotated": false}}, {"SPRITE":{"name":"123","x":3923,"y":2,"w":130,"h":81,"rotated":false}},
{"SPRITE" : {"name": "0124","x":4035,"y":818,"w":39,"h":34,"rotated": false}}, {"SPRITE":{"name":"124","x":3691,"y":621,"w":132,"h":83,"rotated":false}},
{"SPRITE" : {"name": "0125","x":4035,"y":701,"w":50,"h":61,"rotated": false}}, {"SPRITE":{"name":"125","x":3531,"y":1025,"w":122,"h":80,"rotated":false}},
{"SPRITE" : {"name": "0126","x":1874,"y":798,"w":64,"h":78,"rotated": false}}, {"SPRITE":{"name":"126","x":3814,"y":522,"w":129,"h":82,"rotated":false}},
{"SPRITE" : {"name": "0127","x":4035,"y":438,"w":52,"h":76,"rotated": false}}, {"SPRITE":{"name":"127","x":3709,"y":705,"w":125,"h":77,"rotated":false}},
{"SPRITE" : {"name": "0128","x":958,"y":934,"w":46,"h":63,"rotated": false}}, {"SPRITE":{"name":"128","x":3885,"y":189,"w":132,"h":76,"rotated":false}},
{"SPRITE" : {"name": "0129","x":4059,"y":146,"w":30,"h":41,"rotated": false}}, {"SPRITE":{"name":"129","x":2035,"y":1122,"w":225,"h":142,"rotated":false}},
{"SPRITE" : {"name": "0130","x":4059,"y":189,"w":23,"h":35,"rotated": false}}, {"SPRITE":{"name":"130","x":2777,"y":187,"w":151,"h":264,"rotated":false}},
{"SPRITE" : {"name": "0131","x":4059,"y":226,"w":20,"h":30,"rotated": false}}, {"SPRITE":{"name":"131","x":2551,"y":588,"w":277,"h":144,"rotated":false}},
{"SPRITE" : {"name": "0132","x":4059,"y":278,"w":17,"h":22,"rotated": false}}, {"SPRITE":{"name":"132","x":3327,"y":197,"w":205,"h":140,"rotated":false}},
{"SPRITE" : {"name": "0133","x":2333,"y":1816,"w":183,"h":88,"rotated": false}} {"SPRITE":{"name":"133","x":2175,"y":401,"w":305,"h":172,"rotated":false}},
]}, {"SPRITE":{"name":"134","x":2841,"y":1136,"w":137,"h":129,"rotated":false}},
"meta": { {"SPRITE":{"name":"135","x":3129,"y":172,"w":197,"h":178,"rotated":false}},
"app": "Adobe Animate", {"SPRITE":{"name":"136","x":2,"y":1122,"w":364,"h":131,"rotated":false}},
"version": "21.0.1.37179", {"SPRITE":{"name":"137","x":3424,"y":338,"w":162,"h":147,"rotated":false}},
"image": "spritemap1.png", {"SPRITE":{"name":"138","x":1933,"y":228,"w":223,"h":264,"rotated":false}},
"format": "RGBA8888", {"SPRITE":{"name":"139","x":1351,"y":2,"w":418,"h":269,"rotated":false}},
"size": {"w":4093,"h":1988}, {"SPRITE":{"name":"140","x":1599,"y":1133,"w":350,"h":113,"rotated":false}},
"resolution": "1" {"SPRITE":{"name":"141","x":1599,"y":985,"w":435,"h":147,"rotated":false}},
{"SPRITE":{"name":"142","x":3921,"y":670,"w":80,"h":84,"rotated":false}},
{"SPRITE":{"name":"143","x":2564,"y":147,"w":9,"h":8,"rotated":false}},
{"SPRITE":{"name":"144","x":2437,"y":1168,"w":31,"h":27,"rotated":false}},
{"SPRITE":{"name":"145","x":3664,"y":808,"w":110,"h":88,"rotated":false}},
{"SPRITE":{"name":"146","x":3024,"y":608,"w":43,"h":39,"rotated":false}},
{"SPRITE":{"name":"147","x":3563,"y":1106,"w":55,"h":58,"rotated":false}},
{"SPRITE":{"name":"148","x":3971,"y":619,"w":19,"h":34,"rotated":false}},
{"SPRITE":{"name":"149","x":3416,"y":1250,"w":30,"h":23,"rotated":false}},
{"SPRITE":{"name":"150","x":1939,"y":898,"w":82,"h":85,"rotated":false}},
{"SPRITE":{"name":"151","x":4054,"y":39,"w":34,"h":41,"rotated":false}},
{"SPRITE":{"name":"152","x":3691,"y":516,"w":122,"h":104,"rotated":false}},
{"SPRITE":{"name":"153","x":3762,"y":1060,"w":17,"h":32,"rotated":false}},
{"SPRITE":{"name":"154","x":3745,"y":783,"w":28,"h":20,"rotated":false}},
{"SPRITE":{"name":"155","x":3524,"y":486,"w":45,"h":35,"rotated":false}},
{"SPRITE":{"name":"156","x":4000,"y":266,"w":74,"h":105,"rotated":false}},
{"SPRITE":{"name":"157","x":3775,"y":836,"w":137,"h":57,"rotated":false}},
{"SPRITE":{"name":"158","x":3384,"y":1250,"w":31,"h":23,"rotated":false}},
{"SPRITE":{"name":"159","x":3504,"y":908,"w":24,"h":24,"rotated":false}},
{"SPRITE":{"name":"160","x":4013,"y":984,"w":55,"h":85,"rotated":false}},
{"SPRITE":{"name":"161","x":4054,"y":2,"w":40,"h":36,"rotated":false}},
{"SPRITE":{"name":"162","x":3775,"y":783,"w":163,"h":52,"rotated":false}},
{"SPRITE":{"name":"163","x":1950,"y":1133,"w":73,"h":115,"rotated":false}},
{"SPRITE":{"name":"164","x":3531,"y":962,"w":159,"h":62,"rotated":false}},
{"SPRITE":{"name":"165","x":3811,"y":1093,"w":20,"h":34,"rotated":false}},
{"SPRITE":{"name":"166","x":4067,"y":230,"w":27,"h":32,"rotated":false}},
{"SPRITE":{"name":"167","x":4067,"y":188,"w":24,"h":41,"rotated":false}},
{"SPRITE":{"name":"168","x":3504,"y":856,"w":26,"h":51,"rotated":false}},
{"SPRITE":{"name":"169","x":2929,"y":352,"w":86,"h":98,"rotated":false}},
{"SPRITE":{"name":"170","x":3824,"y":605,"w":146,"h":64,"rotated":false}},
{"SPRITE":{"name":"171","x":1589,"y":1100,"w":9,"h":10,"rotated":false}},
{"SPRITE":{"name":"172","x":3327,"y":172,"w":8,"h":10,"rotated":false}},
{"SPRITE":{"name":"173","x":3939,"y":806,"w":82,"h":27,"rotated":false}},
{"SPRITE":{"name":"174","x":3944,"y":522,"w":141,"h":62,"rotated":false}},
{"SPRITE":{"name":"175","x":3024,"y":352,"w":208,"h":169,"rotated":false}},
{"SPRITE":{"name":"176","x":3129,"y":2,"w":208,"h":169,"rotated":false}},
{"SPRITE":{"name":"177","x":3042,"y":814,"w":206,"h":164,"rotated":false}},
{"SPRITE":{"name":"178","x":3072,"y":522,"w":208,"h":161,"rotated":false}},
{"SPRITE":{"name":"179","x":779,"y":1037,"w":382,"h":218,"rotated":false}},
{"SPRITE":{"name":"180","x":935,"y":357,"w":237,"h":259,"rotated":false}},
{"SPRITE":{"name":"181","x":3155,"y":979,"w":86,"h":203,"rotated":false}},
{"SPRITE":{"name":"182","x":3364,"y":751,"w":90,"h":183,"rotated":false}},
{"SPRITE":{"name":"183","x":3357,"y":961,"w":91,"h":188,"rotated":false}},
{"SPRITE":{"name":"184","x":2852,"y":814,"w":185,"h":122,"rotated":false}},
{"SPRITE":{"name":"185","x":3787,"y":318,"w":89,"h":133,"rotated":false}},
{"SPRITE":{"name":"186","x":2590,"y":187,"w":183,"h":172,"rotated":false}},
{"SPRITE":{"name":"187","x":3242,"y":979,"w":114,"h":151,"rotated":false}},
{"SPRITE":{"name":"188","x":4047,"y":834,"w":42,"h":36,"rotated":false}},
{"SPRITE":{"name":"189","x":1134,"y":617,"w":53,"h":63,"rotated":false}},
{"SPRITE":{"name":"190","x":3166,"y":1183,"w":67,"h":80,"rotated":false}},
{"SPRITE":{"name":"191","x":3848,"y":1004,"w":55,"h":79,"rotated":false}},
{"SPRITE":{"name":"192","x":3834,"y":1084,"w":50,"h":65,"rotated":false}},
{"SPRITE":{"name":"193","x":3629,"y":808,"w":33,"h":43,"rotated":false}},
{"SPRITE":{"name":"194","x":3493,"y":152,"w":26,"h":38,"rotated":false}},
{"SPRITE":{"name":"195","x":3971,"y":585,"w":23,"h":33,"rotated":false}},
{"SPRITE":{"name":"196","x":2530,"y":498,"w":20,"h":24,"rotated":false}},
{"SPRITE":{"name":"197","x":3939,"y":755,"w":121,"h":50,"rotated":false}},
{"SPRITE":{"name":"198","x":3913,"y":836,"w":133,"h":49,"rotated":false}},
{"SPRITE":{"name":"199","x":3971,"y":449,"w":119,"h":69,"rotated":false}},
{"SPRITE":{"name":"200","x":3072,"y":684,"w":253,"h":125,"rotated":false}},
{"SPRITE":{"name":"201","x":3441,"y":600,"w":116,"h":135,"rotated":false}},
{"SPRITE":{"name":"202","x":2475,"y":986,"w":194,"h":224,"rotated":false}},
{"SPRITE":{"name":"203","x":1770,"y":2,"w":335,"h":225,"rotated":false}},
{"SPRITE":{"name":"204","x":2157,"y":158,"w":217,"h":242,"rotated":false}},
{"SPRITE":{"name":"205","x":3249,"y":810,"w":114,"h":150,"rotated":false}},
{"SPRITE":{"name":"206","x":3326,"y":600,"w":114,"h":150,"rotated":false}},
{"SPRITE":{"name":"207","x":3835,"y":670,"w":85,"h":87,"rotated":false}},
{"SPRITE":{"name":"208","x":3671,"y":2,"w":129,"h":147,"rotated":false}},
{"SPRITE":{"name":"209","x":2852,"y":650,"w":219,"h":163,"rotated":false}},
{"SPRITE":{"name":"210","x":3338,"y":2,"w":154,"h":194,"rotated":false}},
{"SPRITE":{"name":"211","x":1514,"y":272,"w":108,"h":68,"rotated":false}},
{"SPRITE":{"name":"212","x":3664,"y":897,"w":132,"h":64,"rotated":false}},
{"SPRITE":{"name":"213","x":2437,"y":1211,"w":160,"h":62,"rotated":false}},
{"SPRITE":{"name":"214","x":3449,"y":935,"w":81,"h":188,"rotated":false}},
{"SPRITE":{"name":"215","x":2841,"y":950,"w":200,"h":185,"rotated":false}},
{"SPRITE":{"name":"216","x":3384,"y":1150,"w":63,"h":99,"rotated":false}},
{"SPRITE":{"name":"217","x":2481,"y":398,"w":63,"h":99,"rotated":false}},
{"SPRITE":{"name":"218","x":3558,"y":600,"w":132,"h":105,"rotated":false}},
{"SPRITE":{"name":"219","x":3531,"y":856,"w":132,"h":105,"rotated":false}},
{"SPRITE":{"name":"220","x":2590,"y":2,"w":231,"h":184,"rotated":false}},
{"SPRITE":{"name":"221","x":2670,"y":950,"w":170,"h":224,"rotated":false}},
{"SPRITE":{"name":"222","x":2273,"y":749,"w":198,"h":144,"rotated":false}},
{"SPRITE":{"name":"223","x":3691,"y":962,"w":93,"h":53,"rotated":false}},
{"SPRITE":{"name":"224","x":2979,"y":1183,"w":186,"h":91,"rotated":false}},
{"SPRITE":{"name":"225","x":1939,"y":733,"w":333,"h":164,"rotated":false}},
{"SPRITE":{"name":"226","x":935,"y":2,"w":415,"h":354,"rotated":false}},
{"SPRITE":{"name":"227","x":1220,"y":357,"w":402,"h":353,"rotated":false}},
{"SPRITE":{"name":"228","x":3998,"y":84,"w":90,"h":103,"rotated":false}},
{"SPRITE":{"name":"229","x":2375,"y":158,"w":214,"h":239,"rotated":false}},
{"SPRITE":{"name":"230","x":2262,"y":898,"w":212,"h":237,"rotated":false}}]},
"meta":{
"app":"Adobe Animate (Better TA Extension)",
"version":"22.0.3.179",
"image":"spritemap1.png",
"format":"RGBA8888",
"size":{"w":4096,"h":1280},
"resolution":"1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

+11 -3
View File
@@ -203,6 +203,7 @@
<str id="paste">Paste</str> <str id="paste">Paste</str>
<str id="cut">Cut</str> <str id="cut">Cut</str>
<str id="delete">Delete</str> <str id="delete">Delete</str>
<str id="deletestacked">Delete Stacked Notes</str>
</group> </group>
<str id="topBar.chart">Chart</str> <str id="topBar.chart">Chart</str>
@@ -223,6 +224,7 @@
<str id="resetZoom">Reset Zoom</str> <str id="resetZoom">Reset Zoom</str>
<str id="showSectionsSeparator">Show Sections Separator</str> <str id="showSectionsSeparator">Show Sections Separator</str>
<str id="showBeatsSeparator">Show Beats Separator</str> <str id="showBeatsSeparator">Show Beats Separator</str>
<str id="showCameraHighlights">Show Camera Highlights</str>
<str id="rainbowWaveforms">Rainbow Waveforms</str> <str id="rainbowWaveforms">Rainbow Waveforms</str>
<str id="lowDetailWaveforms">Low Detail Waveforms</str> <str id="lowDetailWaveforms">Low Detail Waveforms</str>
<str id="scrollLeft">Scroll Left</str> <str id="scrollLeft">Scroll Left</str>
@@ -236,8 +238,9 @@
<str id="goEnd">Go forward to the end</str> <!-- used to be "Go to the end" --> <str id="goEnd">Go forward to the end</str> <!-- used to be "Go to the end" -->
<str id="addOpponentCamera">Add camera on Opponent</str> <str id="addOpponentCamera">Add camera on Opponent</str>
<str id="addPlayerCamera">Add camera on Player</str> <str id="addPlayerCamera">Add camera on Player</str>
<str id="muteInst">Mute instrumental</str> <str id="inst">Instrumental</str>
<str id="muteVoices">Mute voices</str> <str id="voices">Voices</str>
<str id="hitsounds">Hitsounds</str>
</group> </group>
<group name="BookmarkMenu" prefix="bookmarks."> <group name="BookmarkMenu" prefix="bookmarks.">
@@ -250,6 +253,7 @@
<str id="editBookmarkListTitle">Bookmark List</str> <str id="editBookmarkListTitle">Bookmark List</str>
<str id="editBookmarksTitle">Edit Bookmarks</str> <str id="editBookmarksTitle">Edit Bookmarks</str>
<str id="newBookmarkName">New Bookmark</str> <str id="newBookmarkName">New Bookmark</str>
<str id="bookmarkList">Bookmark List</str>
</group> </group>
<str id="topBar.note">Note</str> <str id="topBar.note">Note</str>
@@ -258,6 +262,7 @@
<str id="subtractSustainLength">Subtract sustain length</str> <str id="subtractSustainLength">Subtract sustain length</str>
<str id="selectAll">Select all</str> <str id="selectAll">Select all</str>
<str id="selectMeasure">Select measure</str> <str id="selectMeasure">Select measure</str>
<str id="noteTypesList">Note Types List</str>
<str id="editNoteTypesList">Edit Note Types List</str> <str id="editNoteTypesList">Edit Note Types List</str>
</group> </group>
<str id="topBar.snap">Snap</str> <str id="topBar.snap">Snap</str>
@@ -308,8 +313,9 @@
<group name="StrumlineOptions" prefix="strumLine."> <group name="StrumlineOptions" prefix="strumLine.">
<str id="button-name">Options ↓</str> <str id="button-name">Options ↓</str>
<str id="waveforms">Waveforms</str>
<str id="hitsounds">Hitsounds</str> <str id="hitsounds">Hitsounds</str>
<str id="muteVocals">Mute Vocals</str> <str id="vocals">Vocals</str>
<str id="edit">Edit</str> <str id="edit">Edit</str>
<str id="delete">Delete</str> <str id="delete">Delete</str>
@@ -433,6 +439,8 @@
<str id="position">Position (X, Y)</str> <str id="position">Position (X, Y)</str>
<str id="fps">FPS</str> <str id="fps">FPS</str>
<str id="indices">Indices (Frames)</str> <str id="indices">Indices (Frames)</str>
<str id="looping">Looping?</str>
<str id="label">Label</str>
</group> </group>
<group name="CharacterSpriteScreen" prefix="characterSpriteScreen."> <group name="CharacterSpriteScreen" prefix="characterSpriteScreen.">
+1
View File
@@ -188,6 +188,7 @@
<str id="paste">Pegar</str> <str id="paste">Pegar</str>
<str id="cut">Cortar</str> <str id="cut">Cortar</str>
<str id="delete">Eliminar</str> <str id="delete">Eliminar</str>
<str id="deletestacked">Eliminar Notas Apiladas</str>
</group> </group>
<str id="topBar.chart">Chart</str> <str id="topBar.chart">Chart</str>
+1
View File
@@ -194,6 +194,7 @@
<str id="paste">Incolla</str> <str id="paste">Incolla</str>
<str id="cut">Taglia</str> <str id="cut">Taglia</str>
<str id="delete">Elimina</str> <str id="delete">Elimina</str>
<str id="deletestacked">Elimina Note Impilate</str>
</group> </group>
<str id="topBar.chart">Chart</str> <str id="topBar.chart">Chart</str>
+1
View File
@@ -197,6 +197,7 @@
<str id="paste">Wklej</str> <str id="paste">Wklej</str>
<str id="cut">Wytnij</str> <str id="cut">Wytnij</str>
<str id="delete">Usuń</str> <str id="delete">Usuń</str>
<str id="deletestacked">Usuń Notatki Ułożone w Stos</str>
</group> </group>
<str id="topBar.chart">Mapa</str> <str id="topBar.chart">Mapa</str>
+1
View File
@@ -203,6 +203,7 @@
<str id="paste">Colar</str> <str id="paste">Colar</str>
<str id="cut">Cortar</str> <str id="cut">Cortar</str>
<str id="delete">Apagar</str> <str id="delete">Apagar</str>
<str id="deletestacked">Apagar Notas Empilhadas</str>
</group> </group>
<str id="topBar.chart">Mapa</str> <str id="topBar.chart">Mapa</str>
+1 -1
View File
@@ -12,7 +12,7 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225); tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true; tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/guns-tankman')); tankman.loadSprite(Paths.image('game/cutscenes/tank/guns-tankman'));
tankman.animateAtlas.anim.addBySymbol('tank', 'TANK TALK 2', 0, false); tankman.addAnim('tank', 'TANK TALK 2', 0, false);
game.insert(game.members.indexOf(game.dad), tankman); game.insert(game.members.indexOf(game.dad), tankman);
game.dad.visible = false; game.dad.visible = false;
+13 -9
View File
@@ -33,20 +33,20 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225); tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true; tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/stress-tankman')); tankman.loadSprite(Paths.image('game/cutscenes/tank/stress-tankman'));
tankman.animateAtlas.anim.addBySymbol('p1', 'TANK TALK 3 P1 UNCUT', 0, false); tankman.addAnim('p1', 'TANK TALK 3 P1 UNCUT', 0, false);
tankman.animateAtlas.anim.addBySymbol('p2', 'TANK TALK 3 P2 UNCUT', 0, false); tankman.addAnim('p2', 'TANK TALK 3 P2 UNCUT', 0, false);
tankman.playAnim('p1'); tankman.playAnim('p1');
pico = new FunkinSprite(game.gf.x + game.gf.globalOffset.x + 150, game.gf.y + game.gf.globalOffset.y + 395); pico = new FunkinSprite(game.gf.x + game.gf.globalOffset.x - 615, game.gf.y + game.gf.globalOffset.y - 130);
pico.antialiasing = true; pico.antialiasing = true;
pico.loadSprite(Paths.image('game/cutscenes/tank/stress-pico')); pico.loadSprite(Paths.image('game/cutscenes/tank/stress-pico'));
pico.animateAtlas.anim.addBySymbol('die', 'GF Time to Die sequence', 24, false); pico.addAnim('die', 'die', 24, false, null, null, 0, 0, null, true);
pico.animateAtlas.anim.addBySymbol('saves', 'Pico Saves them sequence', 24, false); pico.addAnim('saves', 'saves', 24, false, null, null, 0, 0, null, true);
pico.animateAtlas.anim.addBySymbol('idle', 'Pico Dual Wield on Speaker idle', 24, true); pico.addAnim('idle', 'idle', 24, true, null, null, 0, 0, null, true);
pico.scrollFactor.set(0.95, 0.95); pico.scrollFactor.set(0.95, 0.95);
pico.playAnim("idle"); pico.playAnim("idle");
pico.visible = false; pico.visible = false;
game.insert(game.members.indexOf(game.gf) + 1, pico); game.insert(game.members.indexOf(game.gf) + 2, pico);
game.insert(game.members.indexOf(game.dad) + 1, tankman); game.insert(game.members.indexOf(game.dad) + 1, tankman);
game.dad.visible = false; game.dad.visible = false;
@@ -59,7 +59,11 @@ function update(elapsed) {
lipSync(tankman, 0, 16750); lipSync(tankman, 0, 16750);
if (stressCutscene.time > 15100) { if (stressCutscene.time > 15100) {
step = 1; step = 1;
focusOn(game.gf);
//focusOn(game.gf);
game.camFollow.x += 350;
game.camFollow.y -= 200;
pico.visible = true; pico.visible = true;
pico.playAnim('die', true); pico.playAnim('die', true);
@@ -118,7 +122,7 @@ function update(elapsed) {
} }
function lipSync(char:FunkinSprite, begin:Float, end:Float) { function lipSync(char:FunkinSprite, begin:Float, end:Float) {
char.animateAtlas.anim.curFrame = Std.int(FlxMath.remapToRange(stressCutscene.time, begin, end, 0, char.animateAtlas.anim.length-1)); char.anim.curAnim.curFrame = Std.int(FlxMath.remapToRange(stressCutscene.time, begin, end, 0, char.anim.curAnim.numFrames - 1));
} }
function focusOn(char, snap:Bool = false) { function focusOn(char, snap:Bool = false) {
+2 -2
View File
@@ -12,8 +12,8 @@ function create() {
tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225); tankman = new FunkinSprite(game.dad.x + game.dad.globalOffset.x + 520, game.dad.y + game.dad.globalOffset.y + 225);
tankman.antialiasing = true; tankman.antialiasing = true;
tankman.loadSprite(Paths.image('game/cutscenes/tank/ugh-tankman')); tankman.loadSprite(Paths.image('game/cutscenes/tank/ugh-tankman'));
tankman.animateAtlas.anim.addBySymbol('1', 'TANK TALK 1 P1', 0, false); tankman.addAnim('1', 'TANK TALK 1 P1', 0, false);
tankman.animateAtlas.anim.addBySymbol('2', 'TANK TALK 1 P2', 0, false); tankman.addAnim('2', 'TANK TALK 1 P2', 0, false);
game.insert(game.members.indexOf(game.dad), tankman); game.insert(game.members.indexOf(game.dad), tankman);
game.dad.visible = false; game.dad.visible = false;
+8 -9
View File
@@ -4,21 +4,18 @@
<print pretty="true">Preparing installation...</print> <print pretty="true">Preparing installation...</print>
<!-- OpenFL & Lime (Required for Flixel) --> <!-- OpenFL & Lime (Required for Flixel) -->
<git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" /> <git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" ref="old" />
<lib name="lime" version="8.1.2" /> <lib name="lime" version="8.1.2" />
<!-- <git name="lime" url="https://github.com/CodenameCrew/cne-lime" /> disabled for now until fixed -->
<!-- Flixel --> <!-- Flixel -->
<git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" /> <git name="flixel" url="https://github.com/CodenameCrew/cne-flixel" ref="old" />
<git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" /> <git name="flixel-addons" url="https://github.com/CodenameCrew/cne-flixel-addons" ref="old" />
<!-- Other Libraries --> <!-- Other Libraries -->
<git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" /> <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" ref="old" skipDeps="true" />
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" /> <lib name="funkin-modchart" skipDeps="true" />
<lib name="funkin-modchart" version="1.2.4" skipDeps="true" />
<lib name="hxvlc" version="1.9.3" skipDeps="true" /> <lib name="hxvlc" version="1.9.3" skipDeps="true" />
<git name="bin-serializer" url="https://github.com/CodenameCrew/bin-serializer" />
<!-- Documentation and other features --> <!-- Documentation and other features -->
<git name="away3d" url="https://github.com/CodenameCrew/away3d" /> <git name="away3d" url="https://github.com/CodenameCrew/away3d" />
@@ -32,7 +29,9 @@
<lib global="true" name="hxp" /> <lib name="hxp" /> <lib global="true" name="hxp" /> <lib name="hxp" />
<lib global="true" name="format" /> <lib name="format" /> <lib global="true" name="format" /> <lib name="format" />
<git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" /> <git name="hxcpp" url="https://github.com/CodenameCrew/cne-hxcpp" ref="old" />
<git name="flixel-animate" url="https://github.com/CodenameCrew/cne-flixel-animate/" ref="old" skipDeps="true"/>
</if> </if>
<!-- Hxcpp Building --> <!-- Hxcpp Building -->
+12 -7
View File
@@ -3,16 +3,19 @@
<!-- _________________________ Application Settings _________________________ --> <!-- _________________________ Application Settings _________________________ -->
<app <app
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main" title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
file="CodenameEngine" packageName="com.codenamecrew.codenameengine" file="CodenameEngine" packageName="com.yoshman29.codenameengine"
package="com.codenamecrew.codenameengine" package="com.yoshman29.codenameengine"
version="1.0.1" company="CodenameCrew" /> version="1.0.1" company="Yoshman29" />
<!-- <!--
CHANGE THE SAVE PATH & NAME FOR YOUR HARDCODED MOD HERE! CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
--> -->
<haxedef name="SAVE_PATH" value="CodenameCrew/CodenameEngine"/> <haxedef name="SAVE_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_NAME" value="save-default"/> <haxedef name="SAVE_NAME" value="save-default"/>
<haxedef name="SAVE_OPTIONS_PATH" value="CodenameEngine"/>
<haxedef name="SAVE_OPTIONS_NAME" value="options"/>
<!--Switch Export with Unique ApplicationID and Icon--> <!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000" /> <set name="APP_ID" value="0x0100f6c013bbc000" />
@@ -71,6 +74,9 @@
Reduces compilation time at the cost of limited HScript flexibility. !--> Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" /> <define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable abstract support in hscript. !-->
<haxedef name="HSCRIPT_ABSTRACT_SUPPORT" unless="STRIPPED_COMPILE" />
<!-- Comment this out to disable multithreading !--> <!-- Comment this out to disable multithreading !-->
<haxedef name="ALLOW_MULTITHREADING" unless="web || flash" /> <haxedef name="ALLOW_MULTITHREADING" unless="web || flash" />
@@ -127,8 +133,7 @@
<haxelib name="hxvlc" if="VIDEO_CUTSCENES" /> <haxelib name="hxvlc" if="VIDEO_CUTSCENES" />
<haxelib name="away3d" if="THREE_D_SUPPORT" /> <haxelib name="away3d" if="THREE_D_SUPPORT" />
<haxelib name="format" /> <haxelib name="format" />
<haxelib name="flxanimate" /> <haxelib name="flixel-animate" />
<haxelib name="bin-serializer" />
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/> <haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
<haxelib name="hscript-improved" /> <haxelib name="hscript-improved" />
-92
View File
@@ -1,92 +0,0 @@
package funkin.backend;
import flixel.graphics.frames.FlxFrame;
import flixel.math.FlxAngle;
import flixel.math.FlxMatrix;
import openfl.display.BlendMode;
import openfl.geom.ColorTransform;
class FlxAnimate extends flxanimate.FlxAnimate {
static var rMatrix = new FlxMatrix();
override function drawLimb(limb:FlxFrame, _rMatrix:FlxMatrix, ?colorTransform:ColorTransform, ?blendMode:BlendMode)
{
if (alpha == 0 || colorTransform != null && (colorTransform.alphaMultiplier == 0 || colorTransform.alphaOffset == -255) || limb == null || limb.type == EMPTY)
return;
if (blendMode == null)
blendMode = BlendMode.NORMAL;
for (camera in cameras)
{
rMatrix.identity();
limb.prepareMatrix(rMatrix, FlxFrameAngle.ANGLE_0, _checkFlipX() != camera.flipX, _checkFlipY() != camera.flipY);
rMatrix.concat(_rMatrix);
if (!camera.visible || !camera.exists || !limbOnScreen(limb, _rMatrix, camera))
return;
getScreenPosition(_point, camera).subtractPoint(offset);
rMatrix.translate(-origin.x, -origin.y);
if (limb != _pivot) {
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
var cos = Math.cos(angleOff);
var sin = Math.sin(angleOff);
// cos doesn't need to be negated
rMatrix.rotateWithTrig(cos, -sin);
rMatrix.translate(-frameOffset.x, -frameOffset.y);
rMatrix.rotateWithTrig(cos, sin);
}
else
{
rMatrix.translate(-frameOffset.x, -frameOffset.y);
}
rMatrix.scale(scale.x, scale.y);
if (!matrixExposed && bakedRotationAngle <= 0)
{
updateTrig();
if (angle != 0)
rMatrix.rotateWithTrig(_cosAngle, _sinAngle);
}
}
else
rMatrix.a = rMatrix.d = 0.7 / camera.zoom;
if (matrixExposed)
{
rMatrix.concat(transformMatrix);
}
else
{
rMatrix.concat(@:privateAccess flxanimate.FlxAnimate._skewMatrix);
}
_point.addPoint(origin);
if (isPixelPerfectRender(camera))
{
_point.floor();
}
rMatrix.translate(_point.x, _point.y);
camera.drawPixels(limb, null, rMatrix, colorTransform, blendMode, antialiasing, shaderEnabled ? shader : null);
#if FLX_DEBUG
FlxBasic.visibleCount++;
#end
}
// doesn't work, needs to be remade
// #if FLX_DEBUG
// if (FlxG.debugger.drawDebug)
// drawDebug();
// #end
}
override function limbOnScreen(limb:FlxFrame, m:FlxMatrix, ?Camera:FlxCamera)
{
// TODO: ACTUAL OPTIMIZATION
return true;
}
}
+76 -144
View File
@@ -1,5 +1,7 @@
package funkin.backend; package funkin.backend;
import animate.FlxAnimate;
import animate.FlxAnimateController.FlxAnimateAnimation;
import flixel.addons.effects.FlxSkewedSprite; import flixel.addons.effects.FlxSkewedSprite;
import flixel.animation.FlxAnimation; import flixel.animation.FlxAnimation;
import flixel.math.FlxMatrix; import flixel.math.FlxMatrix;
@@ -7,7 +9,6 @@ import flixel.math.FlxPoint;
import flixel.math.FlxRect; import flixel.math.FlxRect;
import flixel.system.FlxAssets.FlxGraphicAsset; import flixel.system.FlxAssets.FlxGraphicAsset;
import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfTwo;
import flxanimate.animate.FlxAnim.FlxSymbolAnimation;
import funkin.backend.scripting.events.sprite.PlayAnimContext; import funkin.backend.scripting.events.sprite.PlayAnimContext;
import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.interfaces.IBeatReceiver;
import funkin.backend.system.interfaces.IOffsetCompatible; import funkin.backend.system.interfaces.IOffsetCompatible;
@@ -15,6 +16,10 @@ import funkin.backend.utils.XMLUtil.AnimData;
import funkin.backend.utils.XMLUtil.BeatAnim; import funkin.backend.utils.XMLUtil.BeatAnim;
import funkin.backend.utils.XMLUtil.IXMLEvents; import funkin.backend.utils.XMLUtil.IXMLEvents;
import haxe.io.Path; import haxe.io.Path;
import flixel.graphics.frames.FlxFrame;
import flixel.math.FlxAngle;
import animate.internal.RenderTexture;
import animate.FlxAnimateFrames;
enum abstract XMLAnimType(Int) enum abstract XMLAnimType(Int)
{ {
@@ -43,7 +48,7 @@ enum abstract XMLAnimType(Int)
} }
} }
class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents
{ {
public var extra:Map<String, Dynamic> = []; public var extra:Map<String, Dynamic> = [];
@@ -56,6 +61,9 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public var animEnabled:Bool = true; public var animEnabled:Bool = true;
public var zoomFactorEnabled:Bool = true; public var zoomFactorEnabled:Bool = true;
//Backwards compatibility
public var animateAtlas(get, never):FunkinSprite;
public var globalCurFrame(get, set):Int; public var globalCurFrame(get, set):Int;
/** /**
@@ -66,9 +74,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public var beatOffset:Int = 0; public var beatOffset:Int = 0;
public var skipNegativeBeats:Bool = false; public var skipNegativeBeats:Bool = false;
public var animateAtlas:FlxAnimate; public var animateSettings:FlxAnimateSettings = {};
@:noCompletion public var atlasPlayingAnim:String;
@:noCompletion public var atlasPath:String;
var _rect2:FlxRect; var _rect2:FlxRect;
@@ -85,6 +91,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
} }
moves = false; moves = false;
applyStageMatrix = true;
} }
/** /**
@@ -100,8 +107,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
@:privateAccess { @:privateAccess {
spr.setPosition(source.x, source.y); spr.setPosition(source.x, source.y);
spr.frames = source.frames; spr.frames = source.frames;
if (casted != null && casted.animateAtlas != null && casted.atlasPath != null)
spr.loadSprite(casted.atlasPath);
spr.animation.copyFrom(source.animation); spr.animation.copyFrom(source.animation);
spr.visible = source.visible; spr.visible = source.visible;
spr.alpha = source.alpha; spr.alpha = source.alpha;
@@ -111,8 +116,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
if (casted != null) { if (casted != null) {
spr.skew.set(casted.skew.x, casted.skew.y); spr.skew.set(casted.skew.x, casted.skew.y);
spr.transformMatrix = casted.transformMatrix;
spr.matrixExposed = casted.matrixExposed;
spr.animOffsets = casted.animOffsets.copy(); spr.animOffsets = casted.animOffsets.copy();
spr.zoomFactor = casted.zoomFactor; spr.zoomFactor = casted.zoomFactor;
} }
@@ -123,8 +126,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public override function update(elapsed:Float) public override function update(elapsed:Float)
{ {
super.update(elapsed); super.update(elapsed);
if (animateAtlas != null)
animateAtlas.update(elapsed);
// hate how it looks like but hey at least its optimized and fast - Nex // hate how it looks like but hey at least its optimized and fast - Nex
if (!debugMode && isAnimFinished()) { if (!debugMode && isAnimFinished()) {
@@ -142,15 +143,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public function loadSprite(path:String, Unique:Bool = false, Key:String = null) public function loadSprite(path:String, Unique:Bool = false, Key:String = null)
{ {
var noExt = Path.withoutExtension(path); var noExt = Path.withoutExtension(path);
if (Assets.exists('$noExt/Animation.json')) frames = Paths.getFrames(path, true, null, null, animateSettings);
{
atlasPath = noExt;
animateAtlas = new FlxAnimate(x, y, noExt);
}
else
{
frames = Paths.getFrames(path, true);
}
return this; return this;
} }
@@ -180,50 +173,22 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
{ {
} }
// ANIMATE ATLAS DRAWING public override function draw() {
#if REGION // re-implementing the `onDraw` functionality from `FlxSprite` since `FlxAnimate` didn't have this, so we have to add it back in ourselves
public override function draw() if (this.isAnimate && this.__drawOverrided) {
{ this.__drawOverrided = false;
if (animateAtlas != null) this.onDraw(this);
{ this.__drawOverrided = true;
copyAtlasValues(); return;
animateAtlas.draw(); }
} super.draw();
else
{
super.draw();
}
} }
public function copyAtlasValues() // ANIMATE ATLAS DRAWING
{ #if REGION
@:privateAccess {
animateAtlas.cameras = cameras; // investigate if we can use _cameras
animateAtlas.scrollFactor = scrollFactor;
animateAtlas.scale = scale;
animateAtlas.offset = offset;
animateAtlas.frameOffset = frameOffset;
animateAtlas.x = x;
animateAtlas.y = y;
animateAtlas.angle = angle;
animateAtlas.alpha = alpha;
animateAtlas.visible = visible;
animateAtlas.flipX = flipX;
animateAtlas.flipY = flipY;
animateAtlas.shader = shader;
animateAtlas.shaderEnabled = shaderEnabled;
animateAtlas.antialiasing = antialiasing;
animateAtlas.skew = skew;
animateAtlas.transformMatrix = transformMatrix;
animateAtlas.matrixExposed = matrixExposed;
animateAtlas.colorTransform = colorTransform;
}
}
public override function destroy() public override function destroy()
{ {
animateAtlas = FlxDestroyUtil.destroy(animateAtlas);
if (animOffsets != null) { if (animOffsets != null) {
for (key in animOffsets.keys()) { for (key in animOffsets.keys()) {
final point = animOffsets[key]; final point = animOffsets[key];
@@ -260,25 +225,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
); );
} }
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
{
if (camera == null)
camera = FlxG.camera;
newRect = super.getScreenBounds(newRect, camera);
if(__shouldDoZoomFactor()) {
__prepareZoomFactor(_rect2, camera);
newRect.set(
(newRect.x - _rect2.x) * _rect2.width + _rect2.x,
(newRect.y - _rect2.y) * _rect2.height + _rect2.y,
newRect.width * _rect2.width,
newRect.height * _rect2.height,
);
}
return newRect;
}
override public function isOnScreen(?camera:FlxCamera):Bool override public function isOnScreen(?camera:FlxCamera):Bool
{ {
if (forceIsOnScreen) if (forceIsOnScreen)
@@ -293,22 +239,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return camera.containsRect(bounds); return camera.containsRect(bounds);
} }
// ZOOM FACTOR RENDERING
public override function doAdditionalMatrixStuff(matrix:FlxMatrix, camera:FlxCamera)
{
// no need to...
//super.doAdditionalMatrixStuff(matrix, camera);
if(__shouldDoZoomFactor()) {
__prepareZoomFactor(_rect, camera);
matrix.setTo(
matrix.a * _rect.width, matrix.b * _rect.height,
matrix.c * _rect.width, matrix.d * _rect.height,
(matrix.tx - _rect.x) * _rect.width + _rect.x,
(matrix.ty - _rect.y) * _rect.height + _rect.y,
);
}
}
// OFFSETTING // OFFSETTING
#if REGION #if REGION
public var animOffsets:Map<String, FlxPoint> = new Map<String, FlxPoint>(); public var animOffsets:Map<String, FlxPoint> = new Map<String, FlxPoint>();
@@ -340,13 +270,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
Force = anim != null && anim.forced; Force = anim != null && anim.forced;
} }
if (animateAtlas != null) animation.play(AnimName, Force, Reversed, Frame);
{
@:privateAccess
animateAtlas.anim.play(AnimName, Force, Reversed, Frame);
atlasPlayingAnim = AnimName;
}
else animation.play(AnimName, Force, Reversed, Frame);
var daOffset = getAnimOffset(AnimName); var daOffset = getAnimOffset(AnimName);
frameOffset.set(daOffset.x, daOffset.y); frameOffset.set(daOffset.x, daOffset.y);
@@ -355,7 +279,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
lastAnimContext = Context; lastAnimContext = Context;
} }
public inline function addAnim(name:String, prefix:String, frameRate:Float = 24, ?looped:Bool, ?forced:Bool, ?indices:Array<Int>, x:Float = 0, y:Float = 0, animType:XMLAnimType = NONE) public inline function addAnim(name:String, prefix:String, frameRate:Float = 24, ?looped:Bool, ?forced:Bool, ?indices:Array<Int>, x:Float = 0, y:Float = 0, animType:XMLAnimType = NONE, animateAtlasLabel:Bool = false)
{ {
return XMLUtil.addAnimToSprite(this, { return XMLUtil.addAnimToSprite(this, {
name: name, name: name,
@@ -366,22 +290,16 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
x: x, x: x,
y: y, y: y,
indices: indices, indices: indices,
forced: forced forced: forced,
label: animateAtlasLabel
}); });
} }
public inline function removeAnim(name:String) public inline function removeAnim(name:String) {
{ animation.remove(name);
if (animateAtlas != null)
@:privateAccess animateAtlas.anim.animsMap.remove(name);
else
animation.remove(name);
} }
public function getAnim(name:String):OneOfTwo<FlxAnimation, FlxSymbolAnimation> public function getAnim(name:String):OneOfTwo<FlxAnimation, FlxAnimateAnimation> {
{
if(animateAtlas != null)
return animateAtlas.anim.getByName(name);
return animation.getByName(name); return animation.getByName(name);
} }
@@ -392,40 +310,26 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return FlxPoint.weak(0, 0); return FlxPoint.weak(0, 0);
} }
public inline function hasAnim(AnimName:String):Bool @:privateAccess public inline function hasAnim(AnimName:String):Bool
return animateAtlas != null ? (animateAtlas.anim.animsMap.exists(AnimName) return animation.exists(AnimName);
|| animateAtlas.anim.symbolDictionary.exists(AnimName)) : animation.exists(AnimName);
public inline function getAnimName() { public inline function getAnimName()
return (animateAtlas != null) ? atlasPlayingAnim : animation.name; return animation.name;
}
public inline function isAnimReversed():Bool { public inline function isAnimReversed():Bool
return animateAtlas != null ? animateAtlas.anim.reversed : animation.curAnim != null ? animation.curAnim.reversed : false; return animation.curAnim?.reversed ?? false;
}
public inline function getNameList():Array<String> { public inline function getNameList():Array<String>
if (animateAtlas != null) return animation.getNameList();
return [for (name in @:privateAccess animateAtlas.anim.animsMap.keys()) name];
else
return animation.getNameList();
}
public inline function stopAnim() public inline function stopAnim()
{ animation.stop();
if (animateAtlas != null)
animateAtlas.anim.pause();
else
animation.stop();
}
public inline function isAnimFinished() { public inline function isAnimFinished()
return animateAtlas != null ? animateAtlas.anim.finished : (animation.curAnim != null ? animation.curAnim.finished : true); return animation.curAnim?.finished ?? true;
}
public inline function isAnimAtEnd() { public inline function isAnimAtEnd()
return animateAtlas != null ? animateAtlas.anim.isAtEnd : (animation.curAnim != null ? animation.curAnim.isAtEnd : false); return animation.curAnim?.isAtEnd ?? false;
}
override function updateAnimation(elapsed:Float) { override function updateAnimation(elapsed:Float) {
if (animEnabled) if (animEnabled)
@@ -447,10 +351,38 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
return beatInterval = v; return beatInterval = v;
} }
@:noCompletion private inline function get_globalCurFrame() { @:noCompletion
return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0); @:deprecated("`FunkinSprite.animateAtlas` is deprecated, just use `FunkinSprite` instead")
} public function get_animateAtlas():FunkinSprite
return isAnimate ? this : null;
@:noCompletion private inline function get_globalCurFrame()
return animation.curAnim?.curFrame ?? 0;
@:noCompletion private inline function set_globalCurFrame(val:Int) { @:noCompletion private inline function set_globalCurFrame(val:Int) {
return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val); if (animation.curAnim != null)
animation.curAnim.curFrame = val;
return val;
}
override function prepareDrawMatrix(matrix:FlxMatrix, camera:FlxCamera):Void {
super.prepareDrawMatrix(matrix, camera);
if (__shouldDoZoomFactor()) {
__prepareZoomFactor(_rect2, camera);
matrix.setTo(
matrix.a * _rect2.width, matrix.b * _rect2.height,
matrix.c * _rect2.width, matrix.d * _rect2.height,
(matrix.tx - _rect2.x) * _rect2.width + _rect2.x,
(matrix.ty - _rect2.y) * _rect2.height + _rect2.y,
);
}
}
override function checkFlipX() {
return super.checkFlipX() != camera.flipX;
}
override function checkFlipY() {
return super.checkFlipY() != camera.flipY;
} }
} }
+2 -6
View File
@@ -114,12 +114,8 @@ class MusicBeatTransition extends MusicBeatSubstate {
} }
if (transitionSprite != null) { if (transitionSprite != null) {
if (transitionSprite.animateAtlas == null) { transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height);
transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height); transitionSprite.updateHitbox();
transitionSprite.updateHitbox();
} else {
transitionSprite.screenCenter();
}
} }
} }
@@ -5,9 +5,22 @@ import funkin.backend.assets.TranslatedAssetLibrary;
#end #end
import funkin.backend.assets.IModsAssetLibrary; import funkin.backend.assets.IModsAssetLibrary;
import lime.utils.AssetLibrary; import lime.utils.AssetLibrary;
import haxe.ds.Map;
class AssetsLibraryList extends AssetLibrary { class AssetsLibraryList extends AssetLibrary {
public var libraries:Array<AssetLibrary> = []; public var libraries:Array<AssetLibrary> = [];
public var cleanLibraries(get, never):Array<AssetLibrary>;
function get_cleanLibraries():Array<AssetLibrary> {
return [for (l in libraries) getCleanLibrary(l)];
}
// is true if any library in `libraries` contains some kind of compressed library.
public var hasCompressedLibrary(get, never):Bool;
function get_hasCompressedLibrary():Bool {
for (l in libraries) if (getCleanLibrary(l).isCompressed) return true;
return false;
}
@:allow(funkin.backend.system.Main) @:allow(funkin.backend.system.Main)
@:allow(funkin.backend.system.MainState) @:allow(funkin.backend.system.MainState)
@@ -141,6 +154,16 @@ class AssetsLibraryList extends AssetLibrary {
public override inline function getAsset(id:String, type:String):Dynamic public override inline function getAsset(id:String, type:String):Dynamic
return getSpecificAsset(id, type, BOTH); return getSpecificAsset(id, type, BOTH);
public override function list(type:String):Array<String> {
// idk if there's a more efficient way tbh, correct if u find better
var files:Map<String, Bool> = [];
for(k=>l in libraries) {
for(f in l.list(type))
files.set(f, false);
}
return [for(k=>e in files) k];
}
public override function isLocal(id:String, type:String) { public override function isLocal(id:String, type:String) {
return true; return true;
} }
+13 -21
View File
@@ -83,10 +83,11 @@ class ModsFolder {
*/ */
public static function loadModLib(path:String, force:Bool = false, ?modName:String) { public static function loadModLib(path:String, force:Bool = false, ?modName:String) {
#if MOD_SUPPORT #if MOD_SUPPORT
if (FileSystem.exists('$path.zip')) for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force, modName); if (!FileSystem.exists('$path.$ext')) continue;
else return loadLibraryFromZip('$path'.toLowerCase(), '$path.$ext', force, modName);
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName); }
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
#else #else
return null; return null;
@@ -96,27 +97,16 @@ class ModsFolder {
public static function getModsList():Array<String> { public static function getModsList():Array<String> {
var mods:Array<String> = []; var mods:Array<String> = [];
#if MOD_SUPPORT #if MOD_SUPPORT
if (!FileSystem.exists(modsPath)) { // Mods directory does not exist yet, create it
// Mods directory does not exist yet, create it if (!FileSystem.exists(modsPath)) FileSystem.createDirectory(modsPath);
FileSystem.createDirectory(modsPath);
}
final modsList:Array<String> = FileSystem.readDirectory(modsPath); final modsList:Array<String> = FileSystem.readDirectory(modsPath);
if (modsList == null || modsList.length <= 0) if (modsList == null || modsList.length <= 0) return mods;
return mods;
for (modFolder in modsList) { for (modFolder in modsList) {
if (FileSystem.isDirectory(modsPath + modFolder)) { if (FileSystem.isDirectory(modsPath + modFolder)) mods.push(modFolder);
mods.push(modFolder); else if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(modFolder))) mods.push(Path.withoutExtension(modFolder));
} else {
var ext = Path.extension(modFolder).toLowerCase();
switch(ext) {
case 'zip':
// is a zip mod!!
mods.push(Path.withoutExtension(modFolder));
}
}
} }
#end #end
return mods; return mods;
@@ -128,7 +118,9 @@ class ModsFolder {
#if TRANSLATIONS_SUPPORT #if TRANSLATIONS_SUPPORT
if(skipTranslated && (l is TranslatedAssetLibrary)) continue; if(skipTranslated && (l is TranslatedAssetLibrary)) continue;
#end #end
if (l is ScriptedAssetLibrary || l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary)); // No need to check for it being a `ScriptedAssetLibrary`, if `ScriptedAssetLibrary` extends ModsFolderLibrary, which implements `IModsAssetLibrary`
// If you have to revert this change then uhhhhh wasn't me, trust 🙏
if (/*l is ScriptedAssetLibrary ||*/ l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary));
} }
return libs; return libs;
} }
@@ -149,6 +149,22 @@ class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
} }
return true; return true;
} }
public override function list(type:String):Array<String> {
var result = [];
__listAppend(result, '');
return result;
}
function __listAppend(arr:Array<String>, folder:String) {
for(file in FileSystem.readDirectory('$basePath/$folder')) {
var fullPath = '$basePath/$folder/$file';
if (FileSystem.isDirectory(fullPath))
__listAppend(arr, '$folder$file/');
else
arr.push('$prefix$folder$file');
}
}
#end #end
// Backwards compat // Backwards compat
+9 -3
View File
@@ -8,6 +8,7 @@ import funkin.backend.scripting.Script;
import haxe.io.Path; import haxe.io.Path;
import lime.utils.AssetLibrary; import lime.utils.AssetLibrary;
import openfl.utils.Assets as OpenFlAssets; import openfl.utils.Assets as OpenFlAssets;
import animate.FlxAnimateFrames;
using StringTools; using StringTools;
@@ -174,6 +175,9 @@ class Paths
inline static public function getSparrowAtlas(key:String, ?library:String, ?ext:String) inline static public function getSparrowAtlas(key:String, ?library:String, ?ext:String)
return FlxAtlasFrames.fromSparrow(image(key, library, ext), file('images/$key.xml', library)); return FlxAtlasFrames.fromSparrow(image(key, library, ext), file('images/$key.xml', library));
inline static public function getAnimateAtlasAlt(key:String, ?settings:FlxAnimateSettings)
return FlxAnimateFrames.fromAnimate(key, null, null, null, false, settings);
inline static public function getSparrowAtlasAlt(key:String, ?ext:String) inline static public function getSparrowAtlasAlt(key:String, ?ext:String)
return FlxAtlasFrames.fromSparrow('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.xml'); return FlxAtlasFrames.fromSparrow('$key.${ext != null ? ext : Flags.IMAGE_EXT}', '$key.xml');
@@ -197,7 +201,7 @@ class Paths
* @param key Path to the frames * @param key Path to the frames
* @param library (Additional) library to load the frames from. * @param library (Additional) library to load the frames from.
*/ */
public static function getFrames(key:String, assetsPath:Bool = false, ?library:String, ?ext:String = null) { public static function getFrames(key:String, assetsPath:Bool = false, ?library:String, ?ext:String = null, ?animateSettings:FlxAnimateSettings) {
if (tempFramesCache.exists(key)) { if (tempFramesCache.exists(key)) {
var frames = tempFramesCache[key]; var frames = tempFramesCache[key];
if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable) if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable)
@@ -205,7 +209,7 @@ class Paths
else else
tempFramesCache.remove(key); tempFramesCache.remove(key);
} }
return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext); return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext, animateSettings);
} }
/** /**
@@ -242,7 +246,7 @@ class Paths
* @param Ext Extension of the image. * @param Ext Extension of the image.
* @return FlxFramesCollection Frames * @return FlxFramesCollection Frames
*/ */
static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false, SkipMultiCheck:Bool = false, ?Ext:String = null):FlxFramesCollection { static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false, SkipMultiCheck:Bool = false, ?Ext:String = null, ?animateSettings:FlxAnimateSettings):FlxFramesCollection {
var noExt = Path.withoutExtension(path); var noExt = Path.withoutExtension(path);
var ext = Ext != null ? Ext : Flags.IMAGE_EXT; var ext = Ext != null ? Ext : Flags.IMAGE_EXT;
@@ -263,6 +267,8 @@ class Paths
cur++; cur++;
} }
return finalFrames; return finalFrames;
} else if (Assets.exists('$noExt/Animation.json')) {
return Paths.getAnimateAtlasAlt(noExt, animateSettings);
} else if (Assets.exists('$noExt.xml')) { } else if (Assets.exists('$noExt.xml')) {
return Paths.getSparrowAtlasAlt(noExt, ext); return Paths.getSparrowAtlasAlt(noExt, ext);
} else if (Assets.exists('$noExt.txt')) { } else if (Assets.exists('$noExt.txt')) {
@@ -108,6 +108,8 @@ class TranslatedAssetLibrary extends AssetLibrary implements IModsAssetLibrary {
return null; return null;
} }
// TODO: list support for TranslatedAssetLibrary (idk how it works)
#if MOD_SUPPORT #if MOD_SUPPORT
public var _parsedAsset:String = null; // Theres no need to actually make this work - Nex public var _parsedAsset:String = null; // Theres no need to actually make this work - Nex
@@ -1,11 +1,14 @@
package funkin.backend.assets; package funkin.backend.assets;
import funkin.backend.system.Flags;
import haxe.io.Path; import haxe.io.Path;
import lime.graphics.Image; import lime.graphics.Image;
import lime.media.AudioBuffer; import lime.media.AudioBuffer;
import lime.text.Font; import lime.text.Font;
import lime.utils.Bytes; import lime.utils.Bytes;
import openfl.utils.AssetLibrary; import openfl.utils.AssetLibrary;
import sys.io.File;
#if MOD_SUPPORT #if MOD_SUPPORT
import funkin.backend.utils.SysZip.SysZipEntry; import funkin.backend.utils.SysZip.SysZipEntry;
@@ -15,7 +18,6 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var basePath:String; public var basePath:String;
public var modName:String; public var modName:String;
public var libName:String; public var libName:String;
public var useImageCache:Bool = false;
public var prefix = 'assets/'; public var prefix = 'assets/';
public var zip:SysZip; public var zip:SysZip;
@@ -23,7 +25,9 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var lowerCaseAssets:Map<String, SysZipEntry> = []; public var lowerCaseAssets:Map<String, SysZipEntry> = [];
public var nameMap:Map<String, String> = []; public var nameMap:Map<String, String> = [];
public function new(basePath:String, libName:String, ?modName:String) { public var PRELOAD_VIDEOS:Bool = true;
public function new(basePath:String, libName:String, ?modName:String, ?preloadVideos:Bool = true) {
this.libName = libName; this.libName = libName;
this.basePath = basePath; this.basePath = basePath;
@@ -31,21 +35,59 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
this.modName = (modName == null) ? libName : modName; this.modName = (modName == null) ? libName : modName;
zip = SysZip.openFromFile(basePath); zip = SysZip.openFromFile(basePath);
zip.read();
for(entry in zip.entries) { for(entry in zip.entries) {
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry; if (entry.fileName.length < 0 || entry.fileName.endsWith("/")) continue;
nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
var name:String = entry.fileName.toLowerCase(); // calling .toLowerCase a million times is never the solution
lowerCaseAssets[name] = assets[name] = assets[entry.fileName] = entry;
nameMap.set(name, entry.fileName);
} }
super(); super();
isCompressed = true;
// don't override default value of true if the file exists.
// by default `PRELOAD_VIDEOS` is true so you will never need to add this file, but in the case of it being false this is a backup method.
PRELOAD_VIDEOS = (!PRELOAD_VIDEOS) ? exists("assets/data/PRECACHE_VIDEOS", "TEXT") : PRELOAD_VIDEOS;
// if (PRELOAD_VIDEOS) precacheVideos(); // we do this in `MainState` now to handle for `Flags.VIDEO_EXT` :)
}
public function precacheVideos() {
_videoExtensions = [Flags.VIDEO_EXT];
videoCacheRemap = [];
for (entry in zip.entries) {
var name = entry.fileName.toLowerCase();
if (_videoExtensions.contains(Path.extension(name))) getPath(prefix+name);
}
var count:Int = 0;
for (_ in videoCacheRemap.keys()) count++;
if (count <= 0) return;
trace('Precached $count video${(count == 1) ? "" : "s"}');
}
// Now we have supports for videos in ZIP!!
public var _videoExtensions:Array<String> = [Flags.VIDEO_EXT];
public var videoCacheRemap:Map<String, String> = [];
public function getVideoRemap(originalPath:String):String {
if (!_videoExtensions.contains(Path.extension(_parsedAsset))) return originalPath;
if (videoCacheRemap.exists(originalPath)) return videoCacheRemap.get(originalPath);
// We adding the length of the string to counteract folder in folder naming duplicates.
var newPath = './.temp/${_parsedAsset.length}-zipvideo-${_parsedAsset.split("/").pop()}';
File.saveBytes(newPath, unzip(assets[_parsedAsset]));
videoCacheRemap.set(originalPath, newPath);
return newPath;
} }
function toString():String { function toString():String {
return '(ZipFolderLibrary: $libName/$modName)'; return '(ZipFolderLibrary: $libName/$modName | ${zip.entries.length} entries | Detected Video Extensions: ${_videoExtensions.join(", ")})';
} }
public var _parsedAsset:String; public var _parsedAsset:String;
public override function getAudioBuffer(id:String):AudioBuffer { public override function getAudioBuffer(id:String):AudioBuffer {
__parseAsset(id); __parseAsset(id);
return AudioBuffer.fromBytes(unzip(assets[_parsedAsset])); return AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
@@ -68,15 +110,12 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return getAssetPath(); return getAssetPath();
} }
public inline function unzip(f:SysZipEntry) return (f == null) ? null : zip.unzipEntry(f);
public inline function unzip(f:SysZipEntry)
return f == null ? null : zip.unzipEntry(f);
public function __parseAsset(asset:String):Bool { public function __parseAsset(asset:String):Bool {
if (!asset.startsWith(prefix)) return false; if (!asset.startsWith(prefix)) return false;
_parsedAsset = asset.substr(prefix.length); _parsedAsset = asset.substr(prefix.length);
if(ModsFolder.useLibFile) { if (ModsFolder.useLibFile) {
var file = new haxe.io.Path(_parsedAsset); var file = new haxe.io.Path(_parsedAsset);
if(file.file.startsWith("LIB_")) { if(file.file.startsWith("LIB_")) {
var library = file.file.substr(4); var library = file.file.substr(4);
@@ -87,8 +126,7 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
} }
_parsedAsset = _parsedAsset.toLowerCase(); _parsedAsset = _parsedAsset.toLowerCase();
if(nameMap.exists(_parsedAsset)) if (nameMap.exists(_parsedAsset)) _parsedAsset = nameMap.get(_parsedAsset);
_parsedAsset = nameMap.get(_parsedAsset);
return true; return true;
} }
@@ -103,9 +141,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return assets[_parsedAsset] != null; return assets[_parsedAsset] != null;
} }
private function getAssetPath() { private inline function getAssetPath() {
trace('[ZIP]$basePath/$_parsedAsset'); return getVideoRemap('$basePath/$_parsedAsset');
return '[ZIP]$basePath/$_parsedAsset';
} }
// TODO: rewrite this to 1 function, like ModsFolderLibrary // TODO: rewrite this to 1 function, like ModsFolderLibrary
@@ -154,14 +191,6 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return content; return content;
} }
// Backwards compat public override function list(type:String):Array<String> { return [for(k=>e in nameMap) '$prefix$e']; }
@:noCompletion public var zipPath(get, set):String;
@:noCompletion private inline function get_zipPath():String {
return basePath;
}
@:noCompletion private inline function set_zipPath(value:String):String {
return basePath = value;
}
} }
#end #end
+7 -38
View File
@@ -97,41 +97,6 @@ class Chart {
data.setFieldDefault("vocalsSuffix", ""); data.setFieldDefault("vocalsSuffix", "");
data.setFieldDefault("needsVoices", true); data.setFieldDefault("needsVoices", true);
data.setFieldDefault("difficulties", []); data.setFieldDefault("difficulties", []);
var songNameLower = songName.toLowerCase();
var metaPath = Paths.file('songs/${songNameLower}/meta.json');
var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json');
var blacklist:Array<String> = [];
var data:ChartMetaData = null;
var fromMods:Bool = fromMods;
for (path in [metaDiffPath, metaPath]) {
if (Assets.exists(path)) {
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
try {
var temp = CoolUtil.parseJson(path);
// Backwards compatibility (they both can be null so != true) - Nex
var coopExlc = false;
if (Reflect.hasField(temp, "coopAllowed") && Reflect.field(temp, "coopAllowed") != true) {
blacklist = blacklist.concat(["codename.coop", "codename.coop-opponent"]);
Reflect.deleteField(temp, "coopAllowed");
coopExlc = true;
}
if (Reflect.hasField(temp, "opponentModeAllowed") && Reflect.field(temp, "opponentModeAllowed") != true) {
if (!coopExlc) blacklist.push("codename.coop-opponent");
blacklist.push("codename.opponent");
Reflect.deleteField(temp, "opponentModeAllowed");
}
data = temp;
} catch(e) {
Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
}
if (data != null) break;
}
}
data.setFieldDefault("excludedGameModes", blacklist);
data.setFieldDefault("variants", []); data.setFieldDefault("variants", []);
data.setFieldDefault("metas", []); data.setFieldDefault("metas", []);
@@ -313,9 +278,13 @@ class Chart {
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true); var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true);
#if sys #if sys
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : ""; var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "", difficultySuffix = difficulty != null && difficulty != "" ? '-$difficulty' : "";
var metaPath = 'meta$variantSuffix.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String; var metaPath = 'meta$variantSuffix.json', altMetaPath = 'meta${variantSuffix}${difficultySuffix}.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) { if ((temp = Paths.assetsTree.getPath('assets/$songPath/$altMetaPath')) != null) { //check for difficulty specific
songPath = temp.substr(0, temp.length - altMetaPath.length - 1);
metaPath = temp;
}
else if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
songPath = temp.substr(0, temp.length - metaPath.length - 1); songPath = temp.substr(0, temp.length - metaPath.length - 1);
metaPath = temp; metaPath = temp;
} }
+2 -1
View File
@@ -32,7 +32,8 @@ typedef ChartMetaData = {
public var ?icon:String; public var ?icon:String;
public var ?color:FlxColor; public var ?color:FlxColor;
public var ?excludedGameModes:Array<String>; public var ?coopAllowed:Bool;
public var ?opponentModeAllowed:Bool;
public var ?metas:Map<String, ChartMetaData>; public var ?metas:Map<String, ChartMetaData>;
public var ?instSuffix:String; public var ?instSuffix:String;
+1 -1
View File
@@ -75,7 +75,7 @@ class EventsData {
defValue: "In" defValue: "In"
}, },
{name: "Mode", type: TDropDown(['direct', 'stage']), defValue: "direct"}, {name: "Mode", type: TDropDown(['direct', 'stage']), defValue: "direct"},
{name: "Multiplicative?", type: TBool, defValue: true} {name: "Multiplicative?", type: TBool, defValue: false}
], ],
"Camera Modulo Change" => [ "Camera Modulo Change" => [
{name: "Modulo Interval", type: TInt(1, 9999999, 1), defValue: 4}, {name: "Modulo Interval", type: TInt(1, 9999999, 1), defValue: 4},
@@ -106,12 +106,15 @@ class GlobalScript {
public static function onModSwitch(newMod:String) { public static function onModSwitch(newMod:String) {
destroy(); destroy();
scripts = new ScriptPack("GlobalScript"); scripts = new ScriptPack("GlobalScript");
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) { for (lib in funkin.backend.assets.ModsFolder.getLoadedModsLibs()) {
var path = Paths.script('data/global/LIB_$i'); var modName = lib.modName;
var path = Paths.script('data/global/LIB_$modName');
var script = Script.create(path); var script = Script.create(path);
if (script is DummyScript) if (script is DummyScript) continue;
continue; script.remappedNames.set(script.fileName, '$modName:${script.fileName}');
script.remappedNames.set(script.fileName, '$i:${script.fileName}'); // so you can get the current mod's library in GloablScript :)
// you should not make this a static variable then all scripts will try to reference the 1 static variable, which will be overwritten :yoikes:
script.set("MOD_LIBRARY", lib);
scripts.add(script); scripts.add(script);
script.load(); script.load();
} }
+1 -1
View File
@@ -91,7 +91,7 @@ class Script extends FlxBasic implements IFlxDestroyable {
"FunkinShader" => funkin.backend.shaders.FunkinShader, "FunkinShader" => funkin.backend.shaders.FunkinShader,
"CustomShader" => funkin.backend.shaders.CustomShader, "CustomShader" => funkin.backend.shaders.CustomShader,
"FunkinText" => funkin.backend.FunkinText, "FunkinText" => funkin.backend.FunkinText,
"FlxAnimate" => funkin.backend.FlxAnimate, "FlxAnimate" => animate.FlxAnimate,
"FunkinSprite" => funkin.backend.FunkinSprite, "FunkinSprite" => funkin.backend.FunkinSprite,
"Alphabet" => funkin.menus.ui.Alphabet, "Alphabet" => funkin.menus.ui.Alphabet,
"Flags" => funkin.backend.system.Flags, "Flags" => funkin.backend.system.Flags,
@@ -4,7 +4,6 @@ import flixel.util.FlxDestroyUtil.IFlxDestroyable;
@:allow(funkin.backend.scripting.ScriptPack) @:allow(funkin.backend.scripting.ScriptPack)
@:autoBuild(funkin.backend.system.macros.EventMacro.build()) @:autoBuild(funkin.backend.system.macros.EventMacro.build())
@:noCustomClass
class CancellableEvent implements IFlxDestroyable { class CancellableEvent implements IFlxDestroyable {
@:dox(hide) public var cancelled:Bool = false; @:dox(hide) public var cancelled:Bool = false;
@:dox(hide) private var __continueCalls:Bool = true; @:dox(hide) private var __continueCalls:Bool = true;
@@ -1,7 +1,5 @@
package funkin.backend.scripting.events.menu.freeplay; package funkin.backend.scripting.events.menu.freeplay;
import funkin.menus.FreeplayState.FreeplayGameMode;
final class FreeplaySongSelectEvent extends CancellableEvent { final class FreeplaySongSelectEvent extends CancellableEvent {
/** /**
* Song name that is about to be played * Song name that is about to be played
@@ -16,26 +14,11 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
*/ */
public var variant:String; public var variant:String;
/** /**
* Used game mode's data. * Whenever opponent mode is enabled or not.
*/ */
public var gameMode:FreeplayGameMode; public var opponentMode:Bool;
/**
* Whenever coop mode is enabled.
// Backwards compat */
@:noCompletion public var opponentMode(get, set):Bool; public var coopMode:Bool;
@:noCompletion private function get_opponentMode() return gameMode.modeID == "codename.opponent" || gameMode.modeID == "codename.coop-opponent";
@:noCompletion private function set_opponentMode(val:Bool) {
gameMode = coopMode ?
(val ? new FreeplayGameMode("Co-Op Mode (Switched)", "codename.coop-opponent", ["coop-switched", "opponent", "coop"]) : new FreeplayGameMode("Opponent Mode", "codename.opponent", ["opponent"])) :
(val ? new FreeplayGameMode("Opponent Mode", "codename.opponent", ["opponent"]) : null);
return val;
}
@:noCompletion public var coopMode(get, set):Bool;
@:noCompletion private function get_coopMode() return gameMode.modeID == "codename.coop" || gameMode.modeID == "codename.coop-opponent";
@:noCompletion private function set_coopMode(val:Bool) {
gameMode = opponentMode ?
(val ? new FreeplayGameMode("Co-Op Mode (Switched)", "codename.coop-opponent", ["coop-switched", "opponent", "coop"]) : new FreeplayGameMode("Co-Op Mode", "codename.coop", ["coop"])) :
(val ? new FreeplayGameMode("Co-Op Mode", "codename.coop", ["coop"]) : null);
return val;
}
} }
+8 -8
View File
@@ -13,15 +13,17 @@ import lime.utils.AssetType;
/** /**
* A class that reads the `flags.ini` file, allowing to read settable Flags (customs too). * A class that reads the `flags.ini` file, allowing to read settable Flags (customs too).
*/ */
@:build(funkin.backend.system.macros.FlagMacro.build("onReset")) @:build(funkin.backend.system.macros.FlagMacro.build())
class Flags { class Flags {
public static var overridenFlags:Map<String, Bool> = []; public static var overridenFlags:Map<String, Bool> = [];
// -- Codename's Addon Config -- // -- Codename's Addon Config --
@:bypass public static var addonFlags:Map<String, Dynamic> = []; @:bypass public static var addonFlags:Map<String, Dynamic> = [];
public static var CURRENT_API_VERSION:Int = 2; public static var CURRENT_API_VERSION:Int = 2;
// -- Codename's ZipFolderLibrary Config --
public static var ALLOWED_ZIP_EXTENSIONS:Array<String> = ["zip"];
// -- Codename's Mod Config -- // -- Codename's Mod Config --
public static var MOD_NAME:String = ""; public static var MOD_NAME:String = "";
public static var MOD_DESCRIPTION:String = ""; public static var MOD_DESCRIPTION:String = "";
@@ -162,6 +164,8 @@ class Flags {
public static var DEFAULT_FONT:String = "vcr.ttf"; public static var DEFAULT_FONT:String = "vcr.ttf";
public static var DEFAULT_FONT_SIZE:Int = 16; public static var DEFAULT_FONT_SIZE:Int = 16;
public static var DEFAULT_ALT_ANIM_SUFFIX:String = "-alt";
// to translate these you need to convert them into ids // to translate these you need to convert them into ids
// Resume -> pause.resume // Resume -> pause.resume
// Restart Song -> pause.restart // Restart Song -> pause.restart
@@ -312,11 +316,6 @@ class Flags {
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2; if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
} }
private static function onReset() {
var currentModFolder = ModsFolder.currentModFolder;
if (currentModFolder != null) SAVE_NAME = currentModFolder;
}
public static function loadFromDatas(datas:Array<String>):Map<String, String> { public static function loadFromDatas(datas:Array<String>):Map<String, String> {
var flags:Map<String, String> = []; var flags:Map<String, String> = [];
for (data in datas) { for (data in datas) {
@@ -337,8 +336,9 @@ class Flags {
if (!(parsed = parse(name, value))) customFlags.set(name, value); if (!(parsed = parse(name, value))) customFlags.set(name, value);
if (!overridenFlags.exists(name)) overridenFlags.set(name, parsed); if (!overridenFlags.exists(name)) overridenFlags.set(name, parsed);
} }
#if MODCHARTING_FEATURES
Options.modchartingHoldSubdivisions = DEFAULT_MODCHART_HOLD_SUBDIVISIONS; Options.modchartingHoldSubdivisions = DEFAULT_MODCHART_HOLD_SUBDIVISIONS;
#end
} }
/** /**
+42 -13
View File
@@ -7,12 +7,14 @@ import flixel.FlxState;
import funkin.backend.assets.AssetsLibraryList; import funkin.backend.assets.AssetsLibraryList;
import funkin.backend.assets.ModsFolder; import funkin.backend.assets.ModsFolder;
import funkin.backend.assets.ModsFolderLibrary; import funkin.backend.assets.ModsFolderLibrary;
import funkin.backend.assets.ZipFolderLibrary;
import funkin.backend.chart.EventsData; import funkin.backend.chart.EventsData;
import funkin.backend.system.framerate.Framerate; import funkin.backend.system.framerate.Framerate;
import funkin.editors.ModConfigWarning; import funkin.editors.ModConfigWarning;
import funkin.menus.TitleState; import funkin.menus.TitleState;
import haxe.io.Path; import haxe.io.Path;
@dox(hide) @dox(hide)
typedef AddonInfo = { typedef AddonInfo = {
var name:String; var name:String;
@@ -57,12 +59,32 @@ class MainState extends FlxState {
var _highPriorityAddons:Array<AddonInfo> = []; var _highPriorityAddons:Array<AddonInfo> = [];
var _noPriorityAddons:Array<AddonInfo> = []; var _noPriorityAddons:Array<AddonInfo> = [];
var quick_modsPath = ModsFolder.modsPath + ModsFolder.currentModFolder;
// handing if the loading mod (before it's properly loaded) is a compressed mod
// we just need to use `Paths.assetsTree.hasCompressedLibrary` to complete valid checks for actual loaded compressed mods
var isZipMod = false;
// If we know it's a compressed mod, then we can check if it's using the `cnemod` folder path.
// All it is really is a folder with the mod's name, then a compressed file called "cnemod.[zip|7z|rar|etc]"
var isCneMod = false;
// We are doing it like this because think about it: it's 1 for loop lol
// We just need to know if any of these values is true, so if only one is true and we are not close to being done in the loop, that's fine.
//
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (FileSystem.exists(quick_modsPath+"."+ext)) isZipMod = true;
if (FileSystem.exists(quick_modsPath+"/cnemod."+ext)) isCneMod = true;
if (isZipMod && isCneMod) break;
}
// We get the addons folder from relative space (`./`) and then our mod's addons.
var addonPaths = [ var addonPaths = [
ModsFolder.addonsPath, ModsFolder.addonsPath,
( // So to check the mod's addons folder, we need to decompress it. Which is impossible* in this stage of the loading library process.
ModsFolder.currentModFolder != null ? // TODO: Write a function when the library is loaded to decompress the contents and then load the libraries :)
ModsFolder.modsPath + ModsFolder.currentModFolder + "/addons/" : ( (ModsFolder.currentModFolder != null && !isZipMod) ?
null quick_modsPath + "/addons/" : null
) )
]; ];
@@ -72,12 +94,8 @@ class MainState extends FlxState {
for (addon in FileSystem.readDirectory(path)) { for (addon in FileSystem.readDirectory(path)) {
if (!FileSystem.isDirectory(path + addon)) { if (!FileSystem.isDirectory(path + addon)) {
switch(Path.extension(addon).toLowerCase()) { if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(addon))) addon = Path.withoutExtension(addon);
case 'zip': else continue;
addon = Path.withoutExtension(addon);
default:
continue;
}
} }
var data:AddonInfo = { var data:AddonInfo = {
@@ -101,8 +119,13 @@ class MainState extends FlxState {
for (addon in _lowPriorityAddons) for (addon in _lowPriorityAddons)
loadLib(addon.path, ltrim(addon.name, "[LOW]")); loadLib(addon.path, ltrim(addon.name, "[LOW]"));
if (ModsFolder.currentModFolder != null) if (ModsFolder.currentModFolder != null) {
loadLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder); // isCneMod is a guarentee to be a zip mod because we just checked for it, so this will always load as a CompressedLibrary
if (isCneMod)
loadLib(quick_modsPath + "/cnemod", ModsFolder.currentModFolder);
else
loadLib(quick_modsPath, ModsFolder.currentModFolder);
}
for (addon in _noPriorityAddons) for (addon in _noPriorityAddons)
loadLib(addon.path, addon.name); loadLib(addon.path, addon.name);
@@ -134,9 +157,15 @@ class MainState extends FlxState {
CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN); CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN);
#end #end
for (lib in ModsFolder.getLoadedModsLibs()) {
if (!(lib is ZipFolderLibrary)) continue;
if (cast(lib, ZipFolderLibrary).PRELOAD_VIDEOS) cast(lib, ZipFolderLibrary).precacheVideos();
}
var startState:Class<FlxState> = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState; var startState:Class<FlxState> = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState;
if (Options.devMode && Options.allowConfigWarning) { // In this case if the mod we just loaded a compressed modpack, we can't edit or modify files without decompressing it.
if (Options.devMode && Options.allowConfigWarning && !isZipMod) {
var lib:ModsFolderLibrary; var lib:ModsFolderLibrary;
for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary
&& lib.modName == ModsFolder.currentModFolder) && lib.modName == ModsFolder.currentModFolder)
@@ -13,8 +13,13 @@ class CodenameBuildField extends TextField {
} }
public function reload() { public function reload() {
#if COMPILE_EXPERIMENTAL
text = '${Flags.VERSION_MESSAGE} (Experimental Build)';
#else
text = '${Flags.VERSION_MESSAGE}'; text = '${Flags.VERSION_MESSAGE}';
#if debug #end
#if (debug || COMPILE_EXPERIMENTAL)
text += '\n${Flags.COMMIT_MESSAGE}'; text += '\n${Flags.COMMIT_MESSAGE}';
#end #end
} }
@@ -53,25 +53,6 @@ class SystemInfo extends FramerateCategory {
if (osName != "") if (osName != "")
osInfo = '${osName} ${osVersion}'.trim(); osInfo = '${osName} ${osVersion}'.trim();
} }
#elseif windows
var windowsCurrentVersionPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
var buildNumber = Std.parseInt(RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, "CurrentBuildNumber"));
var edition = RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, "ProductName");
var lcuKey = "WinREVersion"; // Last Cumulative Update Key On Older Windows Versions
if (buildNumber >= 22000) { // Windows 11 Initial Release Build Number
edition = edition.replace("Windows 10", "Windows 11");
lcuKey = "LCUVer"; // Last Cumulative Update Key On Windows 11
}
var lcuVersion = RegistryUtil.get(HKEY_LOCAL_MACHINE, windowsCurrentVersionPath, lcuKey);
osInfo = edition;
if (lcuVersion != null && lcuVersion != "")
osInfo += ' ${lcuVersion}';
else if (lime.system.System.platformVersion != null && lime.system.System.platformVersion != "")
osInfo += ' ${lime.system.System.platformVersion}';
#else #else
if (lime.system.System.platformLabel != null && lime.system.System.platformLabel != "" && lime.system.System.platformVersion != null && lime.system.System.platformVersion != "") if (lime.system.System.platformLabel != null && lime.system.System.platformLabel != "" && lime.system.System.platformVersion != null && lime.system.System.platformVersion != "")
osInfo = '${lime.system.System.platformLabel.replace(lime.system.System.platformVersion, "").trim()} ${lime.system.System.platformVersion}'; osInfo = '${lime.system.System.platformLabel.replace(lime.system.System.platformVersion, "").trim()} ${lime.system.System.platformVersion}';
@@ -81,7 +62,10 @@ class SystemInfo extends FramerateCategory {
try { try {
#if windows #if windows
cpuName = RegistryUtil.get(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "ProcessorNameString"); var process = new HiddenProcess("wmic", ["cpu", "get", "name"]);
if (process.exitCode() != 0) throw 'Could not fetch CPU information';
cpuName = process.stdout.readAll().toString().trim().split("\n")[1].trim();
#elseif mac #elseif mac
var process = new HiddenProcess("sysctl -a | grep brand_string"); // Somehow this isn't able to use the args but it still works var process = new HiddenProcess("sysctl -a | grep brand_string"); // Somehow this isn't able to use the args but it still works
if (process.exitCode() != 0) throw 'Could not fetch CPU information'; if (process.exitCode() != 0) throw 'Could not fetch CPU information';
@@ -0,0 +1,134 @@
package funkin.backend.system.macros;
#if macro
import sys.io.File;
import haxe.macro.ComplexTypeTools;
import haxe.macro.Printer;
import haxe.macro.Expr.Position;
import haxe.macro.Context;
import haxe.macro.Expr.Field;
import haxe.macro.Expr;
class AliasMacro {
public static var aliasMetadata:Metadata = [{
name: ":keep",
pos: Context.currentPos()
},{
name: ":dox",
pos: Context.currentPos(),
params: [
{
expr: EConst(CIdent("hide")),
pos: Context.currentPos()
}
]
}];
public static function build():Array<Field> {
#if display
return Context.getBuildFields();
#end
var fields = Context.getBuildFields();
var ogFieldCount = fields.length;
for(i in 0...ogFieldCount) {
var field = fields[i];
if (field.meta == null) continue;
var previousAliases:Array<{name:String, pos:Position}> = [];
for(meta in field.meta) {
if (meta.name == ":alias" && meta.params != null) {
for(p in meta.params) {
switch(p.expr) {
case EConst(CString(s, kind)):
previousAliases.push({
name: s,
pos: p.pos
});
default:
}
}
}
}
if (previousAliases.length == 0) continue;
var ghostAccess = field.access.copy();
ghostAccess.remove(AOverride);
ghostAccess.remove(APublic);
for(alias in previousAliases) {
var ghostField:Field = {
name: alias.name,
pos: alias.pos,
kind: null,
access: ghostAccess,
meta: aliasMetadata
};
switch(field.kind) {
case FFun(f):
var args:Array<Expr> = [for(a in f.args) {
expr: EConst(CIdent(a.name)),
pos: Context.currentPos()
}];
ghostField.kind = FFun({
args: f.args,
params: f.params,
ret: f.ret,
expr: macro return $i{field.name}($a{args})
});
default:
var type = null;
switch(field.kind) {
case FVar(t, e):
type = t;
case FProp(get, set, t, e):
type = t;
default:
}
ghostField.kind = FProp("get", "set", type, null);
fields.push({
name: 'get_${alias.name}',
kind: FFun({
args: [],
expr: macro return $i{field.name},
ret: type
}),
pos: alias.pos,
meta: aliasMetadata
});
fields.push({
name: 'set_${alias.name}',
kind: FFun({
args: [{
name: '_${field.name}',
type: type
}],
expr: macro return ($i{field.name} = $i{'_${field.name}'}),
ret: type
}),
pos: alias.pos,
meta: aliasMetadata
});
}
fields.push(ghostField);
}
var docFooter = '_Aliases: ${[for(alias in previousAliases) '`${alias.name}`'].join(", ")}_';
if (field.doc != null)
field.doc += '\n\n$docFooter';
else
field.doc = docFooter;
}
trace(fields);
return fields;
}
}
#end
@@ -19,7 +19,7 @@ class FlagMacro {
return false; return false;
} }
public static function build(resetCallback:String):Array<Field> { public static function build():Array<Field> {
var fields = Context.getBuildFields(); var fields = Context.getBuildFields();
var clRef = Context.getLocalClass(); var clRef = Context.getLocalClass();
@@ -248,10 +248,7 @@ class FlagMacro {
access: [APublic, AStatic], access: [APublic, AStatic],
kind: FFun({ kind: FFun({
args: [], args: [],
expr: macro { expr: macro $b{resetExprs},
$b{resetExprs};
$i{resetCallback}();
},
ret: macro: Void ret: macro: Void
}), }),
pos: Context.currentPos(), pos: Context.currentPos(),
@@ -23,7 +23,7 @@ class Macros {
#if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end #if VIDEO_CUTSCENES "hxvlc.flixel", "hxvlc.openfl", #end
#if NAPE_ENABLED "nape", "flixel.addons.nape", #end #if NAPE_ENABLED "nape", "flixel.addons.nape", #end
// BASE HAXE // BASE HAXE
"DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting" "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting", "animate"
]) ])
Compiler.include(inc); Compiler.include(inc);
@@ -69,6 +69,7 @@ class Macros {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos(); final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();
fields.push({name: 'tag', access: [APublic], pos: pos, kind: FVar(macro :funkin.backend.assets.AssetSource)}); fields.push({name: 'tag', access: [APublic], pos: pos, kind: FVar(macro :funkin.backend.assets.AssetSource)});
fields.push({name: 'isCompressed', access: [APublic], pos: pos, kind: FVar(macro :Bool, macro false)});
return fields; return fields;
} }
+45 -11
View File
@@ -21,7 +21,6 @@ import flixel.util.FlxAxes;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import flixel.util.typeLimit.OneOfThree; import flixel.util.typeLimit.OneOfThree;
import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfTwo;
import flxanimate.data.AnimationData.AnimAtlas;
import funkin.backend.system.Conductor; import funkin.backend.system.Conductor;
import funkin.backend.utils.XMLUtil.TextFormat; import funkin.backend.utils.XMLUtil.TextFormat;
import haxe.CallStack; import haxe.CallStack;
@@ -34,6 +33,8 @@ import haxe.xml.Access;
import lime.utils.Assets; import lime.utils.Assets;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.geom.ColorTransform; import openfl.geom.ColorTransform;
import animate.FlxAnimateJson;
import flixel.animation.FlxAnimationController;
using StringTools; using StringTools;
@@ -439,11 +440,9 @@ final class CoolUtil
* @param fadeIn * @param fadeIn
*/ */
@:noUsing public static function playMenuSong(fadeIn:Bool = false) { @:noUsing public static function playMenuSong(fadeIn:Bool = false) {
if (FlxG.sound.music == null || !FlxG.sound.music.playing) if (FlxG.sound.music == null || !FlxG.sound.music.playing) {
{
playMusic(Paths.music(Flags.DEFAULT_MENU_MUSIC), true, fadeIn ? 0 : 1, true, 102); playMusic(Paths.music(Flags.DEFAULT_MENU_MUSIC), true, fadeIn ? 0 : 1, true, 102);
if (fadeIn) if (fadeIn) FlxG.sound.music.fadeIn(4, 0, 1);
FlxG.sound.music.fadeIn(4, 0, 0.7);
} }
} }
@@ -574,6 +573,29 @@ final class CoolUtil
anim2.frames = old; anim2.frames = old;
} }
/**
* Swaps two animations on an animation controller.
* @param animation Animation controller
* @param anim1 First animation
* @param anim2 Second animation
*/
public static function swapAnims(animation:FlxAnimationController, anim1:String, anim2:String) @:privateAccess {
var flxAnim1 = animation.getByName(anim1);
animation._animations.remove(anim1);
var flxAnim2 = animation.getByName(anim2);
animation._animations.remove(anim2);
if (flxAnim1 != null) {
flxAnim1.name = anim2;
animation._animations.set(anim2, flxAnim1);
}
if (flxAnim2 != null) {
flxAnim2.name = anim1;
animation._animations.set(anim1, flxAnim2);
}
}
/** /**
* Allows you to set a graphic size (ex: 150x150), with proper hitbox without a stretched sprite. * Allows you to set a graphic size (ex: 150x150), with proper hitbox without a stretched sprite.
* @param sprite Sprite to apply the new graphic size to * @param sprite Sprite to apply the new graphic size to
@@ -1003,6 +1025,20 @@ final class CoolUtil
return result; return result;
} }
/**
* Returns if the mouse is overlapping the sprite on a given camera, taking the camera's position, scroll and zoom into account.
*
* @param sprite Any `FlxObject`
* @param camera The camera you want to check overlap on. Uses the sprite's camera by default.
* @return Bool
*/
public static function mouseOverlaps(sprite:FlxObject, ?camera:FlxCamera) {
var camToCheck:FlxCamera = camera ?? sprite.camera;
var posthing:FlxPoint = FlxG.mouse.getWorldPosition(camToCheck);
return posthing != null && FlxMath.inBounds(posthing.x, sprite.x, sprite.x + sprite.width) && FlxMath.inBounds(posthing.y, sprite.y, sprite.y + sprite.height);
}
/** /**
* Sorts an array alphabetically. * Sorts an array alphabetically.
* @param array Array to sort * @param array Array to sort
@@ -1252,23 +1288,20 @@ final class CoolUtil
return animsList; return animsList;
} }
public static function getAnimsListFromAtlas(atlas:AnimAtlas):Array<String> { public static function getAnimsListFromAtlas(atlas:AnimationJson):Array<String> {
if (atlas == null) return []; if (atlas == null) return [];
var animsList:Array<String> = []; var animsList:Array<String> = [];
if (atlas.AN.SN != null) animsList.push(atlas.AN.SN); if (atlas.AN.SN != null) animsList.push(atlas.AN.SN);
if (atlas.SD != null) if (atlas.SD != null)
for (symbol in atlas.SD.S) for (symbol in atlas.SD)
if (symbol.SN != null) animsList.push(symbol.SN); if (symbol.SN != null) animsList.push(symbol.SN);
return animsList; return animsList;
} }
public static function getAnimsListFromSprite(spr:FunkinSprite):Array<String> { public static function getAnimsListFromSprite(spr:FunkinSprite):Array<String> {
if (spr.animateAtlas != null) { return getAnimsListFromFrames(spr.frames);
return [for (symbol => timeline in spr.animateAtlas.anim.symbolDictionary) symbol];
} else
return getAnimsListFromFrames(spr.frames);
} }
// TODO: check this for bugs // TODO: check this for bugs
@@ -1421,6 +1454,7 @@ final class CoolUtil
return toProperty.setValue(fromProperty.getValue()); return toProperty.setValue(fromProperty.getValue());
} }
} }
class PropertyInfo { class PropertyInfo {
+199 -179
View File
@@ -3,200 +3,220 @@ package funkin.backend.utils;
import haxe.macro.Expr; import haxe.macro.Expr;
final class MathUtil { final class MathUtil {
/** public static inline var EULER:Float = 2.718281828459;
* Returns the maximum value in the arguments.
* @param args Array of values
*
* @return The maximum value
**/
public static function maxInt(...args:Int):Int {
var max = args[0];
for(i in 1...args.length) {
var arg = args[i];
if(arg > max)
max = arg;
}
return max;
}
/** /**
* Returns the minimum value in the arguments. * Returns the maximum value in the arguments.
* @param args Array of values * @param args Array of values
* *
* @return The minimum value * @return The maximum value
**/ **/
public static function minInt(...args:Int):Int { public static function maxInt(...args:Int):Int {
var min = args[0]; var max = args[0];
for(i in 1...args.length) { for(i in 1...args.length) {
var arg = args[i]; var arg = args[i];
if(arg < min) if(arg > max)
min = arg; max = arg;
} }
return min; return max;
} }
/** /**
* Returns the maximum value in the arguments. * Returns the minimum value in the arguments.
* * @param args Array of values
* NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance. *
* * @return The minimum value
* @param args Array of values **/
* public static function minInt(...args:Int):Int {
* @return The maximum value var min = args[0];
**/ for(i in 1...args.length) {
public static function max(...args:Float):Float { var arg = args[i];
var max = args[0]; if(arg < min)
for(i in 1...args.length) { min = arg;
var arg = args[i]; }
if(arg > max) return min;
max = arg; }
}
return max;
}
/** /**
* Returns the minimum value in the arguments. * Returns the maximum value in the arguments.
* *
* NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance. * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance.
* *
* @param args Array of values * @param args Array of values
* *
* @return The minimum value * @return The maximum value
**/ **/
public static function min(...args:Float):Float { public static function max(...args:Float):Float {
var min = args[0]; var max = args[0];
for(i in 1...args.length) { for(i in 1...args.length) {
var arg = args[i]; var arg = args[i];
if(arg < min) if(arg > max)
min = arg; max = arg;
} }
return min; return max;
} }
/** /**
* Checks if a is less than b with considering a margin of error. * Returns the minimum value in the arguments.
* *
* @param a Float * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance.
* @param b Float *
* @param margin Float (Default: EPSILON) * @param args Array of values
* *
* @return Bool * @return The minimum value
**/ **/
public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { public static function min(...args:Float):Float {
return a < b - margin; var min = args[0];
} for(i in 1...args.length) {
var arg = args[i];
if(arg < min)
min = arg;
}
return min;
}
/** /**
* Checks if a is less than or equally b with considering a margin of error. * Checks if a is less than b with considering a margin of error.
* * * @param a Float
* @param a Float * @param b Float
* @param b Float * @param margin Float (Default: EPSILON)
* @param margin Float (Default: EPSILON) * * @return Bool
* **/
* @return Bool public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
**/ return a < b - margin;
public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { }
return a <= b - margin;
}
/** /**
* Checks if a is greater than b with considering a margin of error. * Checks if a is less than or equally b with considering a margin of error.
* * * @param a Float
* @param a Float * @param b Float
* @param b Float * @param margin Float (Default: EPSILON)
* @param margin Float (Default: EPSILON) * * @return Bool
* **/
* @return Bool public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
**/ return a <= b - margin;
public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { }
return a > b + margin;
}
/** /**
* Checks if a is greater than or equally b with considering a margin of error. * Checks if a is greater than b with considering a margin of error.
* * * @param a Float
* @param a Float * @param b Float
* @param b Float * @param margin Float (Default: EPSILON)
* @param margin Float (Default: EPSILON) * * @return Bool
* **/
* @return Bool public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool {
**/ return a > b + margin;
public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { }
return a >= b + margin;
}
/** /**
* Checks if a is approximately equal to b. * Checks if a is greater than or equally b with considering a margin of error.
* * * @param a Float
* @param a Float * @param b Float
* @param b Float * @param margin Float (Default: EPSILON)
* @param margin Float (Default: EPSILON) * * @return Bool
* **/
* @return Bool public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
**/ return a >= b + margin;
public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool { }
return Math.abs(a - b) <= margin;
}
/** /**
* Checks if a are not approximately equal to b. * Checks if a is approximately equal to b.
* * * @param a Float
* @param a Float * @param b Float
* @param b Float * @param margin Float (Default: EPSILON)
* @param margin Float (Default: EPSILON) * * @return Bool
* **/
* @return Bool public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool {
**/ return Math.abs(a - b) <= margin;
public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { }
return Math.abs(a - b) > margin;
}
/** /**
* Shortcut to `Math.max` but with infinite amount of arguments * Checks if a are not approximately equal to b.
* * * @param a Float
* Might not preserve the order of arguments, please test this. * @param b Float
* * @param margin Float (Default: EPSILON)
* Dont use this in hscript, it doesnt work, it only works on compile time * * @return Bool
**/ **/
@:dox(hide) public static macro function maxSmart(..._args:Expr):Expr { public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool {
return genericMinMaxSmart(_args.toArray(), "Math.max"); return Math.abs(a - b) > margin;
} }
/** /**
* Shortcut to `Math.min` but with infinite amount of arguments * @param edge0 Float
* * @param edge1 Float
* Might not preserve the order of arguments, please test this. * @param x Float
* * @return Float
* Dont use this in hscript, it doesnt work, it only works on compile time **/
**/ public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float {
@:dox(hide) public static macro function minSmart(..._args:Expr):Expr { var t = (x - edge0) / (edge1 - edge0);
return genericMinMaxSmart(_args.toArray(), "Math.min"); var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
} return clamped * clamped * (3.0 - 2.0 * clamped);
}
#if macro /**
@:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr { * @param a Float
var args = _args.copy(); * @param b Float
if (args.length == 0) return macro 0; * @param v Float
* @return Float
**/
public static function inverseLerp(a:Float, b:Float, v:Float):Float {
return (v - a) / (b - a);
}
var func = funcPath.split("."); /**
* @param v Float
* @return Float
**/
public static function fract(v:Float):Float {
return v - Math.floor(v);
}
function nested(lst:Array<Expr>):Expr { /**
if (lst.length == 1) { * Shortcut to `Math.max` but with infinite amount of arguments
return macro ${lst[0]}; *
} else if (lst.length == 2) { * Might not preserve the order of arguments, please test this.
return macro $p{func}(${lst[0]}, ${lst[1]}); *
} else { * Dont use this in hscript, it doesnt work, it only works on compile time
var mid = Std.int(lst.length / 2); **/
return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))}); @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr {
} return genericMinMaxSmart(_args.toArray(), "Math.max");
} }
var expr = nested(args); /**
* Shortcut to `Math.min` but with infinite amount of arguments
*
* Might not preserve the order of arguments, please test this.
*
* Dont use this in hscript, it doesnt work, it only works on compile time
**/
@:dox(hide) public static macro function minSmart(..._args:Expr):Expr {
return genericMinMaxSmart(_args.toArray(), "Math.min");
}
//var printer = new haxe.macro.Printer(); #if macro
//trace(printer.printExpr(expr)); @:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr {
var args = _args.copy();
if (args.length == 0) return macro 0;
return macro $expr; var func = funcPath.split(".");
}
#end function nested(lst:Array<Expr>):Expr {
if (lst.length == 1) {
return macro ${lst[0]};
} else if (lst.length == 2) {
return macro $p{func}(${lst[0]}, ${lst[1]});
} else {
var mid = Std.int(lst.length / 2);
return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))});
}
}
var expr = nested(args);
//var printer = new haxe.macro.Printer();
//trace(printer.printExpr(expr));
return macro $expr;
}
#end
} }
+8 -136
View File
@@ -34,151 +34,23 @@ final class MatrixUtil {
//if(_height == -1) _height = sprite.height; //if(_height == -1) _height = sprite.height;
if(camera == null) camera = sprite.camera; if(camera == null) camera = sprite.camera;
if(points is FlxBasePoint) points = [points]; if(points is FlxBasePoint) points = [points];
var isFunkinSprite = sprite is FunkinSprite;
var funkinSprite:FunkinSprite = null;
if(isFunkinSprite) funkinSprite = cast sprite;
var isAnimateAtlas = isFunkinSprite && funkinSprite.animateAtlas != null;
var nc:FakeCamera = isAnimateAtlas ? FakeCallCamera.instance : FakeCamera.instance; var nc:FakeCamera = FakeCamera.instance;
nc.zoom = camera.zoom; nc.zoom = camera.zoom;
nc.scroll.set(camera.scroll.x, camera.scroll.y); nc.scroll.set(camera.scroll.x, camera.scroll.y);
nc.pixelPerfectRender = camera.pixelPerfectRender; nc.pixelPerfectRender = camera.pixelPerfectRender;
var points:Array<FlxPoint> = cast points; var points:Array<FlxPoint> = cast points;
if(isAnimateAtlas) { if(sprite is IPrePostDraw) {
var cnc = FakeCallCamera.instance; var postDraw = cast(sprite, IPrePostDraw);
var sprite = funkinSprite; postDraw.preDraw();
var oldOnDraw = cnc.onDraw; sprite.drawComplex(nc);
postDraw.postDraw();
// TODO: fix rotation
// TODO: fix skew
var boundsTopLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY);
var boundsTopRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.POSITIVE_INFINITY);
var boundsBottomLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.NEGATIVE_INFINITY);
var boundsBottomRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.NEGATIVE_INFINITY);
cnc.onDraw = function(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
// cnc is already perfect rn
// so we need to manually process the limb
var points = [
// corners
FlxPoint.get(0, 0),
FlxPoint.get(1, 0),
FlxPoint.get(0, 1),
FlxPoint.get(1, 1)
];
rawTransformPoints(points, matrix, frame.sourceSize.x, frame.sourceSize.y);
// sort the points, incase like they were transformed with a scale -1 matrix
var topLeft:FlxPoint = points[0];
var topRight:FlxPoint = points[1];
var bottomLeft:FlxPoint = points[2];
var bottomRight:FlxPoint = points[3];
if (points[0].y > points[1].y) {
topLeft = points[1];
topRight = points[0];
}
if (points[2].y > points[3].y) {
bottomLeft = points[3];
bottomRight = points[2];
}
if (topLeft.x > topRight.x) {
var temp = topLeft;
topLeft = topRight;
topRight = temp;
}
if (bottomLeft.x > bottomRight.x) {
var temp = bottomLeft;
bottomLeft = bottomRight;
bottomRight = temp;
}
// points should now contain the corners of the limb
// trace("Corners of limb: " + points);
// set the max bounds of each corner of the limb
if(topLeft.x < boundsTopLeft.x) boundsTopLeft.x = topLeft.x;
if(topLeft.y < boundsTopLeft.y) boundsTopLeft.y = topLeft.y;
if(topRight.x > boundsTopRight.x) boundsTopRight.x = topRight.x;
if(topRight.y < boundsTopRight.y) boundsTopRight.y = topRight.y;
if(bottomLeft.x < boundsBottomLeft.x) boundsBottomLeft.x = bottomLeft.x;
if(bottomLeft.y > boundsBottomLeft.y) boundsBottomLeft.y = bottomLeft.y;
if(bottomRight.x > boundsBottomRight.x) boundsBottomRight.x = bottomRight.x;
if(bottomRight.y > boundsBottomRight.y) boundsBottomRight.y = bottomRight.y;
// recycle the points
for(point in points) point.put();
}
var cameras = sprite._cameras;
var oldVisible = cnc.visible;
cnc.visible = true;
sprite._cameras = [cnc];
sprite.draw();
sprite._cameras = cameras;
cnc.visible = oldVisible;
cnc.onDraw = oldOnDraw;
// fake transform
//trace("");
//trace("topLeft: " + boundsTopLeft);
//trace("topRight: " + boundsTopRight);
//trace("bottomLeft: " + boundsBottomLeft);
//trace("bottomRight: " + boundsBottomRight);
//trace("Before transform");
//trace(points);
for(point in points) {
//var x = matrix.__transformX(point.x * _width, point.y * _height);
//var y = matrix.__transformY(point.x * _width, point.y * _height);
// apply using boundsTopLeft, boundsTopRight, boundsBottomLeft, boundsBottomRight
// matrix doesnt work for this
var x = FlxMath.lerp(
FlxMath.lerp(boundsTopLeft.x, boundsTopRight.x, point.x),
FlxMath.lerp(boundsBottomLeft.x, boundsBottomRight.x, point.x),
point.y
);
var y = FlxMath.lerp(
FlxMath.lerp(boundsTopLeft.y, boundsTopRight.y, point.x),
FlxMath.lerp(boundsBottomLeft.y, boundsBottomRight.y, point.x),
point.y
);
//trace("(" + point.x + ", " + point.y + ") -> (" + x + ", " + y + ")");
// reset to ingame coords
x += camera.scroll.x;
y += camera.scroll.y;
if(isFunkinSprite) {
var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor);
x += camera.width / 2 * ratio;
y += camera.height / 2 * ratio;
}
point.set(x, y);
}
//trace("After transform");
//trace(points);
} else { } else {
if(sprite is IPrePostDraw) { sprite.drawComplex(nc);
var postDraw = cast(sprite, IPrePostDraw);
postDraw.preDraw();
sprite.drawComplex(nc);
postDraw.postDraw();
} else {
sprite.drawComplex(nc);
}
transformPoints(sprite, points, sprite._matrix, camera, _width, _height);
} }
transformPoints(sprite, points, sprite._matrix, camera, _width, _height);
return points; return points;
} }
+4 -14
View File
@@ -127,7 +127,7 @@ final class MemoryUtil {
public static function getMemType():String { public static function getMemType():String {
#if windows #if windows
var memoryMap:Map<Int, String> = [ var memoryMap:Map<Int, String> = [
0 => null, 0 => "Unknown",
1 => "Other", 1 => "Other",
2 => "DRAM", 2 => "DRAM",
3 => "Synchronous DRAM", 3 => "Synchronous DRAM",
@@ -152,23 +152,13 @@ final class MemoryUtil {
22 => "DDR2 FB-DIMM", 22 => "DDR2 FB-DIMM",
24 => "DDR3", 24 => "DDR3",
25 => "FBD2", 25 => "FBD2",
26 => "DDR4", 26 => "DDR4"
27 => "LPDDR",
28 => "LPDDR2",
29 => "LPDDR3",
30 => "LPDDR4",
31 => "Logical Non-volatile device",
32 => "HBM",
33 => "HBM2",
34 => "DDR5",
35 => "LPDDR5",
36 => "HBM3",
]; ];
var memoryOutput:Int = -1; var memoryOutput:Int = -1;
var process = new HiddenProcess("powershell", ["-Command", "Get-CimInstance Win32_PhysicalMemory | Select-Object -ExpandProperty SMBIOSMemoryType" ]); var process = new HiddenProcess("wmic", ["memorychip", "get", "SMBIOSMemoryType"]);
if (process.exitCode() == 0) memoryOutput = Std.int(Std.parseFloat(process.stdout.readAll().toString().trim().split("\n")[1])); if (process.exitCode() == 0) memoryOutput = Std.int(Std.parseFloat(process.stdout.readAll().toString().trim().split("\n")[1]));
if (memoryOutput != -1) return memoryMap[memoryOutput] == null ? 'Unknown ($memoryOutput)' : memoryMap[memoryOutput]; if (memoryOutput != -1) return memoryMap[memoryOutput];
#elseif mac #elseif mac
var process = new HiddenProcess("system_profiler", ["SPMemoryDataType"]); var process = new HiddenProcess("system_profiler", ["SPMemoryDataType"]);
var reg = ~/Type: (.+)/; var reg = ~/Type: (.+)/;
-120
View File
@@ -1,120 +0,0 @@
package funkin.backend.utils;
enum abstract RegistryHive(Int) {
var HKEY_CLASSES_ROOT = 0x80000000;
var HKEY_CURRENT_USER = 0x80000001;
var HKEY_LOCAL_MACHINE = 0x80000002;
var HKEY_USERS = 0x80000003;
var HKEY_CURRENT_CONFIG = 0x80000005;
}
#if windows
@:cppFileCode('
#include <windows.h>
#include <tchar.h>
#include <string>
#include <vector>
')
#end
class RegistryUtil {
#if windows
@:functionCode('
HKEY hKey;
LONG result;
DWORD dataSize = 0;
DWORD dataType = 0;
std::wstring subkey = std::wstring(key.wchar_str());
std::wstring valname = std::wstring(string.wchar_str());
result = RegOpenKeyExW((HKEY)reinterpret_cast<HKEY>(static_cast<uintptr_t>(hive)), subkey.c_str(), 0, KEY_READ, &hKey);
if (result != ERROR_SUCCESS) return null();
result = RegQueryValueExW(hKey, valname.c_str(), NULL, &dataType, NULL, &dataSize);
if (result != ERROR_SUCCESS || dataSize == 0) {
RegCloseKey(hKey);
return null();
}
std::vector<wchar_t> buffer(dataSize / sizeof(wchar_t));
result = RegQueryValueExW(hKey, valname.c_str(), NULL, NULL, (LPBYTE)buffer.data(), &dataSize);
RegCloseKey(hKey);
if (result == ERROR_SUCCESS) {
return ::String(buffer.data());
}
return null();
')
#end
public static function get(hive:RegistryHive, key:String, string:String):Null<String>
{
return null;
}
#if windows
@:functionCode('
HKEY hKey;
LONG result;
std::wstring subkey = std::wstring(key.wchar_str());
std::wstring valname = std::wstring(string.wchar_str());
std::wstring data = std::wstring(value.wchar_str());
result = RegCreateKeyExW((HKEY)reinterpret_cast<HKEY>(static_cast<uintptr_t>(hive)), subkey.c_str(), 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
if (result != ERROR_SUCCESS) return false;
result = RegSetValueExW(hKey, valname.c_str(), 0, REG_SZ, (const BYTE*)data.c_str(), (DWORD)((data.length() + 1) * sizeof(wchar_t)));
RegCloseKey(hKey);
return result == ERROR_SUCCESS;
')
#end
public static function set(hive:RegistryHive, key:String, string:String, value:String):Bool
{
return false;
}
#if windows
@:functionCode('
HKEY hKey;
LONG result;
std::wstring subkey = std::wstring(key.wchar_str());
std::wstring valname = std::wstring(string.wchar_str());
result = RegOpenKeyExW((HKEY)reinterpret_cast<HKEY>(static_cast<uintptr_t>(hive)), subkey.c_str(), 0, KEY_READ, &hKey);
if (result != ERROR_SUCCESS) return false;
DWORD dataType = 0;
result = RegQueryValueExW(hKey, valname.c_str(), NULL, &dataType, NULL, NULL);
RegCloseKey(hKey);
return result == ERROR_SUCCESS;
')
#end
public static function exists(hive:RegistryHive, key:String, string:String):Bool
{
return false;
}
#if windows
@:functionCode('
HKEY hKey;
LONG result;
std::wstring subkey = std::wstring(key.wchar_str());
std::wstring valname = std::wstring(string.wchar_str());
result = RegOpenKeyExW((HKEY)reinterpret_cast<HKEY>(static_cast<uintptr_t>(hive)), subkey.c_str(), 0, KEY_SET_VALUE, &hKey);
if (result != ERROR_SUCCESS) return false;
result = RegDeleteValueW(hKey, valname.c_str());
RegCloseKey(hKey);
return result == ERROR_SUCCESS;
')
#end
public static function delete(hive:RegistryHive, key:String, string:String):Bool {
return false;
}
}
+97 -111
View File
@@ -2,157 +2,143 @@ package funkin.backend.utils;
#if sys #if sys
import haxe.io.Input; import haxe.io.Input;
import haxe.zip.Entry;
import haxe.zip.InflateImpl;
import haxe.zip.Reader; import haxe.zip.Reader;
import sys.io.File; import sys.io.File;
import sys.io.FileInput; import sys.io.FileInput;
import haxe.io.Bytes;
/** /**
* Class that extends Reader allowing you to load ZIP entries without blowing your RAM up!! * Class that extends Reader allowing you to load ZIP entries without blowing your RAM up!!
* Half of the code is taken from haxe libraries btw * ~~Half of the code is taken from haxe libraries btw~~ Reworked by ItsLJcool to actually work for zip files.
*/ */
class SysZip extends Reader { class SysZip {
var input:Input;
var fileInput:FileInput; var fileInput:FileInput;
var filePath:String;
public var entries:List<SysZipEntry>; public var entries:List<SysZipEntry> = new List();
/** /**
* Opens a zip from a specified path. * Opens a zip from a specified path.
* @param path Path to the zip file. * @param path Path to the zip file. (With the extension)
*/ */
public static function openFromFile(path:String) { public static function openFromFile(path:String) { return new SysZip(path); } // keeping for compatibility.
return new SysZip(File.read(path, true));
}
/** /**
* Creates a new SysZip from a specified file input. * Creates a new SysZip from a specified path.
* @param input File input. * @param path Path to the zip file. (With the extension)
*/ */
public function new(input:FileInput) { public function new(path:String) {
super(input); this.filePath = path;
fileInput = input; fileInput = File.read(path, true);
}
/** updateEntries(); // automatic but if you feel like you don't want it to be automatic, you can remove this.
* Reads all the data present in a specified entry.
* NOTE: If the entry is compressed, the data won't be decompressed. For decompression, use `unzipEntry`.
* @param e Entry
*/
public function readEntryData(e:SysZipEntry) {
var bytes:haxe.io.Bytes = null;
var buf = null;
var tmp = null;
fileInput.seek(e.seekPos, SeekBegin);
if (e.crc32 == null) {
if (e.compressed) {
#if neko
// enter progressive mode : we use a different input which has
// a temporary buffer, this is necessary since we have to uncompress
// progressively, and after that we might have pending read data
// that needs to be processed
var bufSize = 65536;
if (buf == null) {
buf = new haxe.io.BufferInput(i, haxe.io.Bytes.alloc(bufSize));
tmp = haxe.io.Bytes.alloc(bufSize);
i = buf;
}
var out = new haxe.io.BytesBuffer();
var z = new neko.zip.Uncompress(-15);
z.setFlushMode(neko.zip.Flush.SYNC);
while (true) {
if (buf.available == 0)
buf.refill();
var p = bufSize - buf.available;
if (p != buf.pos) {
// because of lack of "srcLen" in zip api, we need to always be stuck to the buffer end
buf.buf.blit(p, buf.buf, buf.pos, buf.available);
buf.pos = p;
}
var r = z.execute(buf.buf, buf.pos, tmp, 0);
out.addBytes(tmp, 0, r.write);
buf.pos += r.read;
buf.available -= r.read;
if (r.done)
break;
}
bytes = out.getBytes();
#else
var bufSize = 65536;
if (tmp == null)
tmp = haxe.io.Bytes.alloc(bufSize);
var out = new haxe.io.BytesBuffer();
var z = new InflateImpl(i, false, false);
while (true) {
var n = z.readBytes(tmp, 0, bufSize);
out.addBytes(tmp, 0, n);
if (n < bufSize)
break;
}
bytes = out.getBytes();
#end
} else
bytes = i.read(e.dataSize);
e.crc32 = i.readInt32();
if (e.crc32 == 0x08074b50)
e.crc32 = i.readInt32();
e.dataSize = i.readInt32();
e.fileSize = i.readInt32();
// set data to uncompressed
e.dataSize = e.fileSize;
e.compressed = false;
} else
bytes = i.read(e.dataSize);
return bytes;
} }
/** /**
* Unzips and returns all of the data present in an entry. * Unzips and returns all of the data present in an entry.
* @param f Entry to read from. * @param f Entry to read from.
*/ */
public function unzipEntry(f:SysZipEntry) { public function unzipEntry(f:SysZipEntry):Bytes {
var data = readEntryData(f); if (f.fileSize <= 0) return Bytes.alloc(0);
fileInput.seek(f.seekPos, SeekBegin);
var data = fileInput.read(f.compressedSize);
if (!f.compressed) return data;
if (!f.compressed)
return data;
var c = new haxe.zip.Uncompress(-15); var c = new haxe.zip.Uncompress(-15);
var s = haxe.io.Bytes.alloc(f.fileSize); var s = Bytes.alloc(f.fileSize);
var r = c.execute(data, 0, s, 0); var r = c.execute(data, 0, s, 0);
c.close(); c.close();
if (!r.done || r.read != data.length || r.write != f.fileSize)
throw "Invalid compressed data for " + f.fileName; if (!r.done || r.read != data.length || r.write != f.fileSize) throw 'Invalid compressed data for ${f.fileName} | ${f.compressedSize} -> ${f.fileSize}';
data = s; return s;
return data;
} }
public override function read():List<Entry> { /**
if (entries != null) * Updates the `entries` list with the current contents of the zip file.
return entries; * This is done when the zip is read from SysZip the first time, but if you REALLY need to re-update the entries, you can call this again.
entries = new List(); *
* Note: Calling this function will hold up the game as it has to read the ENTIRE zip, so if it's large like 1GiB or more, it might take a second or more.
*/
public function updateEntries() {
if (entries.length > 0) {
entries.clear();
entries = new List();
}
// --- locate End of Central Directory (EOCD) ---
var fileSize:Int = sys.FileSystem.stat(this.filePath).size; // probably need a better way to check the size of the file.
var scanSize:Int = (65535 < fileSize) ? 65535 : fileSize;
// It seems this usually ends up being 0 anyways, but for cases where it might not be?? I'd just make sure. but Someone do some digging I don't know if this required.
fileInput.seek(fileSize - scanSize, SeekBegin);
var buf = fileInput.read(scanSize);
var b = new haxe.io.BytesInput(buf);
// I LOVE USING MAGIC NUMBERS AND FORGETTING WHAT THEY DO 🔥🔥🔥🔥🔥🔥
b.position = (buf.length - 22) + 16; // offset to start of central directory
// --- read central directory ---
fileInput.seek(b.readInt32(), SeekBegin);
while (true) { while (true) {
var e = readEntryHeader(); if (fileInput.readInt32() != 0x02014b50) break; // central dir file header signature
if (e == null)
break; fileInput.seek(6, SeekCur); // version/flags
var compression_method = fileInput.readUInt16();
fileInput.seek(8, SeekCur); // time/date + CRC32 (4, 4)
var compressed_size = fileInput.readInt32();
var uncompressed_size = fileInput.readInt32();
var nameLen = fileInput.readUInt16();
var extraLen = fileInput.readUInt16();
var commentLen = fileInput.readUInt16();
fileInput.seek(8, SeekCur); // skip disk number/start attrs
var localHeaderOffset = fileInput.readInt32();
var name = fileInput.read(nameLen).toString();
// skip central directory extra/comment
fileInput.seek(extraLen + commentLen, SeekCur);
// --- compute correct seekPos using local header ---
var curPos = fileInput.tell();
// I also forgor what the `+ 26` is for, so uh my b chat
fileInput.seek(localHeaderOffset + 26, SeekBegin);
var localNameLen = fileInput.readUInt16();
var localExtraLen = fileInput.readUInt16();
fileInput.seek(curPos, SeekBegin);
// I completely forgot that we don't really need to log the FOLDER of the content because we only care about where the contents are.
// the folders are labled as 0 bytes anyways so this will save on storing non-required data.
if (name.endsWith("/")) continue;
var zipEntry:SysZipEntry = cast e; var zipEntry:SysZipEntry = {
zipEntry.seekPos = fileInput.tell(); fileName: name,
fileSize: uncompressed_size,
// I don't remember what the `+ 30` is for, but probably to offset something
seekPos: (localHeaderOffset + 30 + localNameLen + localExtraLen),
compressedSize: compressed_size,
compressed: (compression_method == 8),
};
entries.add(zipEntry); entries.add(zipEntry);
fileInput.seek(e.dataSize, SeekCur);
} }
return entries;
} }
/**
* calling `dispose` doesn't actually kill the class, you can still access the entries.
* disposing of SysZip will free the compressed file from being used by the engine.
*/
public function dispose() { public function dispose() {
if (input != null) if (fileInput != null) fileInput.close();
input.close();
} }
} }
typedef SysZipEntry = { typedef SysZipEntry = {
> Entry, var fileName:String;
var fileSize:Int;
var seekPos:Int; var seekPos:Int;
var compressedSize:Int;
var compressed:Bool;
} }
#end #end
+24 -8
View File
@@ -14,6 +14,7 @@ import funkin.backend.scripting.ScriptPack;
import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.system.interfaces.IOffsetCompatible; import funkin.backend.system.interfaces.IOffsetCompatible;
import haxe.xml.Access; import haxe.xml.Access;
import animate.FlxAnimateFrames;
using StringTools; using StringTools;
@@ -127,6 +128,9 @@ final class XMLUtil {
spr.spriteAnimType = XMLAnimType.fromString(node.att.type, spr.spriteAnimType); spr.spriteAnimType = XMLAnimType.fromString(node.att.type, spr.spriteAnimType);
} }
if (node.has.applyStageMatrix) spr.applyStageMatrix = node.att.applyStageMatrix == "true";
if (node.has.useRenderTexture) spr.useRenderTexture = node.att.useRenderTexture == "true";
if(node.has.x) { if(node.has.x) {
var x:Null<Float> = Std.parseFloat(node.att.x); var x:Null<Float> = Std.parseFloat(node.att.x);
if (x.isNotNull()) spr.x = x; if (x.isNotNull()) spr.x = x;
@@ -225,7 +229,8 @@ final class XMLUtil {
animType: spr.spriteAnimType, animType: spr.spriteAnimType,
x: 0, x: 0,
y: 0, y: 0,
indices: [for(i in 0...spr.frames.frames.length) i] indices: [for(i in 0...spr.frames.frames.length) i],
label: false
}); });
} }
@@ -261,7 +266,8 @@ final class XMLUtil {
animType: animType, animType: animType,
x: 0, x: 0,
y: 0, y: 0,
indices: [] indices: [],
label: false
}; };
if (anim.has.name) animData.name = anim.att.name; if (anim.has.name) animData.name = anim.att.name;
@@ -273,6 +279,7 @@ final class XMLUtil {
if (anim.has.loop) animData.loop = anim.att.loop == "true"; if (anim.has.loop) animData.loop = anim.att.loop == "true";
if (anim.has.forced) animData.forced = anim.att.forced == "true"; if (anim.has.forced) animData.forced = anim.att.forced == "true";
if (anim.has.indices) animData.indices = CoolUtil.parseNumberRange(anim.att.indices); if (anim.has.indices) animData.indices = CoolUtil.parseNumberRange(anim.att.indices);
if (anim.has.label) animData.label = anim.att.label == "true";
return animData; return animData;
} }
@@ -299,15 +306,23 @@ final class XMLUtil {
if (animData.name != null) { if (animData.name != null) {
if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24; if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;
if (sprite is FunkinSprite && cast(sprite, FunkinSprite).animateAtlas != null) { if (sprite.frames is FlxAnimateFrames) {
var animateAnim = cast(sprite, FunkinSprite).animateAtlas.anim;
if(animData.anim == null) if(animData.anim == null)
return MISSING_PROPERTY; return MISSING_PROPERTY;
if (animData.indices != null && animData.indices.length > 0) var animateAnim = cast(sprite, FunkinSprite).anim;
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else if (animData.label) {
animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop); if (animData.indices != null && animData.indices.length > 0)
animateAnim.addByFrameLabelIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else
animateAnim.addByFrameLabel(animData.name, animData.anim, animData.fps, animData.loop);
} else {
if (animData.indices != null && animData.indices.length > 0)
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else
animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
}
} else { } else {
if (animData.indices != null && animData.indices.length > 0) { if (animData.indices != null && animData.indices.length > 0) {
if (animData.anim == null) if (animData.anim == null)
@@ -463,6 +478,7 @@ typedef AnimData = {
var y:Float; var y:Float;
var indices:Array<Int>; var indices:Array<Int>;
var animType:XMLAnimType; var animType:XMLAnimType;
var label:Bool;
var ?forced:Bool; var ?forced:Bool;
} }
+17
View File
@@ -20,6 +20,8 @@ class EditorTreeMenu extends funkin.options.TreeMenu {
bg.antialiasing = true; bg.antialiasing = true;
setBackgroundRotation(-5); setBackgroundRotation(-5);
super.createPost(); super.createPost();
if (Paths.assetsTree.hasCompressedLibrary) warnCompressLibrary();
} }
public inline function setBackgroundRotation(rotation:Float) { public inline function setBackgroundRotation(rotation:Float) {
@@ -58,6 +60,21 @@ class EditorTreeMenu extends funkin.options.TreeMenu {
bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25); bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25);
bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25); bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25);
} }
private function warnCompressLibrary() {
var warningMessage = "It seems you have libraries loaded that are compressed, and can not have files written to them.\n
This is just a friendly reminder that if you're loading a Mod and wish to edit files, you need to uncompress it to be able to use any editors!\n\nCompressed Libraries: ";
var compressedList = Paths.assetsTree.libraries.filter(l -> funkin.backend.assets.AssetsLibraryList.getCleanLibrary(l).isCompressed);
var modNameList = [for (l in compressedList) {
l = funkin.backend.assets.AssetsLibraryList.getCleanLibrary(l);
if (l is funkin.backend.assets.IModsAssetLibrary) cast(l, funkin.backend.assets.IModsAssetLibrary).modName;
}];
warningMessage += modNameList.join(", ");
var zipLibraryWarning = new funkin.editors.ui.UIWarningSubstate("Compressed Library Detected!", warningMessage, [{label: "Ok", color: 0x969533, onClick: (state) -> {} }], false);
openSubState(zipLibraryWarning);
}
} }
class EditorTreeMenuScreen extends funkin.options.TreeMenuScreen { class EditorTreeMenuScreen extends funkin.options.TreeMenuScreen {
@@ -2,18 +2,13 @@ package funkin.editors.character;
import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormat;
import flixel.text.FlxText.FlxTextFormatMarkerPair; import flixel.text.FlxText.FlxTextFormatMarkerPair;
import flxanimate.animate.FlxAnim.FlxSymbolAnimation;
import flxanimate.animate.FlxSymbol;
import flxanimate.animate.FlxElement;
import flxanimate.animate.SymbolParameters;
import flxanimate.animate.FlxKeyFrame;
import flxanimate.animate.FlxTimeline;
import flixel.graphics.frames.FlxFrame; import flixel.graphics.frames.FlxFrame;
import flixel.animation.FlxAnimation; import flixel.animation.FlxAnimation;
import openfl.geom.Rectangle; import openfl.geom.Rectangle;
import funkin.backend.utils.XMLUtil.AnimData; import funkin.backend.utils.XMLUtil.AnimData;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import funkin.editors.character.CharacterAnimsWindow;
using StringTools; using StringTools;
@@ -26,6 +21,7 @@ class CharacterAnimButton extends UIButton {
public var animationDisplayBG:UISliceSprite; public var animationDisplayBG:UISliceSprite;
public var nameTextBox:UITextBox; public var nameTextBox:UITextBox;
public var animTextBox:UIAutoCompleteTextBox; public var animTextBox:UIAutoCompleteTextBox;
public var animLabelCheckbox:UICheckbox;
public var positionXStepper:UINumericStepper; public var positionXStepper:UINumericStepper;
public var positionYStepper:UINumericStepper; public var positionYStepper:UINumericStepper;
public var fpsStepper:UINumericStepper; public var fpsStepper:UINumericStepper;
@@ -116,13 +112,20 @@ class CharacterAnimButton extends UIButton {
foldableButtons.push(fpsStepper); foldableButtons.push(fpsStepper);
addLabelOn(fpsStepper, translate("fps"), 12); addLabelOn(fpsStepper, translate("fps"), 12);
loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, "Looping?", animData.loop, 0, true); loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, translate("looping"), animData.loop, 0, true);
loopedCheckbox.onChecked = (newLooping:Bool) -> {this.changeLooping(newLooping);}; loopedCheckbox.onChecked = (newLooping:Bool) -> {this.changeLooping(newLooping);};
members.push(loopedCheckbox); members.push(loopedCheckbox);
foldableButtons.push(loopedCheckbox); foldableButtons.push(loopedCheckbox);
loopedCheckbox.x += 8; loopedCheckbox.y += 6; loopedCheckbox.x += 8; loopedCheckbox.y += 6;
if (parent.character.isAnimate) {
animLabelCheckbox = new UICheckbox(loopedCheckbox.x, animTextBox.y + 26, translate("label"), animData.label, 0, true);
animLabelCheckbox.onChecked = (newLabel:Bool) -> {this.changeLabel(newLabel);};
members.push(animLabelCheckbox);
foldableButtons.push(animLabelCheckbox);
}
indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y, CoolUtil.formatNumberRange(animData.indices.getDefault([]), ", "), 278, 22, false, true); indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y, CoolUtil.formatNumberRange(animData.indices.getDefault([]), ", "), 278, 22, false, true);
indicesTextBox.onChange = (text:String) -> { indicesTextBox.onChange = (text:String) -> {
this.changeIndicies(CoolUtil.parseNumberRange(text)); this.changeIndicies(CoolUtil.parseNumberRange(text));
@@ -204,6 +207,7 @@ class CharacterAnimButton extends UIButton {
fpsStepper.follow(this, 282, 118); fpsStepper.follow(this, 282, 118);
loopedCheckbox.follow(this, 336, 122); loopedCheckbox.follow(this, 336, 122);
animLabelCheckbox?.follow(this, 336, 98);
indicesTextBox.follow(this, 158, 164); indicesTextBox.follow(this, 158, 164);
@@ -231,18 +235,12 @@ class CharacterAnimButton extends UIButton {
} }
var oldName:String = anim; var oldName:String = anim;
if (parent.character.animateAtlas != null) {
var animSymbol:FlxSymbolAnimation = __getAnimationSymbol();
parent.character.animateAtlas.anim.animsMap.remove(anim); var flxAnimation:FlxAnimation = __getFlxAnimation();
parent.character.animateAtlas.anim.animsMap.set(newName, animSymbol); flxAnimation.name = newName;
} else {
var flxAnimation:FlxAnimation = __getFlxAnimation();
flxAnimation.name = newName;
parent.character.animation._animations.remove(anim); parent.character.animation._animations.remove(anim);
parent.character.animation._animations.set(newName, flxAnimation); parent.character.animation._animations.set(newName, flxAnimation);
}
var animData:AnimData = parent.character.animDatas[anim]; var animData:AnimData = parent.character.animDatas[anim];
animData.name = newName; animData.name = newName;
@@ -254,7 +252,7 @@ class CharacterAnimButton extends UIButton {
parent.character.animOffsets.remove(anim); parent.character.animOffsets.remove(anim);
parent.character.animOffsets.set(newName, animOffset); parent.character.animOffsets.set(newName, animOffset);
var displayFrame:{scale:Float, animBounds:Rectangle, frame:Int} = parent.displayAnimsFramesList[anim]; var displayFrame:DisplayAnimFrameEntry = parent.displayAnimsFramesList[anim];
parent.displayAnimsFramesList.remove(anim); parent.displayAnimsFramesList.remove(anim);
parent.displayAnimsFramesList.set(newName, displayFrame); parent.displayAnimsFramesList.set(newName, displayFrame);
@@ -276,23 +274,20 @@ class CharacterAnimButton extends UIButton {
var oldAnim:String = animData.anim; var oldAnim:String = animData.anim;
animData.anim = newAnim; animData.anim = newAnim;
if (parent.character.animateAtlas != null) var flxAnimation:FlxAnimation = __getFlxAnimation();
__refreshAnimation(); flxAnimation.prefix = newAnim;
else {
var flxAnimation:FlxAnimation = __getFlxAnimation();
flxAnimation.prefix = newAnim;
refreshFlxAnimationFrames(flxAnimation, animData); refreshAnimation(__getFlxAnimation(), data);
if (valid) { if (valid) {
parent.buildAnimDisplay(anim, data); parent.buildAnimDisplay(anim, data);
animationDisplayBG.alpha = 1; animationDisplayBG.alpha = 1;
} else { } else {
parent.removeAnimDisplay(anim); parent.removeAnimDisplay(anim);
animationDisplayBG.alpha = 0.4; animationDisplayBG.alpha = 0.4;
}
} }
if (parent.character.getAnimName() == anim) if (parent.character.getAnimName() == anim)
CharacterEditor.instance.playAnimation(anim); CharacterEditor.instance.playAnimation(anim);
@@ -330,13 +325,8 @@ class CharacterAnimButton extends UIButton {
var oldFPS:Float = animData.fps; var oldFPS:Float = animData.fps;
animData.fps = newFPS; animData.fps = newFPS;
if (parent.character.animateAtlas != null) { var flxAnimation:FlxAnimation = __getFlxAnimation();
var animSymbol:FlxSymbolAnimation = __getAnimationSymbol(); flxAnimation.frameRate = newFPS;
animSymbol.frameRate = newFPS;
} else {
var flxAnimation:FlxAnimation = __getFlxAnimation();
flxAnimation.frameRate = newFPS;
}
if (parent.character.getAnimName() == anim) if (parent.character.getAnimName() == anim)
CharacterEditor.instance.playAnimation(anim); CharacterEditor.instance.playAnimation(anim);
@@ -351,13 +341,8 @@ class CharacterAnimButton extends UIButton {
animData.loop = newLooping; animData.loop = newLooping;
if (parent.character.animateAtlas != null) { var flxAnimation:FlxAnimation = __getFlxAnimation();
var animSymbol:FlxSymbolAnimation = __getAnimationSymbol(); flxAnimation.looped = animData.loop;
animSymbol.instance.symbol.loop = animData.loop ? Loop : PlayOnce;
} else {
var flxAnimation:FlxAnimation = __getFlxAnimation();
flxAnimation.looped = animData.loop;
}
if (parent.character.getAnimName() == anim) if (parent.character.getAnimName() == anim)
CharacterEditor.instance.playAnimation(anim); CharacterEditor.instance.playAnimation(anim);
@@ -367,6 +352,19 @@ class CharacterAnimButton extends UIButton {
if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditLooping(ID, newLooping)); if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditLooping(ID, newLooping));
} }
public function changeLabel(newLabel:Bool, addToUndo:Bool = true) @:privateAccess {
var animData:AnimData = parent.character.animDatas[anim];
animData.label = newLabel;
if (animLabelCheckbox != null)
animLabelCheckbox.checked = newLabel;
__refreshAnimation();
if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditLabel(ID, newLabel));
}
public function changeIndicies(indicies:Array<Int>, addToUndo:Bool = true) @:privateAccess { public function changeIndicies(indicies:Array<Int>, addToUndo:Bool = true) @:privateAccess {
var animData:AnimData = parent.character.animDatas[anim]; var animData:AnimData = parent.character.animDatas[anim];
var oldIndices:Array<Int> = animData.indices.copy(); var oldIndices:Array<Int> = animData.indices.copy();
@@ -403,58 +401,14 @@ class CharacterAnimButton extends UIButton {
CharacterEditor.undos.addToUndo(CAnimEditIndices(ID, oldIndices, indicies)); CharacterEditor.undos.addToUndo(CAnimEditIndices(ID, oldIndices, indicies));
} }
public inline function refreshFlxAnimationFrames(flxAnimation:FlxAnimation, animData:AnimData) @:privateAccess {
try {
if (animData.indices.length > 0) {
var frameIndices:Array<Int> = new Array<Int>();
parent.character.animation.byIndicesHelper(frameIndices, flxAnimation.prefix, animData.indices, "");
flxAnimation.frames = frameIndices;
} else {
final animFrames:Array<FlxFrame> = new Array<FlxFrame>();
parent.character.animation.findByPrefix(animFrames, flxAnimation.prefix);
final frameIndices:Array<Int> = [];
parent.character.animation.byPrefixHelper(frameIndices, animFrames, flxAnimation.prefix);
flxAnimation.frames = frameIndices;
}
if (flxAnimation.frames.length <= 0) invalidate();
else validate();
} catch (e) {
trace('ERROR REFRESHING FLXANIMATION FRAMES: $e');
invalidate();
}
}
public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess {
var wasRefreshed:Bool = false;
try {
parent.character.animateAtlas.anim.animsMap.remove(animData.name);
if (animData.indices.length > 0)
parent.character.animateAtlas.anim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
else
parent.character.animateAtlas.anim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
wasRefreshed = parent.character.animateAtlas.anim.animsMap.exists(animData.name);
if (!wasRefreshed) __getAnimationSymbol();
} catch (e) {
trace('ERROR REFRESHING SYMBOL FRAMES: $e');
}
validate(wasRefreshed);
}
public function toggleGhost() { public function toggleGhost() {
if (valid && parent.ghosts.indexOf(anim) == -1) { if (valid && parent.ghosts.indexOf(anim) == -1) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND);
parent.ghosts.push(anim); parent.ghosts.push(anim);
ghostIcon.animation.play("alive", true); ghostIcon.animation.play("alive", true);
ghostIcon.color = 0xFFFFFFFF; ghostIcon.color = 0xFFFFFFFF;
} else { } else {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND);
parent.ghosts.remove(anim); parent.ghosts.remove(anim);
ghostIcon.animation.play("dead", true); ghostIcon.animation.play("dead", true);
ghostIcon.color = 0xFFADADAD; ghostIcon.color = 0xFFADADAD;
@@ -466,8 +420,8 @@ class CharacterAnimButton extends UIButton {
super.draw(); super.draw();
if (!closed && parent.displayAnimsFramesList.exists(anim)) { if (!closed && parent.displayAnimsFramesList.exists(anim)) {
var displayData:{frame:Int, scale:Float, animBounds:Rectangle} = parent.displayAnimsFramesList.get(anim); var displayData:DisplayAnimFrameEntry = parent.displayAnimsFramesList.get(anim);
parent.displayWindowSprite.frame = parent.displayWindowSprite.frames.frames[displayData.frame]; parent.displayWindowSprite.frame = displayData.frame;
parent.displayWindowSprite.scale.x = parent.displayWindowSprite.scale.y = displayData.scale; parent.displayWindowSprite.scale.x = parent.displayWindowSprite.scale.y = displayData.scale;
parent.displayWindowSprite.updateHitbox(); parent.displayWindowSprite.updateHitbox();
@@ -521,16 +475,26 @@ class CharacterAnimButton extends UIButton {
return parent.character.animation._animations[anim]; return parent.character.animation._animations[anim];
} }
public inline function __getAnimationSymbol():Null<FlxSymbolAnimation> @:privateAccess { public inline function refreshAnimation(anim:FlxAnimation, animData:AnimData) @:privateAccess {
if (!parent.character.animateAtlas.anim.animsMap.exists(anim)) var refreshed:Bool = false;
XMLUtil.addAnimToSprite(parent.character, data);
return parent.character.animateAtlas.anim.animsMap[anim]; parent.character.animation.remove(animData.name);
parent.character.animation._curAnim = null;
XMLUtil.addAnimToSprite(parent.character, animData);
var anim = parent.character.animation.getByName(animData.name);
if (anim != null && anim.frames.length > 0) {
validate();
parent.character.animation.play(animData.name);
}
else
invalidate();
} }
@:noCompletion function __refreshAnimation() @:privateAccess { @:noCompletion function __refreshAnimation() @:privateAccess {
if (parent.character.animateAtlas != null) refreshSymbolKeyFrames(__getAnimationSymbol(), data); refreshAnimation(__getFlxAnimation(), data);
else refreshFlxAnimationFrames(__getFlxAnimation(), data);
if (valid) { if (valid) {
parent.buildAnimDisplay(anim, data); parent.buildAnimDisplay(anim, data);
@@ -8,15 +8,24 @@ import openfl.geom.Rectangle;
import flixel.graphics.frames.FlxFrame; import flixel.graphics.frames.FlxFrame;
import funkin.game.Character; import funkin.game.Character;
import flixel.animation.FlxPrerotatedAnimation; import flixel.animation.FlxPrerotatedAnimation;
import animate.internal.RenderTexture;
import flixel.graphics.frames.FlxFrame;
import funkin.backend.system.Logs;
using funkin.backend.utils.BitmapUtil; using funkin.backend.utils.BitmapUtil;
typedef DisplayAnimFrameEntry = {
scale:Float,
frame:FlxFrame,
renderTexture:RenderTexture
}
class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> { class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
public var character:CharacterGhost; public var character:CharacterGhost;
public var displayWindowSprite:FlxSprite; public var displayWindowSprite:FlxSprite;
public var displayWindowGraphic:FlxGraphic; public var displayWindowGraphic:FlxGraphic;
public var displayAnimsFramesList:Map<String, {scale:Float, animBounds:Rectangle, frame:OneOfTwo<Int, String>}> = []; public var displayAnimsFramesList:Map<String, DisplayAnimFrameEntry> = [];
public var animButtons:Map<String, CharacterAnimButton> = []; public var animButtons:Map<String, CharacterAnimButton> = [];
public var animsList:Array<String> = []; public var animsList:Array<String> = [];
@@ -30,7 +39,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
buttonCameras.pixelPerfectRender = true; buttonCameras.pixelPerfectRender = true;
if (Assets.exists(Paths.image('characters/${character.sprite}')) && character.animateAtlas == null) if (Assets.exists(Paths.image('characters/${character.sprite}')))
displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false)); displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false));
displayWindowSprite = new FlxSprite(); displayWindowSprite = new FlxSprite();
@@ -69,37 +78,48 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
} }
public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess { public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess {
if (character.animateAtlas == null) { var anim:FlxAnimation = character.animation._animations[anim.name];
var anim:FlxAnimation = character.animation._animations[anim.name]; if (anim == null) return;
if (anim == null || anim.frames.length <= 0) return;
if (character.isAnimate) {
var renderTex = character.generateRenderTextureForAnim(name);
var frame = renderTex.graphic.imageFrame.frame;
displayAnimsFramesList.set(name, {
frame: frame,
scale: 104/Math.max(frame.frame.width, frame.frame.height),
renderTexture: renderTex
});
} else {
var frameIndex:Int = anim.frames.getDefault([0])[0]; var frameIndex:Int = anim.frames.getDefault([0])[0];
var frame:FlxFrame = displayWindowSprite.frames.frames[frameIndex]; var frame:FlxFrame = displayWindowSprite.frames.frames[frameIndex];
var frameRect:Rectangle = new Rectangle(frame.offset.x, frame.offset.y, frame.sourceSize.x, frame.sourceSize.y); var frameRect:Rectangle = new Rectangle(frame.offset.x, frame.offset.y, frame.sourceSize.x, frame.sourceSize.y);
var animBounds:Rectangle = displayWindowGraphic != null ? displayWindowGraphic.bitmap.bounds(frameRect) : frameRect; var animBounds:Rectangle = displayWindowGraphic != null ? displayWindowGraphic.bitmap.bounds(frameRect) : frameRect;
displayAnimsFramesList.set(name, {frame: anim.frames.getDefault([0])[0], scale: 104/Math.max(animBounds.width, animBounds.height), animBounds: animBounds}); displayAnimsFramesList.set(name, {
} else { frame: character.frames.frames[anim.frames.getDefault([0])[0]],
character.storeAtlasState(); scale: 104/Math.max(animBounds.width, animBounds.height),
renderTexture: null
/* });
character.animateAtlas.anim.play(anim.name, true, false, 0);
character.animateAtlas.anim.stop();
var animBounds:Rectangle = MatrixUtil.getBounds(character).copyToFlash();
displayAnimsFramesList.set(name, {frame: anim.anim, scale: 104/animBounds.height, animBounds: animBounds});
*/
character.restoreAtlasState();
} }
} }
public function removeAnimDisplay(name:String) public function removeAnimDisplay(name:String) {
if (displayAnimsFramesList.exists(name))
displayAnimsFramesList[name].renderTexture?.destroy();
displayAnimsFramesList.remove(name); displayAnimsFramesList.remove(name);
}
public function clearDisplayAnims() {
for(k=>e in displayAnimsFramesList)
e.renderTexture?.destroy();
displayAnimsFramesList.clear();
}
public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) { public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (buttons.members.length <= 1) return; if (buttons.members.length <= 1) return;
if (character.getAnimName() == button.anim) if (character.getAnimName() == button.anim)
@:privateAccess CharacterEditor.instance._animation_down(null); @:privateAccess CharacterEditor.instance._animation_down(null);
@@ -134,6 +154,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
x: 0, y: 0, x: 0, y: 0,
indices: [], indices: [],
animType: NONE, animType: NONE,
label: false
}; };
addAnimation(animData); addAnimation(animData);
} }
@@ -453,7 +453,7 @@ class CharacterEditor extends UIState {
function _file_save(_) { function _file_save(_) {
#if sys #if sys
FlxG.sound.play(Paths.sound('editors/save')); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
CoolUtil.safeSaveFile( CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml', '${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
buildCharacter() buildCharacter()
@@ -465,7 +465,7 @@ class CharacterEditor extends UIState {
} }
function _file_saveas(_) { function _file_saveas(_) {
FlxG.sound.play(Paths.sound('editors/save')); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
openSubState(new SaveSubstate(buildCharacter(), { openSubState(new SaveSubstate(buildCharacter(), {
defaultSaveFile: '${character.curCharacter}.xml' defaultSaveFile: '${character.curCharacter}.xml'
})); }));
@@ -499,7 +499,7 @@ class CharacterEditor extends UIState {
} }
function _undo(undo:CharacterEditorChange) { function _undo(undo:CharacterEditorChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
switch (undo) { switch (undo) {
case null: // do nothing case null: // do nothing
case CCharEditPosition(oldPos, newPos): case CCharEditPosition(oldPos, newPos):
@@ -554,6 +554,9 @@ class CharacterEditor extends UIState {
case CAnimEditLooping(animID, newLooping): case CAnimEditLooping(animID, newLooping):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeLooping(!newLooping, false); button.changeLooping(!newLooping, false);
case CAnimEditLabel(animID, newLabel):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeLabel(!newLabel, false);
case CAnimEditIndices(animID, oldIndicies, newIndicies): case CAnimEditIndices(animID, oldIndicies, newIndicies):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeIndicies(oldIndicies, false); button.changeIndicies(oldIndicies, false);
@@ -570,7 +573,7 @@ class CharacterEditor extends UIState {
} }
function _redo(redo:CharacterEditorChange) { function _redo(redo:CharacterEditorChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
switch (redo) { switch (redo) {
case null: // do nothing case null: // do nothing
case CCharEditPosition(oldPos, newPos): case CCharEditPosition(oldPos, newPos):
@@ -625,6 +628,9 @@ class CharacterEditor extends UIState {
case CAnimEditLooping(animID, newLooping): case CAnimEditLooping(animID, newLooping):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeLooping(newLooping, false); button.changeLooping(newLooping, false);
case CAnimEditLabel(animID, newLabel):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeLabel(newLabel, false);
case CAnimEditIndices(animID, oldIndicies, newIndicies): case CAnimEditIndices(animID, oldIndicies, newIndicies):
var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID];
button.changeIndicies(newIndicies, false); button.changeIndicies(newIndicies, false);
@@ -936,6 +942,7 @@ enum CharacterEditorChange {
CAnimEditFPS(animID:Int, oldFPS:Float, newFPS:Float); CAnimEditFPS(animID:Int, oldFPS:Float, newFPS:Float);
CAnimEditLooping(animID:Int, newLooping:Bool); CAnimEditLooping(animID:Int, newLooping:Bool);
CAnimEditIndices(animID:Int, oldIndicies:Array<Int>, newIndicies:Array<Int>); CAnimEditIndices(animID:Int, oldIndicies:Array<Int>, newIndicies:Array<Int>);
CAnimEditLabel(animID:Int, newLabel:Bool);
CCharClearOffsets(oldOffsets:Map<String, FlxPoint>); CCharClearOffsets(oldOffsets:Map<String, FlxPoint>);
} }
@@ -4,6 +4,8 @@ import funkin.backend.system.FakeCamera.FakeCallCamera;
import openfl.geom.ColorTransform; import openfl.geom.ColorTransform;
import flixel.animation.FlxAnimation; import flixel.animation.FlxAnimation;
import funkin.game.Character; import funkin.game.Character;
import haxe.xml.Access;
import animate.internal.RenderTexture;
typedef AtlasState = { typedef AtlasState = {
var oldAnim:String; var oldAnim:String;
@@ -14,39 +16,35 @@ typedef AtlasState = {
class CharacterGhost extends Character { class CharacterGhost extends Character {
public var ghosts:Array<String> = []; public var ghosts:Array<String> = [];
public override function buildCharacter(xml:Access) {
useRenderTexture = true; // per request from maru heh, still overridable
super.buildCharacter(xml);
}
public override function draw() @:privateAccess { public override function draw() @:privateAccess {
ghostDraw = FakeCallCamera.instance.ignoreDraws = true; ghostDraw = FakeCallCamera.instance.ignoreDraws = true;
var wasInvalidFrame:Bool = !colorTransform.__isDefault(false); var wasInvalidFrame:Bool = !colorTransform.__isDefault(false);
colorTransform.__identity(); colorTransform.__identity();
if (animateAtlas != null) { var animName = animation.curAnim?.name ?? null;
storeAtlasState(); var animFrame = animation.curAnim?.curFrame ?? 0;
for (anim in ghosts) { var animFrameTimer = animation.curAnim?._frameTimer ?? 0;
animateAtlas.anim.play(anim, true, false, 0);
setAnimOffset(anim);
alpha = 0.4; color = 0xFFAEAEAE; for (anim in ghosts) @:privateAccess {
super.draw(); alpha = 0.4; color = 0xFFAEAEAE;
}
if (ghosts.length > 0) restoreAtlasState(); var flxanim:FlxAnimation = animation._animations.get(anim);
animation.play(anim, true, false, flxanim.frames.length - 1);
setAnimOffset(anim);
super.draw();
} }
else {
for (anim in ghosts) @:privateAccess {
alpha = 0.4; color = 0xFFAEAEAE;
var flxanim:FlxAnimation = animation._animations.get(anim); if (ghosts.length > 0)
var frameIndex:Int = flxanim.frames.getDefault([0])[flxanim.frames.length - 1]; frame = frames.frames[animation.frameIndex];
frame = frames.frames[frameIndex];
setAnimOffset(anim);
super.draw();
}
if (ghosts.length > 0)
frame = frames.frames[animation.frameIndex];
}
ghostDraw = FakeCallCamera.instance.ignoreDraws = false; ghostDraw = FakeCallCamera.instance.ignoreDraws = false;
alpha = 1; color = 0xFFFFFFFF; alpha = 1; color = 0xFFFFFFFF;
@@ -54,8 +52,12 @@ class CharacterGhost extends Character {
frameOffset.set(0, 0); frameOffset.set(0, 0);
offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y); offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y);
colorTransform.color = 0xFFEF0202; colorTransform.color = 0xFFEF0202;
} else } else {
setAnimOffset(animateAtlas != null ? atlasPlayingAnim : animation.name); animation.play(animName, true, false, animFrame);
setAnimOffset(animName);
if (animation.curAnim != null)
animation.curAnim._frameTimer = animFrameTimer;
}
super.draw(); super.draw();
} }
@@ -70,22 +72,45 @@ class CharacterGhost extends Character {
offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y); offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y);
} }
public function generateRenderTextureForAnim(anim:String):RenderTexture @:privateAccess {
if (!isAnimate) return null;
var wasInvalidFrame:Bool = !colorTransform.__isDefault(false);
colorTransform.__identity();
var animName = animation.curAnim?.name ?? null;
var animFrame = animation.curAnim?.curFrame ?? 0;
var animFrameTimer = animation.curAnim?._frameTimer ?? 0;
var oldRenderTexture:RenderTexture = this._renderTexture;
var oldRenderTextureDirty = _renderTextureDirty;
var oldAlpha = alpha;
this._renderTexture = null;
this._renderTextureDirty = true;
this.alpha = 256.0/257.0;
animation.play(anim, true, false, 0);
drawAnimate(FakeCallCamera.instance);
var renderTex = this._renderTexture;
this._renderTexture = oldRenderTexture;
this._renderTextureDirty = oldRenderTextureDirty;
this.alpha = oldAlpha;
if (wasInvalidFrame) {
colorTransform.color = 0xFFEF0202;
} else {
animation.play(animName, true, false, animFrame);
if (animation.curAnim != null)
animation.curAnim._frameTimer = animFrameTimer;
}
return renderTex;
}
// This gets annoying lmao -lunar // This gets annoying lmao -lunar
private var atlasState:AtlasState = null; // it's gone now <3 -yosh
public function storeAtlasState():AtlasState @:privateAccess {
return atlasState = {
oldAnim: atlasPlayingAnim,
oldFrame: animateAtlas.anim.curFrame,
oldTick: animateAtlas.anim._tick,
oldPlaying: animateAtlas.anim.isPlaying,
};
}
public function restoreAtlasState(state:AtlasState = null) @:privateAccess {
if (state == null) state = atlasState;
animateAtlas.anim.play(state.oldAnim, true, false, state.oldFrame);
animateAtlas.anim._tick = state.oldTick;
animateAtlas.anim.isPlaying = state.oldPlaying;
}
} }
@@ -1,7 +1,6 @@
package funkin.editors.character; package funkin.editors.character;
import funkin.backend.FlxAnimate;
import haxe.io.Path; import haxe.io.Path;
import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo; import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo;
import funkin.game.Character; import funkin.game.Character;
@@ -184,31 +183,15 @@ class CharacterPropertiesWindow extends UISliceSprite {
character.sprite = sprite; character.sprite = sprite;
animsWindow.displayWindowSprite.animation.reset(); animsWindow.displayWindowSprite.animation.reset();
animsWindow.displayAnimsFramesList.clear(); animsWindow.clearDisplayAnims();
if (animsWindow.displayWindowGraphic != null) if (animsWindow.displayWindowGraphic != null)
animsWindow.displayWindowGraphic.destroy(); animsWindow.displayWindowGraphic.destroy();
if (Assets.exists('$noExt/Animation.json')) { character.frames = Paths.getFrames(path, true);
if (character.animateAtlas == null) {
character.animation.reset();
character.animateAtlas = new FlxAnimate(character.x, character.y);
} else
character.animateAtlas.anim.stop();
character.atlasPath = noExt; animsWindow.displayWindowSprite.loadGraphicFromSprite(character);
character.animateAtlas.loadAtlas(noExt); if (Assets.exists(Paths.image('characters/${character.sprite}')))
} else { animsWindow.displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false));
if (character.animateAtlas != null) {
character.animateAtlas.destroy();
character.animateAtlas = null;
character.atlasPlayingAnim = character.atlasPath = null;
}
character.frames = Paths.getFrames(path, true);
animsWindow.displayWindowSprite.loadGraphicFromSprite(character);
if (Assets.exists(Paths.image('characters/${character.sprite}')))
animsWindow.displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false));
}
character.animDatas.clear(); character.animDatas.clear();
for (anim in animsWindow.buttons) anim.checkValid(); // Re-add all animations for (anim in animsWindow.buttons) anim.checkValid(); // Re-add all animations
+221 -59
View File
@@ -77,10 +77,10 @@ class Charter extends UIState {
public var strumlineLockButton:CharterStrumlineButton; public var strumlineLockButton:CharterStrumlineButton;
public var hitsound:FlxSound; public var hitsound:FlxSound;
public var hitsoundGlobalVolume:Float = 1.0;
public var metronome:FlxSound; public var metronome:FlxSound;
public var vocals:FlxSound; public var vocals:FlxSound;
public var voicesMuted:Bool = false;
public var quant:Int = 16; public var quant:Int = 16;
public var quants:Array<Int> = [4, 8, 12, 16, 20, 24, 32, 48, 64, 192]; // different quants public var quants:Array<Int> = [4, 8, 12, 16, 20, 24, 32, 48, 64, 192]; // different quants
@@ -96,6 +96,7 @@ class Charter extends UIState {
public var rightEventRowText:UIText; public var rightEventRowText:UIText;
public var leftEventsGroup:CharterEventGroup = new CharterEventGroup(); public var leftEventsGroup:CharterEventGroup = new CharterEventGroup();
public var rightEventsGroup:CharterEventGroup = new CharterEventGroup(); public var rightEventsGroup:CharterEventGroup = new CharterEventGroup();
public var cameraMovementChanges:Array<CameraChange> = [];
public var charterCamera:FlxCamera; public var charterCamera:FlxCamera;
public var uiCamera:FlxCamera; public var uiCamera:FlxCamera;
@@ -231,6 +232,11 @@ class Charter extends UIState {
label: translate("edit.delete"), label: translate("edit.delete"),
keybind: [DELETE], keybind: [DELETE],
onSelect: _edit_delete onSelect: _edit_delete
},
{
label: translate("edit.deletestacked"),
keybind: [SHIFT,DELETE],
onSelect: _edit_deletestacked
} }
] ]
}, },
@@ -308,6 +314,11 @@ class Charter extends UIState {
onSelect: _view_showeventBeatSeparator, onSelect: _view_showeventBeatSeparator,
icon: Options.charterShowBeats ? 1 : 0 icon: Options.charterShowBeats ? 1 : 0
}, },
{
label: translate("view.showCameraHighlights"),
onSelect: _view_showeventCameraHighlights,
icon: Options.charterShowCameraHighlights ? 1 : 0
},
null, null,
{ {
label: translate("view.rainbowWaveforms"), label: translate("view.rainbowWaveforms"),
@@ -515,7 +526,7 @@ class Charter extends UIState {
strumlineLockButton = new CharterStrumlineButton("editors/charter/lock-strumline", translate("lock-unlock")); strumlineLockButton = new CharterStrumlineButton("editors/charter/lock-strumline", translate("lock-unlock"));
strumlineLockButton.onClick = function () { strumlineLockButton.onClick = function () {
FlxG.sound.play(Paths.sound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND)); UIState.playEditorSound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND);
if (strumLines != null) { if (strumLines != null) {
strumLines.draggable = !strumLines.draggable; strumLines.draggable = !strumLines.draggable;
strumlineLockButton.textTweenColor.color = strumLines.draggable ? 0xFF5C95CA : 0xFFE16565; strumlineLockButton.textTweenColor.color = strumLines.draggable ? 0xFF5C95CA : 0xFFE16565;
@@ -685,6 +696,7 @@ class Charter extends UIState {
CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY; CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY;
updateWaveforms(); updateWaveforms();
updateCameraChanges();
} }
public function getWavesToGenerate():Array<{name:String, sound:FlxSound}> { public function getWavesToGenerate():Array<{name:String, sound:FlxSound}> {
@@ -740,6 +752,38 @@ class Charter extends UIState {
} }
} }
public function updateCameraChanges() {
if (!Options.charterShowCameraHighlights) return;
cameraMovementChanges = [];
for (grp in [leftEventsGroup, rightEventsGroup]) {
grp.filterEvents();
grp.sortEvents();
for(e in grp.members) {
for(event in e.events) {
if (event.name == "Camera Movement") {
cameraMovementChanges.push({
strumLineID: event.params[0],
step: e.step,
endStep: __endStep
});
}
}
}
}
//need to sort again for both local and global events to be used
cameraMovementChanges.sort(function(e1, e2) {
return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step);
});
//update previous change
if (cameraMovementChanges.length > 0) {
for (i in 1...cameraMovementChanges.length) {
cameraMovementChanges[i-1].endStep = cameraMovementChanges[i].step;
}
}
}
public override function beatHit(curBeat:Int) { public override function beatHit(curBeat:Int) {
super.beatHit(curBeat); super.beatHit(curBeat);
if (FlxG.sound.music.playing) { if (FlxG.sound.music.playing) {
@@ -757,6 +801,7 @@ class Charter extends UIState {
public var mousePos:FlxPoint = new FlxPoint(); public var mousePos:FlxPoint = new FlxPoint();
public var selectionDragging:Bool = false; public var selectionDragging:Bool = false;
public var isSelecting:Bool = false; public var isSelecting:Bool = false;
public var isAltCopyDrag:Bool = false;
public function updateSelectionLogic() { public function updateSelectionLogic() {
function select(s:ICharterSelectable) { function select(s:ICharterSelectable) {
@@ -802,7 +847,7 @@ class Charter extends UIState {
else else
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint}); Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
FlxG.sound.play(Paths.sound('editors/save')); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
undos.save(); undos.save();
} }
autoSaveNotif.cancelled = false; autoSaveNotif.cancelled = false;
@@ -921,10 +966,17 @@ class Charter extends UIState {
if (!(verticalChange == 0 && horizontalChange == 0)) { if (!(verticalChange == 0 && horizontalChange == 0)) {
notesGroup.sortNotes(); notesGroup.sortNotes();
undos.addToUndo(CChangeBundle([ var changes:Array<CharterChange> = [];
CSelectionDrag(undoDrags), if (isAltCopyDrag) {
updateEventsGroups(selection) changes.push(CCreateSelection(selection.copy()));
])); isAltCopyDrag = false;
}
changes.push(CSelectionDrag(undoDrags));
changes.push(updateEventsGroups(selection));
undos.addToUndo(CChangeBundle(changes));
} else if (isAltCopyDrag) {
undos.addToUndo(CCreateSelection(selection.copy()));
isAltCopyDrag = false;
} }
gridActionType = NONE; gridActionType = NONE;
@@ -962,7 +1014,7 @@ class Charter extends UIState {
notesGroup.add(note); notesGroup.add(note);
selection = [note]; selection = [note];
undos.addToUndo(CCreateSelection([note])); undos.addToUndo(CCreateSelection([note]));
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND);
} }
isSelecting = false; isSelecting = false;
} }
@@ -979,7 +1031,32 @@ class Charter extends UIState {
} }
if ((Math.abs(mousePos.x - dragStartPos.x) > (noteSusDrag ? 1 : 5) || Math.abs(mousePos.y - dragStartPos.y) > (noteSusDrag ? 1 : 5))) { if ((Math.abs(mousePos.x - dragStartPos.x) > (noteSusDrag ? 1 : 5) || Math.abs(mousePos.y - dragStartPos.y) > (noteSusDrag ? 1 : 5))) {
if (noteHovered) gridActionType = noteHovered ? NOTE_DRAG : INVALID_DRAG; if (noteHovered) {
if (FlxG.keys.pressed.ALT && selection.length > 0) {
var newSelection:Array<ICharterSelectable> = [];
for (s in selection) {
if (s is CharterNote) {
var n:CharterNote = cast s;
var newNote = new CharterNote();
newNote.updatePos(n.step, n.id, n.susLength, n.type, n.strumLine);
notesGroup.add(newNote);
newSelection.push(newNote);
} else if (s is CharterEvent) {
var e:CharterEvent = cast s;
var newEvent = new CharterEvent(e.step, [for (event in e.events) Reflect.copy(event)], e.global);
newEvent.refreshEventIcons();
(e.global ? rightEventsGroup : leftEventsGroup).add(newEvent);
newSelection.push(newEvent);
}
}
for (s in selection) s.selected = false;
selection = newSelection;
for (s in selection) s.selected = true;
isAltCopyDrag = true;
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
}
gridActionType = NOTE_DRAG;
}
if (noteSusDrag) gridActionType = SUSTAIN_DRAG; if (noteSusDrag) gridActionType = SUSTAIN_DRAG;
} }
} }
@@ -1091,7 +1168,7 @@ class Charter extends UIState {
if (selected == null) return selected; if (selected == null) return selected;
if (selected is CharterNote) { if (selected is CharterNote) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
var note:CharterNote = cast selected; var note:CharterNote = cast selected;
note.strumLineID = strumLines.members.indexOf(note.strumLine); note.strumLineID = strumLines.members.indexOf(note.strumLine);
note.strumLine = null; // For static undos :D note.strumLine = null; // For static undos :D
@@ -1571,20 +1648,20 @@ class Charter extends UIState {
else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();} else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();}
} }
function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_save_all(_) {saveEverything(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_save(_) {saveChart(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_saveas(_) {saveChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_events_save(_) {saveEvents(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_events_saveas(_) {saveEventsAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));} function _file_save_no_events(_) {saveChart(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));} function _file_saveas_no_events(_) {saveChartAs(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_meta_save(_) {saveMeta(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_meta_saveas(_) {saveMetaAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _file_saveas_psych(_) {savePsychChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _edit_copy(_, playSFX=true) { function _edit_copy(_, playSFX=true) {
if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND)); if (playSFX) UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
if(selection.length == 0) return; if(selection.length == 0) return;
var minStep:Float = selection[0].step; var minStep:Float = selection[0].step;
@@ -1603,7 +1680,7 @@ class Charter extends UIState {
]; ];
} }
function _edit_paste(_) { function _edit_paste(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_PASTE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
if (clipboard.length <= 0) return; if (clipboard.length <= 0) return;
var minStep = curStep; var minStep = curStep;
@@ -1631,7 +1708,7 @@ class Charter extends UIState {
} }
function _edit_cut(_) { function _edit_cut(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CUT_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
if (selection == null || selection.length == 0) return; if (selection == null || selection.length == 0) return;
_edit_copy(_, false); _edit_copy(_, false);
@@ -1639,7 +1716,7 @@ class Charter extends UIState {
} }
function _edit_delete(_) { function _edit_delete(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (selection == null || selection.length == 0) return; if (selection == null || selection.length == 0) return;
selection.loop((n:CharterNote) -> { selection.loop((n:CharterNote) -> {
noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime}); noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime});
@@ -1647,8 +1724,27 @@ class Charter extends UIState {
selection = deleteSelection(selection, true); selection = deleteSelection(selection, true);
} }
function _edit_deletestacked(_) {
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (notesGroup.members.length == 0) return;
var oldNote:CharterNote = null;
var selectionArray:Array<Dynamic> = ((selection.length != 0) ? selection : notesGroup.members.copy());
var toDelete:Selection = new Selection();
for (note in selectionArray) {
if (oldNote != null && oldNote.step == note.step && oldNote.strumLineID == note.strumLineID && oldNote.id == note.id) {
noteDeleteAnims.deleteNotes.push({note: oldNote, time: noteDeleteAnims.deleteTime});
toDelete.push(oldNote);
}
oldNote = note;
}
if (toDelete.length != 0) {
deleteSelection(toDelete);
if (selection.length != 0) for (i in toDelete) selection.remove(i); //crash prevention
}
}
function _undo(undo:CharterChange) { function _undo(undo:CharterChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
switch(undo) { switch(undo) {
case null: // do nothing case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine): case CDeleteStrumLine(strumLineID, strumLine):
@@ -1697,7 +1793,11 @@ class Charter extends UIState {
case CEditSpecNotesType(notes, oldTypes, newTypes): case CEditSpecNotesType(notes, oldTypes, newTypes):
for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, oldTypes[i]); for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, oldTypes[i]);
case CChangeBundle(changes): case CChangeBundle(changes):
for (change in changes) _undo(change); var i = changes.length - 1;
while (i >= 0) {
_undo(changes[i]);
i--;
}
} }
} }
@@ -1709,7 +1809,7 @@ class Charter extends UIState {
} }
function _redo(redo:CharterChange) { function _redo(redo:CharterChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
switch(redo) { switch(redo) {
case null: // do nothing case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine): case CDeleteStrumLine(strumLineID, strumLine):
@@ -1808,14 +1908,24 @@ class Charter extends UIState {
function _playback_metronome(t) { function _playback_metronome(t) {
t.icon = (Options.charterMetronomeEnabled = !Options.charterMetronomeEnabled) ? 1 : 0; t.icon = (Options.charterMetronomeEnabled = !Options.charterMetronomeEnabled) ? 1 : 0;
} }
function _song_muteinst(t) {
FlxG.sound.music.volume = FlxG.sound.music.volume > 0 ? 0 : 1; public function _slider_mutetoggle(t:UIContextMenuOption) {
t.icon = 1 - Std.int(Math.ceil(FlxG.sound.music.volume)); if (t.slider == null) return;
t.button.slider.value = t.button.slider.value > 0 ? 0 : 1;
} }
function _song_mutevoices(t) {
vocals.volume = (voicesMuted = !voicesMuted) ? 0 : 1; function _song_instvolume(t) {
for (strumLine in strumLines.members) strumLine.updateVoicesVolume(); FlxG.sound.music.volume = t.slider.value;
t.icon = voicesMuted ? 1 : 0; t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_voicesvolume(t) {
vocals.volume = t.slider.value;
for (strumLine in strumLines.members) strumLine.vocals.volume = t.slider.value * strumLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_hitsoundvolume(t) {
hitsoundGlobalVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
} }
function _playback_back(_) { function _playback_back(_) {
if (FlxG.sound.music.playing) return; if (FlxG.sound.music.playing) return;
@@ -1860,6 +1970,7 @@ class Charter extends UIState {
__event.refreshEventIcons(); __event.refreshEventIcons();
(__event.global ? rightEventsGroup : leftEventsGroup).add(__event); (__event.global ? rightEventsGroup : leftEventsGroup).add(__event);
undos.addToUndo(CEditEvent(__event, [], __event.events)); undos.addToUndo(CEditEvent(__event, [], __event.events));
updateCameraChanges();
} }
public function getBookmarkList():Array<ChartBookmark> { public function getBookmarkList():Array<ChartBookmark> {
@@ -2011,26 +2122,56 @@ class Charter extends UIState {
if (bookmarks.length > 0) if (bookmarks.length > 0)
{ {
var bookmarkOptions:Array<UIContextMenuOption> = [];
var goToBookmark = TU.getRaw("charter.bookmarks.goTo"); var goToBookmark = TU.getRaw("charter.bookmarks.goTo");
for (b in bookmarks) for (b in bookmarks)
{ {
newChilds.push({ bookmarkOptions.push({
label: goToBookmark.format([b.name]), label: goToBookmark.format([b.name]),
onSelect: function(_) { Conductor.songPosition = Conductor.getTimeForStep(b.time); } onSelect: function(_) { Conductor.songPosition = Conductor.getTimeForStep(b.time); }
}); });
} }
newChilds.push({
label: translate("bookmarks.bookmarkList"),
childs: bookmarkOptions
});
newChilds.push(null); newChilds.push(null);
} }
newChilds.push({ newChilds.push({
label: translate("song.muteInst"), label: translate("song.inst"),
onSelect: _song_muteinst slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_instvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
}); });
newChilds.push({ newChilds.push({
label: translate("song.muteVoices"), label: translate("song.voices"),
onSelect: _song_mutevoices slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_voicesvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
});
newChilds.push({
label: translate("song.hitsounds"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_hitsoundvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
}); });
if (songTopButton != null) songTopButton.contextMenu = newChilds; if (songTopButton != null) songTopButton.contextMenu = newChilds;
@@ -2055,6 +2196,10 @@ class Charter extends UIState {
function _view_showeventBeatSeparator(t) { function _view_showeventBeatSeparator(t) {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0; t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
} }
function _view_showeventCameraHighlights(t) {
t.icon = (Options.charterShowCameraHighlights = !Options.charterShowCameraHighlights) ? 1 : 0;
updateCameraChanges();
}
function _view_switchWaveformRainbow(t) { function _view_switchWaveformRainbow(t) {
t.icon = (Options.charterRainbowWaveforms = !Options.charterRainbowWaveforms) ? 1 : 0; t.icon = (Options.charterRainbowWaveforms = !Options.charterRainbowWaveforms) ? 1 : 0;
@@ -2080,8 +2225,8 @@ class Charter extends UIState {
inline function _snap_decreasesnap(_) changequant(-1); inline function _snap_decreasesnap(_) changequant(-1);
inline function _snap_resetsnap(_) setquant(16); inline function _snap_resetsnap(_) setquant(16);
inline function changequant(change:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();}; inline function changequant(change:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
inline function setquant(newQuant:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = newQuant; buildSnapsUI();} inline function setquant(newQuant:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = newQuant; buildSnapsUI();}
function buildSnapsUI():Array<UIContextMenuOption> { function buildSnapsUI():Array<UIContextMenuOption> {
var snapsTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[snapIndex]; var snapsTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[snapIndex];
@@ -2117,12 +2262,12 @@ class Charter extends UIState {
} }
inline function _note_addsustain(t) { inline function _note_addsustain(t) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND);
changeNoteSustain(1); changeNoteSustain(1);
} }
inline function _note_subtractsustain(t) { inline function _note_subtractsustain(t) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND);
changeNoteSustain(-1); changeNoteSustain(-1);
} }
@@ -2199,16 +2344,18 @@ class Charter extends UIState {
keybind: [CONTROL, SHIFT, A], keybind: [CONTROL, SHIFT, A],
onSelect: _note_selectmeasure onSelect: _note_selectmeasure
}, },
null, null
{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}
]; ];
var noteKeys:Array<FlxKey> = [ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE]; var noteTypeOptions:Array<UIContextMenuOption> = [{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}];
final noteKeys:Array<Array<Array<FlxKey>>> = [[[ZERO], [NUMPADZERO]], [[ONE], [NUMPADONE]], [[TWO], [NUMPADTWO]], [[THREE], [NUMPADTHREE]], [[FOUR], [NUMPADFOUR]], [[FIVE], [NUMPADFIVE]],
[[SIX], [NUMPADSIX]], [[SEVEN], [NUMPADSEVEN]], [[EIGHT], [NUMPADEIGHT]], [[NINE], [NUMPADNINE]]];
for (i=>type in noteTypes) { for (i=>type in noteTypes) {
var realNoteID:Int = i+1; // Default Note not stored var realNoteID:Int = i+1; // Default Note not stored
var newChild:UIContextMenuOption = { var newChild:UIContextMenuOption = {
@@ -2217,9 +2364,13 @@ class Charter extends UIState {
onSelect: (_) -> {changeNoteType(realNoteID);}, onSelect: (_) -> {changeNoteType(realNoteID);},
icon: this.noteType == realNoteID ? 1 : 0 icon: this.noteType == realNoteID ? 1 : 0
}; };
if (realNoteID <= 9) newChild.keybind = [noteKeys[realNoteID]]; if (realNoteID <= 9) newChild.keybinds = noteKeys[realNoteID];
newChilds.push(newChild); noteTypeOptions.push(newChild);
} }
newChilds.push({
label: translate("note.noteTypesList"),
childs: noteTypeOptions
});
newChilds.push({ newChilds.push({
label: translate("note.editNoteTypesList"), label: translate("note.editNoteTypesList"),
color: 0xFF959829, icon: 4, color: 0xFF959829, icon: 4,
@@ -2227,6 +2378,7 @@ class Charter extends UIState {
onSelect: editNoteTypesList onSelect: editNoteTypesList
}); });
if (noteTopButton != null) noteTopButton.contextMenu = newChilds; if (noteTopButton != null) noteTopButton.contextMenu = newChilds;
if (topMenu != null && topMenu[noteIndex] != null) topMenu[noteIndex].childs = newChilds;
return newChilds; return newChilds;
} }
@@ -2314,8 +2466,12 @@ class Charter extends UIState {
} }
} }
public inline function hitsoundsEnabled(id:Int) public inline function playHitsound(id:Int) {
return strumLines.members[id] != null && strumLines.members[id].hitsounds; if (strumLines.members[id] != null && strumLines.members[id].hitsoundVolume > 0 && hitsoundGlobalVolume > 0) {
hitsound.volume = hitsoundGlobalVolume * strumLines.members[id].hitsoundVolume;
hitsound.replay();
}
}
public inline function __fixSelection(selection:Selection):Selection { public inline function __fixSelection(selection:Selection):Selection {
var newSelection:Selection = new Selection(); var newSelection:Selection = new Selection();
@@ -2406,7 +2562,7 @@ class Charter extends UIState {
quantSelected: quant, quantSelected: quant,
noteTypeSelected: noteType, noteTypeSelected: noteType,
strumlinesDraggable: strumLines.draggable, strumlinesDraggable: strumLines.draggable,
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsounds], hitSounds: [for (strumLine in strumLines.members) strumLine.hitsoundVolume > 0],
mutedVocals: [for (strumLine in strumLines.members) !(strumLine.vocals.volume > 0)], mutedVocals: [for (strumLine in strumLines.members) !(strumLine.vocals.volume > 0)],
waveforms: [for (strumLine in strumLines.members) strumLine.selectedWaveform] waveforms: [for (strumLine in strumLines.members) strumLine.selectedWaveform]
} }
@@ -2422,7 +2578,7 @@ class Charter extends UIState {
strumLines.draggable = playtestInfo.strumlinesDraggable; strumLines.draggable = playtestInfo.strumlinesDraggable;
for (i => strumLine in strumLines.members) for (i => strumLine in strumLines.members)
strumLine.hitsounds = playtestInfo.hitSounds[i]; strumLine.hitsoundVolume = playtestInfo.hitSounds[i] ? 1 : 0;
for (i => strumLine in strumLines.members) for (i => strumLine in strumLines.members)
strumLine.vocals.volume = playtestInfo.mutedVocals[i] ? 0 : 1; strumLine.vocals.volume = playtestInfo.mutedVocals[i] ? 0 : 1;
for (i => strumLine in strumLines.members) for (i => strumLine in strumLines.members)
@@ -2514,3 +2670,9 @@ typedef PlaytestInfo = {
var mutedVocals:Array<Bool>; var mutedVocals:Array<Bool>;
var waveforms:Array<Int>; var waveforms:Array<Int>;
} }
typedef CameraChange = {
var strumLineID:Int;
var step:Float;
var endStep:Float;
}
@@ -82,7 +82,7 @@ class CharterAutoSaveUI extends UISliceSprite {
icon.animation.curAnim.curFrame = 1; icon.animation.curAnim.curFrame = 1;
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95; for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND);
(new FlxTimer()).start(1, (_) -> {disappearAnimation();}); (new FlxTimer()).start(1, (_) -> {disappearAnimation();});
}); });
@@ -178,6 +178,8 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
public var gridShader:CustomShader = new CustomShader("engine/charterGrid"); public var gridShader:CustomShader = new CustomShader("engine/charterGrid");
var __lastKeyCount:Int = 4; var __lastKeyCount:Int = 4;
public var cameraHighlight:CameraHighlight;
public function new() { public function new() {
super(); super();
@@ -187,6 +189,10 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
add(gridBackDrop); add(gridBackDrop);
gridShader.hset("segments", 4); gridShader.hset("segments", 4);
cameraHighlight = new CameraHighlight(this);
cameraHighlight.makeSolid(1, 1, 0xFFFFFFFF);
add(cameraHighlight);
waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000); waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000);
waveformSprite.scale.set(160, 1); waveformSprite.scale.set(160, 1);
waveformSprite.updateHitbox(); waveformSprite.updateHitbox();
@@ -247,10 +253,11 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
x = strumLine.x; x = strumLine.x;
alpha = strumLine.strumLine.visible ? 0.9 : 0.4; alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
keyCount = strumLine.keyCount; keyCount = strumLine.keyCount;
cameraHighlight.color = strumLine.highlightColor;
} else alpha = 0.9; } else alpha = 0.9;
for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit, for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit,
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) { topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite, cameraHighlight]) {
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha; spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
spr.cameras = this.cameras; spr.cameras = this.cameras;
} }
@@ -272,6 +279,9 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
spr.updateHitbox(); spr.updateHitbox();
} }
cameraHighlight.scale.x = (keyCount * 40)-2;
cameraHighlight.x += 1;
waveformSprite.visible = waveformSprite.shader != null; waveformSprite.visible = waveformSprite.shader != null;
if (waveformSprite.shader == null) return; if (waveformSprite.shader == null) return;
@@ -311,16 +321,19 @@ class CharterGridSeperatorBase extends FlxSprite {
private static var lastMaxMeasure:Float = -1; private static var lastMaxMeasure:Float = -1;
public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY; public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY;
public static var lastCameraZoom:Float = -1;
private static var beatStepTimes:Array<Float> = []; private static var beatStepTimes:Array<Float> = [];
private static var measureStepTimes:Array<Float> = []; private static var measureStepTimes:Array<Float> = [];
private static var timeSignatureChangeGaps:Array<Float> = []; private static var timeSignatureChangeGaps:Array<Float> = [];
private function recalculateBeats() { private function recalculateBeats() {
@:privateAccess
var currentZoom = Charter.instance.__camZoom;
var conductorSprY = Charter.instance.gridBackdrops.conductorSprY; var conductorSprY = Charter.instance.gridBackdrops.conductorSprY;
if (conductorSprY == lastConductorSprY) return; if (conductorSprY == lastConductorSprY && currentZoom == lastCameraZoom) return; //only update if song pos or camera zoom has changed
var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5); var zoomOffset = ((FlxG.height * (1/currentZoom)) * 0.5);
minStep = (conductorSprY - zoomOffset)/40; minStep = (conductorSprY - zoomOffset)/40;
maxStep = (conductorSprY + zoomOffset)/40; maxStep = (conductorSprY + zoomOffset)/40;
@@ -359,6 +372,7 @@ class CharterGridSeperatorBase extends FlxSprite {
} }
lastConductorSprY = conductorSprY; lastConductorSprY = conductorSprY;
lastCameraZoom = currentZoom;
} }
private inline function calculateTimeSignatureGaps() { private inline function calculateTimeSignatureGaps() {
@@ -446,6 +460,77 @@ class CharterGridSeperator extends CharterGridSeperatorBase {
} }
} }
class CameraHighlight extends FlxSprite {
private var grid:CharterBackdrop;
private var _currentCameraMovementIndex:Int = 0;
public function new(grid:CharterBackdrop) {
this.grid = grid;
super();
}
override public function draw() {
if (!Options.charterShowCameraHighlights || grid.strumLine == null) return;
@:privateAccess
var minStep = CharterGridSeperatorBase.minStep;
@:privateAccess
var maxStep = CharterGridSeperatorBase.maxStep;
@:privateAccess
var strumLineID = Charter.instance.strumLines.isDragging ? Charter.instance.strumLines.__pastStrumlines.indexOf(grid.strumLine) : Charter.instance.gridBackdrops.members.indexOf(grid);
//first default camera change
if ((Charter.instance.cameraMovementChanges[0] == null || Charter.instance.cameraMovementChanges[0].step != 0) && strumLineID == 0) {
var endStep = Charter.instance.cameraMovementChanges[0] != null ? Charter.instance.cameraMovementChanges[0].step : Charter.instance.__endStep;
if (endStep >= minStep) {
setupHighlight(0, endStep); super.draw();
setupLine(endStep); super.draw();
}
}
//update index if gone backwards
while(_currentCameraMovementIndex > 0) {
if (Charter.instance.cameraMovementChanges[_currentCameraMovementIndex] == null || Charter.instance.cameraMovementChanges[_currentCameraMovementIndex].endStep >= minStep) {
_currentCameraMovementIndex--;
} else {
break;
}
}
var seenFirstVisible = false;
for (i in _currentCameraMovementIndex...Charter.instance.cameraMovementChanges.length) {
var change = Charter.instance.cameraMovementChanges[i];
if (change.endStep >= minStep) {
if (!seenFirstVisible) {
_currentCameraMovementIndex = i; //remember the index for the next frame, so we dont need to loop through everything every time
seenFirstVisible = true;
}
if (change.strumLineID == strumLineID) {
setupHighlight(change.step, change.endStep); super.draw();
setupLine(change.step); super.draw();
setupLine(change.endStep); super.draw();
}
}
if (change.endStep > maxStep) break;
}
}
private inline function setupHighlight(step:Float, endStep:Float) {
y = step * 40;
alpha = 0.15;
scale.y = (endStep -step) * 40;
updateHitbox();
}
private inline function setupLine(step:Float) {
y = (step * 40)-1;
alpha = 0.8;
scale.y = 2;
updateHitbox();
}
}
class EventBackdrop extends FlxBackdrop { class EventBackdrop extends FlxBackdrop {
public var eventBeatSeparator:CharterEventGridSeperator; public var eventBeatSeparator:CharterEventGridSeperator;
@@ -1,6 +1,5 @@
package funkin.editors.charter; package funkin.editors.charter;
import funkin.game.HealthIcon;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import funkin.backend.chart.ChartData.ChartMetaData; import funkin.backend.chart.ChartData.ChartMetaData;
import funkin.editors.extra.PropertyButton; import funkin.editors.extra.PropertyButton;
@@ -22,7 +21,8 @@ class CharterMetaDataScreen extends UISubstateWindow {
public var displayNameTextBox:UITextBox; public var displayNameTextBox:UITextBox;
public var iconTextBox:UITextBox; public var iconTextBox:UITextBox;
public var iconSprite:HealthIcon; public var iconSprite:HealthIcon;
public var excludedGameModesList:UIAutoCompleteButtonList; public var opponentModeCheckbox:UICheckbox;
public var coopAllowedCheckbox:UICheckbox;
public var needsVoicesCheckbox:UICheckbox; public var needsVoicesCheckbox:UICheckbox;
public var colorWheel:UIColorwheel; public var colorWheel:UIColorwheel;
public var difficultiesTextBox:UITextBox; public var difficultiesTextBox:UITextBox;
@@ -78,29 +78,25 @@ class CharterMetaDataScreen extends UISubstateWindow {
add(displayNameTextBox); add(displayNameTextBox);
addLabelOn(displayNameTextBox, translate("displayName")); addLabelOn(displayNameTextBox, translate("displayName"));
add(iconSprite = new HealthIcon(metadata.icon));
iconSprite.scale.set(0.5, 0.5);
iconSprite.updateHitbox();
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, metadata.icon, 150); iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, metadata.icon, 150);
iconTextBox.onChange = (newIcon:String) -> iconSprite.setIcon(newIcon); iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
add(iconTextBox); add(iconTextBox);
addLabelOn(iconTextBox, translate("icon")); addLabelOn(iconTextBox, translate("icon"));
updateIcon(metadata.icon); updateIcon(metadata.icon);
add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID])); opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translate("opponentMode"), metadata.opponentModeAllowed);
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox'); add(opponentModeCheckbox);
excludedGameModesList.cameraSpacing = 0; addLabelOn(opponentModeCheckbox, translate("modesAllowed"));
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
for (mode in metadata.excludedGameModes) coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translate("coopAllowed"), metadata.coopAllowed);
excludedGameModesList.add(new UIAutoCompleteButtonList.UIAutoCompleteButton(0, 0, excludedGameModesList, excludedGameModesList.suggestItems, mode)); add(coopAllowedCheckbox);
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color); colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color);
add(colorWheel); add(colorWheel);
addLabelOn(colorWheel, translate("color")); addLabelOn(colorWheel, translate("color"));
difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, metadata.difficulties.join(", ")); difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", "));
add(difficultiesTextBox); add(difficultiesTextBox);
addLabelOn(difficultiesTextBox, translate("difficulties")); addLabelOn(difficultiesTextBox, translate("difficulties"));
@@ -115,6 +111,9 @@ class CharterMetaDataScreen extends UISubstateWindow {
add(customPropertiesButtonList); add(customPropertiesButtonList);
addLabelOn(customPropertiesButtonList, translate("customValues")); addLabelOn(customPropertiesButtonList, translate("customValues"));
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
{checkbox.y += 6; checkbox.x += 4;}
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() { saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
saveMeta(); saveMeta();
close(); close();
@@ -162,7 +161,8 @@ class CharterMetaDataScreen extends UISubstateWindow {
meta.displayName = displayNameTextBox.label.text; meta.displayName = displayNameTextBox.label.text;
meta.icon = iconTextBox.label.text; meta.icon = iconTextBox.label.text;
meta.color = colorWheel.curColor; meta.color = colorWheel.curColor;
meta.excludedGameModes = [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()], meta.opponentModeAllowed = opponentModeCheckbox.checked;
meta.coopAllowed = coopAllowedCheckbox.checked;
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()]; meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
meta.customValues = customVals; meta.customValues = customVals;
+3 -2
View File
@@ -161,8 +161,9 @@ class CharterNote extends UISprite implements ICharterSelectable {
} }
if (__passed != (__passed = step < Conductor.curStepFloat + (Options.songOffsetAffectEditors ? (Conductor.songOffset / Conductor.stepCrochet) : 0))) { if (__passed != (__passed = step < Conductor.curStepFloat + (Options.songOffsetAffectEditors ? (Conductor.songOffset / Conductor.stepCrochet) : 0))) {
if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID)) if (__passed && FlxG.sound.music.playing) {
Charter.instance.hitsound.replay(); Charter.instance.playHitsound(strumLineID);
}
} }
if (strumLine != null) { if (strumLine != null) {
@@ -21,7 +21,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
for (i in 0...keyCount){ for (i in 0...keyCount){
var strum = new FlxSprite(); var strum = new FlxSprite();
strum.frames = Paths.getFrames("game/notes/default"); strum.frames = Paths.getFrames("game/notes/default");
strum.setGraphicSize(Std.int((strum.width * 0.7) * scale)); strum.setGraphicSize(Std.int((strum.width * Flags.DEFAULT_NOTE_SCALE) * scale));
strum.updateHitbox(); strum.updateHitbox();
var animPrefix = strumAnimPrefix[i % 4]; var animPrefix = strumAnimPrefix[i % 4];
@@ -33,7 +33,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
note = new FlxSprite(); note = new FlxSprite();
note.frames = Paths.getFrames("game/notes/default"); note.frames = Paths.getFrames("game/notes/default");
note.setGraphicSize(Std.int((note.width * 0.7) * scale)); note.setGraphicSize(Std.int((note.width * Flags.DEFAULT_NOTE_SCALE) * scale));
note.updateHitbox(); note.updateHitbox();
note.animation.addByPrefix('purple', 'purple0'); note.animation.addByPrefix('purple', 'purple0');
note.animation.play('purple'); note.animation.play('purple');
@@ -41,7 +41,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
add(note); add(note);
} }
var noteTime:Float = FlxG.height; var noteTime:Float = FlxG.initialHeight;
var scroll:Float = 1.0; var scroll:Float = 1.0;
public function updatePos(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){ public function updatePos(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){
@@ -53,7 +53,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
strum.x = CoolUtil.fpsLerp(strum.x, x + (Note.swagWidth * scale * spacing * i), 0.2); strum.x = CoolUtil.fpsLerp(strum.x, x + (Note.swagWidth * scale * spacing * i), 0.2);
strum.y = CoolUtil.fpsLerp(strum.y, y + (Note.swagWidth*0.5) - (Note.swagWidth * scale * 0.5), 0.2); strum.y = CoolUtil.fpsLerp(strum.y, y + (Note.swagWidth*0.5) - (Note.swagWidth * scale * 0.5), 0.2);
strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, 0.7 * scale, 0.2); strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, Flags.DEFAULT_NOTE_SCALE * scale, 0.2);
strum.updateHitbox(); strum.updateHitbox();
} }
@@ -61,7 +61,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
scroll = CoolUtil.fpsLerp(scroll, scrollSpeed, 0.2); scroll = CoolUtil.fpsLerp(scroll, scrollSpeed, 0.2);
noteTime -= FlxG.elapsed * scroll * 1000 * 0.45; noteTime -= FlxG.elapsed * scroll * 1000 * 0.45;
if (noteTime <= 0.0) if (noteTime <= 0.0)
noteTime = FlxG.height; noteTime = FlxG.initialHeight;
note.x = members[0].x; note.x = members[0].x;
note.y = members[0].y + noteTime; note.y = members[0].y + noteTime;
@@ -146,11 +146,10 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
var screen = parent.tree.last(); var screen = parent.tree.last();
var idx = 0; var idx = 0;
while (!(screen.members[idx] is Separator)) idx++; while (!(screen.members[idx] is Separator || idx >= screen.members.length)) idx++;
screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name)); screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name));
// Add to Meta // Add to Meta
var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant == "" ? "-" + curSong.variant : ""}.json'; var metaPath = '$songFolder/meta${curSong.variant != null && curSong.variant != "" ? "-" + curSong.variant : ""}.json';
CoolUtil.safeSaveFile(metaPath, Chart.makeMetaSaveable(curSong)); CoolUtil.safeSaveFile(metaPath, Chart.makeMetaSaveable(curSong));
} }
#end #end
@@ -105,6 +105,7 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> {
draggingObj = null; draggingObj = null;
fixEvents(); fixEvents();
refreshStrumlineIDs(); refreshStrumlineIDs();
Charter.instance.updateCameraChanges();
} }
public inline function fixEvents() { public inline function fixEvents() {
@@ -1,5 +1,7 @@
package funkin.editors.charter; package funkin.editors.charter;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import flixel.util.FlxColor;
import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup;
import flixel.sound.FlxSound; import flixel.sound.FlxSound;
import funkin.backend.chart.ChartData.ChartStrumLine; import funkin.backend.chart.ChartData.ChartStrumLine;
@@ -10,11 +12,11 @@ import funkin.game.HealthIcon;
class CharterStrumline extends UISprite { class CharterStrumline extends UISprite {
public var strumLine:ChartStrumLine; public var strumLine:ChartStrumLine;
public var hitsounds:Bool = true;
public var draggingSprite:UISprite; public var draggingSprite:UISprite;
public var healthIcons:FlxSpriteGroup; public var healthIcons:FlxSpriteGroup;
public var button:CharterStrumlineOptions; public var button:CharterStrumlineOptions;
public var highlightColor:FlxColor;
public var draggable:Bool = false; public var draggable:Bool = false;
public var dragging:Bool = false; public var dragging:Bool = false;
@@ -22,7 +24,9 @@ class CharterStrumline extends UISprite {
public var curMenu:UIContextMenu = null; public var curMenu:UIContextMenu = null;
public var vocals:FlxSound; public var vocals:FlxSound;
public var voicesMuted:Bool = false; public var hasVocals:Bool = false;
public var vocalsVolume:Float = 1;
public var hitsoundVolume:Float = 1;
public var keyCount:Int = 4; public var keyCount:Int = 4;
public var startingID(get, null):Int; public var startingID(get, null):Int;
@@ -142,15 +146,22 @@ class CharterStrumline extends UISprite {
if (asset != null) { if (asset != null) {
vocals.reset(); vocals.reset();
vocals.loadEmbedded(asset); vocals.loadEmbedded(asset);
hasVocals = true;
} }
else { else {
vocals.destroy(); vocals.destroy();
hasVocals = false;
} }
vocals.group = FlxG.sound.defaultMusicGroup; vocals.group = FlxG.sound.defaultMusicGroup;
}
public function updateVoicesVolume() { highlightColor = 0xFFFFFFFF;
vocals.volume = (voicesMuted || (Charter.instance?.voicesMuted ?? false)) ? 0 : 1; if (icons[0] != null) {
var characterXML = Character.getXMLFromCharName(icons[0]);
if (characterXML != null && characterXML.x.exists("color")) highlightColor = FlxColor.fromString(characterXML.x.get("color"));
//make darker colors more visible for the highlight
highlightColor.brightness = Math.max(highlightColor.brightness, 0.65);
}
} }
} }
@@ -173,18 +184,17 @@ class CharterStrumlineOptions extends UITopMenuButton {
contextMenu = [ contextMenu = [
{ {
label: TU.translate("charter.strumLine.hitsounds"), label: TU.translate("charter.strumLine.hitsounds"),
onSelect: function(_) { slider: {
strLine.hitsounds = !strLine.hitsounds; min: 0,
max: 1,
value: strLine.hitsoundVolume,
onChange: function(t) {
strLine.hitsoundVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
}, },
icon: strLine.hitsounds ? 1 : 0 onIconClick: Charter.instance._slider_mutetoggle,
}, icon: 7
{
label: TU.translate("charter.strumLine.muteVocals"),
onSelect: function(_) {
strLine.voicesMuted = !strLine.voicesMuted;
strLine.updateVoicesVolume();
},
icon: strLine.voicesMuted ? 1 : 0
}, },
null, null,
{ {
@@ -205,20 +215,44 @@ class CharterStrumlineOptions extends UITopMenuButton {
} }
]; ];
contextMenu.insert(0, { if (strLine.hasVocals) {
label: TU.translate("charter.strumLine.noWaveform"), contextMenu.insert(1, {
onSelect: function(_) {strLine.selectedWaveform = -1;}, label: TU.translate("charter.strumLine.vocals"),
icon: strLine.selectedWaveform == -1 ? 1 : 0 slider: {
}); min: 0,
max: 1,
value: strLine.vocalsVolume,
onChange: function(t) {
strLine.vocalsVolume = t.slider.value;
strLine.vocals.volume = Charter.instance.vocals.volume * strLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
},
onIconClick: Charter.instance._slider_mutetoggle,
icon: 7
});
}
var waveformOptions:Array<UIContextMenuOption> = [
{
label: TU.translate("charter.strumLine.noWaveform"),
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
}
];
for (i => name in Charter.waveformHandler.waveformList) for (i => name in Charter.waveformHandler.waveformList)
contextMenu.insert(1+i, { waveformOptions.push({
label: name, label: name,
onSelect: function(_) {strLine.selectedWaveform = i;}, onSelect: function(_) {strLine.selectedWaveform = i;},
icon: strLine.selectedWaveform == i ? 6 : 5 icon: strLine.selectedWaveform == i ? 6 : 5
}); });
contextMenu.insert(1+Charter.waveformHandler.waveformList.length, null); contextMenu.insert(0, {
label: TU.translate("charter.strumLine.waveforms"),
childs: waveformOptions
});
contextMenu.insert(1, null);
var cam = Charter.instance.charterCamera; var cam = Charter.instance.charterCamera;
var point = CoolUtil.worldToScreenPosition(this, cam); var point = CoolUtil.worldToScreenPosition(this, cam);
@@ -33,6 +33,8 @@ class CharterStrumlineScreen extends UISubstateWindow {
public var strumLineCam:HudCamera; public var strumLineCam:HudCamera;
public var previewStrumLine:CharterPreviewStrumLine; public var previewStrumLine:CharterPreviewStrumLine;
public var previewBorder:Array<FlxSprite> = [];
private final borderGap:Int = 5;
private var onSave:ChartStrumLine -> Void = null; private var onSave:ChartStrumLine -> Void = null;
@@ -176,20 +178,30 @@ class CharterStrumlineScreen extends UISubstateWindow {
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix")); addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84); keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84);
// if (Flags.CHARTER_ADVANCED_SETTINGS) { add(keyCountStepper);
add(keyCountStepper); addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
// }
strumLineCam = new HudCamera(); strumLineCam = new HudCamera();
strumLineCam.downscroll = Options.downscroll; strumLineCam.downscroll = Options.downscroll;
strumLineCam.bgColor = 0; strumLineCam.bgColor = 0;
strumLineCam.alpha = 0; strumLineCam.alpha = 0;
FlxG.cameras.add(strumLineCam, false); FlxG.cameras.add(strumLineCam, false);
updateStrumlineCam(FlxG.width, FlxG.height);
previewStrumLine = new CharterPreviewStrumLine(0, 0, 0, 1, 4, 0); previewStrumLine = new CharterPreviewStrumLine(0, 0, 0, 1, 4, 0);
previewStrumLine.camera = strumLineCam; previewStrumLine.camera = strumLineCam;
add(previewStrumLine); add(previewStrumLine);
FlxTween.tween(strumLineCam, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut}); FlxTween.tween(strumLineCam, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut});
//preview border
previewBorder.push(new FlxSprite(-borderGap, -borderGap).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(FlxG.initialWidth, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, FlxG.initialHeight).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
for (border in previewBorder) {
border.camera = strumLineCam;
add(border);
}
} }
function saveStrumline() { function saveStrumline() {
@@ -222,7 +234,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
previewStrumLine.visible = visibleCheckbox.checked; previewStrumLine.visible = visibleCheckbox.checked;
var xOffset:Float = StrumLine.calculateStartingXPos(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value)); var xOffset:Float = StrumLine.calculateStartingXPosFromInitialWidth(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value));
previewStrumLine.updatePos(xOffset, hudYStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value), scrollSpeed); previewStrumLine.updatePos(xOffset, hudYStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value), scrollSpeed);
super.update(elapsed); super.update(elapsed);
@@ -233,6 +245,26 @@ class CharterStrumlineScreen extends UISubstateWindow {
FlxTween.cancelTweensOf(strumLineCam); FlxTween.cancelTweensOf(strumLineCam);
FlxG.cameras.remove(strumLineCam); FlxG.cameras.remove(strumLineCam);
} }
public override function onResize(width:Int, height:Int) {
super.onResize(width, height);
if (!UIState.resolutionAware) return;
if ((width < FlxG.initialWidth || height < FlxG.initialHeight) && !Options.bypassEditorsResize) {
width = FlxG.initialWidth; height = FlxG.initialHeight;
}
updateStrumlineCam(width, height);
}
private function updateStrumlineCam(windowWidth:Int, windowHeight:Int) {
strumLineCam.width = FlxG.initialWidth + (borderGap*2);
strumLineCam.height = FlxG.initialHeight + (borderGap*2);
strumLineCam.x = (windowWidth/2) - (strumLineCam.width/2);
strumLineCam.y = (windowHeight/2) - (strumLineCam.height/2);
strumLineCam.scroll.x = -borderGap;
strumLineCam.scroll.y = -borderGap;
}
} }
class CharacterButton extends UIButton { class CharacterButton extends UIButton {
@@ -1,6 +1,5 @@
package funkin.editors.charter; package funkin.editors.charter;
import funkin.game.HealthIcon;
import flixel.group.FlxGroup; import flixel.group.FlxGroup;
import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormat;
import flixel.text.FlxText.FlxTextFormatMarkerPair; import flixel.text.FlxText.FlxTextFormatMarkerPair;
@@ -38,7 +37,8 @@ class SongCreationScreen extends UISubstateWindow {
public var displayNameTextBox:UITextBox; public var displayNameTextBox:UITextBox;
public var iconTextBox:UITextBox; public var iconTextBox:UITextBox;
public var iconSprite:HealthIcon; public var iconSprite:HealthIcon;
public var excludedGameModesList:UIAutoCompleteButtonList; public var opponentModeCheckbox:UICheckbox;
public var coopAllowedCheckbox:UICheckbox;
public var colorWheel:UIColorwheel; public var colorWheel:UIColorwheel;
public var difficultiesTextBox:UITextBox; public var difficultiesTextBox:UITextBox;
@@ -166,19 +166,24 @@ class SongCreationScreen extends UISubstateWindow {
updateIcon("face"); updateIcon("face");
menuDataGroup.add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID])); opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translateMeta("opponentMode"), true);
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox'); menuDataGroup.add(opponentModeCheckbox);
excludedGameModesList.cameraSpacing = 0; addLabelOn(opponentModeCheckbox, translateMeta("modesAllowed"));
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
colorWheel = new UIColorwheel(iconTextBox.x, excludedGameModesList.y, 0xFFFFFF); coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translateMeta("coopAllowed"), true);
menuDataGroup.add(coopAllowedCheckbox);
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, 0xFFFFFF);
menuDataGroup.add(colorWheel); menuDataGroup.add(colorWheel);
addLabelOn(colorWheel, translateMeta("color")); addLabelOn(colorWheel, translateMeta("color"));
difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, ""); difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, "");
menuDataGroup.add(difficultiesTextBox); menuDataGroup.add(difficultiesTextBox);
addLabelOn(difficultiesTextBox, translateMeta("difficulties")); addLabelOn(difficultiesTextBox, translateMeta("difficulties"));
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
{checkbox.y += 6; checkbox.x += 4;}
var menuTitle:UIText; var menuTitle:UIText;
selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importSource"), 28)); selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importSource"), 28));
@@ -428,7 +433,8 @@ class SongCreationScreen extends UISubstateWindow {
displayName: displayNameTextBox.label.text, displayName: displayNameTextBox.label.text,
icon: iconTextBox.label.text, icon: iconTextBox.label.text,
color: colorWheel.curColor, color: colorWheel.curColor,
excludedGameModes: [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()], opponentModeAllowed: opponentModeCheckbox.checked,
coopAllowed: coopAllowedCheckbox.checked,
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) if (diff.length > 0) diff.trim()] difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) if (diff.length > 0) diff.trim()]
}); });
+6 -10
View File
@@ -377,9 +377,7 @@ class StageEditor extends UIState {
// Play first anim, and make it the last frame // Play first anim, and make it the last frame
var animToPlay = char.getAnimOrder()[0]; var animToPlay = char.getAnimOrder()[0];
char.playAnim(animToPlay, true, NONE); char.playAnim(animToPlay, true, NONE);
var lastIndx = (char.animateAtlas != null) ? var lastIndx = char.animation.curAnim.numFrames - 1;
char.animateAtlas.anim.length - 1 :
char.animation.curAnim.numFrames - 1;
char.playAnim(animToPlay, true, NONE, false, lastIndx); char.playAnim(animToPlay, true, NONE, false, lastIndx);
char.stopAnimation(); char.stopAnimation();
@@ -510,7 +508,7 @@ class StageEditor extends UIState {
function _file_save(_) { function _file_save(_) {
#if sys #if sys
FlxG.sound.play(Paths.sound('editors/save')); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
CoolUtil.safeSaveFile( CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/stages/${__stage}.xml', '${Paths.getAssetsRoot()}/data/stages/${__stage}.xml',
buildStage() buildStage()
@@ -522,7 +520,7 @@ class StageEditor extends UIState {
} }
function _file_saveas(_) { function _file_saveas(_) {
FlxG.sound.play(Paths.sound('editors/save')); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
openSubState(new SaveSubstate(buildStage(), { openSubState(new SaveSubstate(buildStage(), {
defaultSaveFile: '${__stage}.xml' defaultSaveFile: '${__stage}.xml'
})); }));
@@ -565,9 +563,7 @@ class StageEditor extends UIState {
// Play first anim, and make it the last frame // Play first anim, and make it the last frame
var animToPlay = char.getAnimOrder()[0]; var animToPlay = char.getAnimOrder()[0];
char.playAnim(animToPlay, true, NONE); char.playAnim(animToPlay, true, NONE);
var lastIndx = (char.animateAtlas != null) ? var lastIndx = char.animation.curAnim.numFrames - 1;
char.animateAtlas.anim.length - 1 :
char.animation.curAnim.numFrames - 1;
char.playAnim(animToPlay, true, NONE, false, lastIndx); char.playAnim(animToPlay, true, NONE, false, lastIndx);
char.stopAnimation(); char.stopAnimation();
@@ -734,7 +730,7 @@ class StageEditor extends UIState {
} }
function _edit_undo(_) { function _edit_undo(_) {
FlxG.sound.play(Flags.DEFAULT_EDITOR_UNDO_SOUND); UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
var undo = undos.undo(); var undo = undos.undo();
switch(undo) { switch(undo) {
case null: case null:
@@ -760,7 +756,7 @@ class StageEditor extends UIState {
} }
function _edit_redo(_) { function _edit_redo(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
var redo = undos.redo(); var redo = undos.redo();
switch(redo) { switch(redo) {
case null: case null:
@@ -1,50 +0,0 @@
package funkin.editors.ui;
typedef UIAutoCompleteButtonList = UITypedAutoCompleteButtonList<UIAutoCompleteButton>;
class UITypedAutoCompleteButtonList<T:UIAutoCompleteButton> extends UIButtonList<T> {
public var suggestItems(default, set):Array<String> = [];
private function set_suggestItems(value:Array<String>) {
buttons.forEachAlive((button) -> button.textBox.suggestItems = value);
return suggestItems = value;
}
public function new(x:Float, y:Float, width:Int, height:Int, windowName:String, ?suggestItems:Array<String>, ?buttonSize:FlxPoint, ?buttonOffset:FlxPoint, buttonSpacing:Float = 0) {
super(x, y, width, height, windowName, buttonSize == null ? FlxPoint.get(width, width * 0.15) : buttonSize, buttonOffset, buttonSpacing);
if (suggestItems != null && suggestItems.length > 0) this.suggestItems = suggestItems;
addButton.callback = () -> add(cast new UIAutoCompleteButton(0, 0, cast this, this.suggestItems));
}
}
class UIAutoCompleteButton extends UIButton {
public var textBox:UIAutoCompleteTextBox;
public var deleteButton:UIButton;
public var deleteIcon:FlxSprite;
public function new (x:Float, y:Float, parent:UIAutoCompleteButtonList, ?suggestItems:Array<String>, text:String = "", ?w:Int, ?h:Int, multiline:Bool = false) {
super(x, y, "", null, w == null ? Std.int(parent.buttonSize.x) : w, h == null ? Std.int(parent.buttonSize.y) : h);
members.push(textBox = new UIAutoCompleteTextBox(16, bHeight/2 - (32/2), text, Std.int(bWidth - 32 - 36), 32, multiline));
if (suggestItems != null && suggestItems.length > 0) textBox.suggestItems = suggestItems;
textBox.antialiasing = false;
members.push(deleteButton = new UIButton(textBox.x + textBox.bWidth + 10, textBox.y, "", () -> { parent.remove(this); destroy(); }, 32));
deleteButton.color = 0xFFFF0000;
deleteButton.autoAlpha = false;
members.push(deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')));
deleteIcon.antialiasing = false;
}
override function update(elapsed) {
deleteButton.y = y + bHeight / 2 - deleteButton.bHeight / 2;
textBox.y = y + bHeight/2 - 16;
deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8;
textBox.selectable = deleteButton.selectable = selectable;
deleteButton.shouldPress = shouldPress;
super.update(elapsed);
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ class UIButton extends UISliceSprite {
super.onHovered(); super.onHovered();
if (FlxG.mouse.justPressed) { if (FlxG.mouse.justPressed) {
hasBeenPressed = true; hasBeenPressed = true;
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND);
} }
if (FlxG.mouse.justReleased && callback != null && shouldPress && hasBeenPressed) { if (FlxG.mouse.justReleased && callback != null && shouldPress && hasBeenPressed) {
callback(); callback();
+225 -31
View File
@@ -16,6 +16,12 @@ class UIContextMenu extends MusicBeatSubstate {
public var contextMenuOptions:Array<UIContextMenuOptionSpr> = []; public var contextMenuOptions:Array<UIContextMenuOptionSpr> = [];
public var separators:Array<FlxSprite> = []; public var separators:Array<FlxSprite> = [];
public var childContextMenu:UIContextMenu = null;
public var parentContextMenu:UIContextMenu = null;
private var childContextMenuOptionIndex:Int = -1;
@:allow(funkin.editors.ui.UIContextMenu)
private var lastHoveredOptionIndex:Int = -1;
var scroll:Float = 0.0; var scroll:Float = 0.0;
var flipped:Bool = false; var flipped:Bool = false;
@@ -96,6 +102,10 @@ class UIContextMenu extends MusicBeatSubstate {
for(o in separators) for(o in separators)
o.x -= bg.bWidth; o.x -= bg.bWidth;
} }
for(o in contextMenuOptions) {
o.postCreate();
}
} }
public function select(option:UIContextMenuOption) { public function select(option:UIContextMenuOption) {
@@ -105,12 +115,12 @@ class UIContextMenu extends MusicBeatSubstate {
if (callback != null) if (callback != null)
callback(this, index, option); callback(this, index, option);
if (option.closeOnSelect == null ? true : option.closeOnSelect) if (option.closeOnSelect == null ? true : option.closeOnSelect)
close(); closeWithParents();
} }
public override function update(elapsed:Float) { public override function update(elapsed:Float) {
if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild) if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild && !hoveringAnyChildren())
close(); closeWithParents();
__oobDeletion = true; __oobDeletion = true;
@@ -121,6 +131,14 @@ class UIContextMenu extends MusicBeatSubstate {
contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, scroll, 0.5); contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, scroll, 0.5);
contextCam.alpha = CoolUtil.fpsLerp(contextCam.alpha, 1, 0.25); contextCam.alpha = CoolUtil.fpsLerp(contextCam.alpha, 1, 0.25);
if (parentContextMenu != null) {
if (hoveringAnyParents() && parentContextMenu.lastHoveredOptionIndex != parentContextMenu.childContextMenuOptionIndex) {
closeWithChildren();
parentContextMenu.childContextMenuOptionIndex = -1;
}
}
} }
public override function destroy() { public override function destroy() {
@@ -129,6 +147,56 @@ class UIContextMenu extends MusicBeatSubstate {
if (UIState.state.curContextMenu == this) if (UIState.state.curContextMenu == this)
UIState.state.curContextMenu = null; UIState.state.curContextMenu = null;
} }
public function openChildContextMenu(optionSpr:UIContextMenuOptionSpr) {
var index = contextMenuOptions.indexOf(optionSpr);
if (index != childContextMenuOptionIndex) {
childContextMenuOptionIndex = index;
var child = new UIContextMenu(optionSpr.option.childs, null, optionSpr.x + optionSpr.bWidth + 4, optionSpr.y - 4);
persistentDraw = true;
persistentUpdate = true;
child.parentContextMenu = this;
childContextMenu = child;
openSubState(child);
}
}
public function closeWithParents() {
close();
if (parentContextMenu != null) {
parentContextMenu.closeWithParents();
}
}
public function closeWithChildren() {
if (childContextMenu != null) {
childContextMenu.closeWithChildren();
}
close();
}
public function hoveringAnyParents() {
if (parentContextMenu != null) {
return parentContextMenu.bg.hoveredByChild || parentContextMenu.hoveringAnyParents();
}
return false;
}
public function hoveringAnyChildren() {
if (childContextMenu != null) {
return childContextMenu.bg.hoveredByChild || childContextMenu.hoveringAnyChildren();
}
return false;
}
}
typedef UIContextMenuSliderOptionData = {
var min:Float;
var max:Float;
var value:Float;
var ?onChange:UIContextMenuOption->Void;
//default = 120, ignored if sameLine = false
var ?width:Float;
//disables stepper and text if false, default = false
var ?showValues:Bool;
//if true, the slider will be on the same line as the label text, otherwise it will be on the next line below the label
var ?sameLine:Bool;
} }
typedef UIContextMenuCallback = UIContextMenu->Int->UIContextMenuOption->Void; typedef UIContextMenuCallback = UIContextMenu->Int->UIContextMenuOption->Void;
@@ -144,13 +212,24 @@ typedef UIContextMenuOption = {
var ?button:UIContextMenuOptionSpr; var ?button:UIContextMenuOptionSpr;
var ?onCreate:UIContextMenuOptionSpr->Void; var ?onCreate:UIContextMenuOptionSpr->Void;
var ?childs:Array<UIContextMenuOption>; var ?childs:Array<UIContextMenuOption>;
var ?slider:UIContextMenuSliderOptionData;
var ?onIconClick:UIContextMenuOption->Void;
}
enum abstract UIContextMenuOptionType(Int) from Int {
var DEFAULT = 0;
var SUBMENU = 1;
var SLIDER = 2;
} }
class UIContextMenuOptionSpr extends UISliceSprite { class UIContextMenuOptionSpr extends UISliceSprite {
public var label:UIText; public var label:UIText;
public var labelKeybind:UIText; public var labelKeybind:UIText;
public var icon:FlxSprite; public var icon:UIContextMenuOptionIcon;
public var option:UIContextMenuOption; public var option:UIContextMenuOption;
public var optionType:UIContextMenuOptionType = DEFAULT;
public var slider:UISlider = null;
var parent:UIContextMenu; var parent:UIContextMenu;
@@ -160,42 +239,98 @@ class UIContextMenuOptionSpr extends UISliceSprite {
this.parent = parent; this.parent = parent;
this.color = option.color; this.color = option.color;
if (option.icon != null && option.icon > 0) { var w:Int = label.frameWidth + 22;
icon = new FlxSprite(0, 0).loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20); var h:Int = label.frameHeight;
icon.animation.add('icon', [option.icon-1], 0, true);
icon.animation.play('icon');
}
if (option.keybinds == null) { if (option.childs != null) optionType = SUBMENU;
if (option.keybind != null) { if (option.slider != null) optionType = SLIDER;
option.keybinds = [option.keybind];
}
}
if (option.keybinds != null || option.keybindText != null) { switch(optionType) {
var text = if(option.keybindText == null) {
var textKeys:Array<String> = []; case SUBMENU:
for (o in option.keybinds[0]) { labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, ">");
if (Std.int(o) > 0) { case SLIDER:
textKeys.push(o.toUIString()); labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, "");
//slider needs to be created after so that it can match the menu width (when not on the same line)
if (option.slider.sameLine != null && option.slider.sameLine) {
var sliderWidth = option.slider.width != null ? Std.int(option.slider.width) : 120;
w += 120 + slider.barWidth;
} else {
h *= 2;
}
default:
if (option.keybinds == null) {
if (option.keybind != null) {
option.keybinds = [option.keybind];
} }
} }
textKeys.join("+");
} else { if (option.keybinds != null || option.keybindText != null) {
option.keybindText; var text = if(option.keybindText == null) {
} var textKeys:Array<String> = [];
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text); for (o in option.keybinds[0]) {
labelKeybind.alpha = 0.75; if (Std.int(o) > 0) {
textKeys.push(o.toUIString());
}
}
textKeys.join("+");
} else {
option.keybindText;
}
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text);
labelKeybind.alpha = 0.75;
w = Std.int(labelKeybind.x + labelKeybind.frameWidth + 10);
}
} }
super(x, y, labelKeybind != null ? Std.int(labelKeybind.x + labelKeybind.frameWidth + 10) : (label.frameWidth + 22), label.frameHeight, 'editors/ui/menu-item'); super(x, y, w, h, 'editors/ui/menu-item');
members.push(label); members.push(label);
if (icon != null) updateIcon();
members.push(icon);
if (labelKeybind != null) if (labelKeybind != null)
members.push(labelKeybind); members.push(labelKeybind);
} }
//Called after all options are created and the context menu width/height is final
public function postCreate() {
switch(optionType) {
case SLIDER:
var sliderWidth = bWidth-50;
if (option.slider.sameLine != null && option.slider.sameLine) {
option.slider.width != null ? Std.int(option.slider.width) : 120;
}
slider = new UISlider(0, 0, sliderWidth, option.slider.value,
[{start: option.slider.min, end: option.slider.max, size: option.slider.max-option.slider.min}], false);
slider.onChange = function(v) {
option.slider.value = v;
if (option.slider.onChange != null) option.slider.onChange(option);
updateIcon(); //check if icon has changed
@:privateAccess
labelKeybind.text = '${CoolUtil.quantize(slider.__barProgress * 100, 1)}%';
};
slider.value = option.slider.value;
if (option.slider.showValues == null || !option.slider.showValues) {
slider.startText.visible = false;
slider.endText.visible = false;
slider.valueStepper.visible = false;
slider.valueStepper.selectable = false;
}
members.push(slider);
case SUBMENU:
default:
}
}
public override function draw() { public override function draw() {
alpha = option.color == null ? (hovered ? 1 : 0) : 1; alpha = option.color == null ? (hovered ? 1 : 0) : 1;
if (option.color != null) color = hovered ? option.color.getLightened(.4) : option.color; if (option.color != null) color = hovered ? option.color.getLightened(.4) : option.color;
@@ -205,12 +340,71 @@ class UIContextMenuOptionSpr extends UISliceSprite {
icon.follow(this, 0, 0); icon.follow(this, 0, 0);
if (labelKeybind != null) if (labelKeybind != null)
labelKeybind.follow(this, bWidth - 10 - labelKeybind.frameWidth, 2); labelKeybind.follow(this, bWidth - 10 - labelKeybind.frameWidth, 2);
if (slider != null) {
if (option.slider.sameLine != null && option.slider.sameLine) {
slider.follow(this, bWidth - 18 - slider.barWidth - (slider.endText.visible ? slider.endText.width : 0), 5);
} else {
slider.follow(this, 20, 5 + label.frameHeight);
}
}
super.draw(); super.draw();
} }
public override function onHovered() { public override function onHovered() {
super.onHovered(); super.onHovered();
if (FlxG.mouse.justReleased)
parent.select(option); parent.lastHoveredOptionIndex = parent.contextMenuOptions.indexOf(this);
switch(optionType) {
case SUBMENU:
parent.openChildContextMenu(this);
case SLIDER:
default:
if (FlxG.mouse.justReleased)
parent.select(option);
}
}
public function updateIcon() {
var currentIcon = option.icon != null ? option.icon : 0;
if (icon == null && currentIcon > 0) {
members.push(icon = new UIContextMenuOptionIcon(option));
}
if (icon != null) {
icon.updateIconState(currentIcon);
}
}
}
class UIContextMenuOptionIcon extends UISprite {
private var option:UIContextMenuOption;
private var _lastState:Int = 0;
override public function new(option:UIContextMenuOption) {
super();
this.option = option;
loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
selectable = option.onIconClick != null;
cursor = option.onIconClick != null ? CLICK : ARROW;
}
public function updateIconState(state:Int) {
if (_lastState == state) return;
_lastState = state;
visible = state > 0;
if (state > 0) {
animation.add('icon', [state-1], 0, true);
animation.play('icon');
}
}
public override function onHovered() {
super.onHovered();
if (FlxG.mouse.justReleased && option.onIconClick != null) {
option.onIconClick(option);
}
} }
} }
+1 -1
View File
@@ -87,7 +87,7 @@ class UIDropDown extends UISliceSprite {
} }
public function openContextMenu() { public function openContextMenu() {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND)); UIState.playEditorSound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND);
var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]); var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]);
curMenu = UIState.state.openContextMenu([ curMenu = UIState.state.openContextMenu([
for(k=>o in items) { for(k=>o in items) {

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