Author SHA1 Message Date
⍚~NexandFuro 0d9360f0ff Bring dev/scripted-game-modes 2026-01-21 18:17:48 +01:00
⍚~Nex 1cfe6ca468 forgor 2026-01-21 17:16:31 +01:00
⍚~Nex 3bbeafcc3e restarting again
i hate git
2026-01-21 17:09:15 +01:00
72 changed files with 147127 additions and 11225 deletions
+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 (Stage coming soon) - Editors for Charts and Characters
- 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 (Character editor rework soon) - Mature Chart editor
- 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,6 +2,13 @@
<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" color="#A5004D"> <character x="100" y="275" sprite="gf" icon="gf" color="#A5004D">
<anim name="cheer" anim="GF Cheer"/> <anim name="cheer" anim="GF Cheer" x="0" y="0" fps="24" loop="false"/>
<anim name="singUP" anim="GF Up Note"/> <anim name="singUP" anim="GF Up Note" x="0" y="0" fps="24" loop="false"/>
<anim name="singLEFT" anim="GF left note"/> <anim name="singLEFT" anim="GF left note" x="0" y="0" fps="24" loop="false"/>
<anim name="singRIGHT" anim="GF Right Note"/> <anim name="singRIGHT" anim="GF Right Note" x="0" y="0" fps="24" loop="false"/>
<anim name="singDOWN" anim="GF Down Note"/> <anim name="singDOWN" anim="GF Down Note" x="0" y="0" fps="24" loop="false"/>
<anim name="danceLeft" anim="GF Dancing Beat" indices="0..14"/> <anim name="danceLeft" anim="GF Dancing Beat" x="0" y="0" fps="24" loop="false" indices="0..14"/>
<anim name="danceRight" anim="GF Dancing Beat" indices="15..30"/> <anim name="danceRight" anim="GF Dancing Beat" x="0" y="0" fps="24" loop="false" indices="15..30"/>
<anim name="hairBlow" anim="GF Dancing Beat Hair blowing" loop="true" indices="0..3"/> <anim name="hairBlow" anim="GF Dancing Beat Hair blowing" x="0" y="0" fps="24" loop="true" indices="0,1,2,3"/>
<anim name="hairFall" anim="GF Dancing Beat Hair Landing" indices="0..11"/> <anim name="hairFall" anim="GF Dancing Beat Hair Landing" x="0" y="0" fps="24" loop="false" indices="0..11"/>
<anim name="scared" anim="GF FEAR " loop="true"/> <anim name="scared" anim="GF FEAR" x="0" y="0" fps="24" loop="true"/>
<anim name="sad" anim="gf sad" x="-51" y="315" loop="true" indices="0..12"/> <anim name="sad" anim="gf sad" x="0" y="0" fps="24" loop="true" indices="0..12"/>
</character> </character>
+3
View File
@@ -1,3 +1,6 @@
# THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT AND WHITELIST FREEPLAY SONGS
test test
tutorial tutorial
+8
View File
@@ -0,0 +1,8 @@
# THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT AND WHITELIST GAME MODES
solo
opponent
coop
coop-switched
botplay
@@ -1,5 +1,5 @@
# THIS FILE ISN'T NECESSARY, # THIS FILE ISN'T NECESSARY,
# BUT YOU CAN USE IT TO SORT WEEKS # BUT YOU CAN USE IT TO SORT AND WHITELIST WEEKS
tutorial tutorial
week1 week1
+4
View File
@@ -0,0 +1,4 @@
{
"displayName": "Botplay Mode",
"modeID": "codename.botplay"
}
+2
View File
@@ -0,0 +1,2 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Botplay Mode" id="codename.botplay"/>
+5
View File
@@ -0,0 +1,5 @@
{
"displayName": "Co-Op Mode (Switched)",
"modeID": "codename.coop-opponent",
"scripts": ["opponent", "coop"]
}
+11
View File
@@ -0,0 +1,11 @@
<!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
@@ -0,0 +1,4 @@
{
"displayName": "Co-Op Mode",
"modeID": "codename.coop"
}
+2
View File
@@ -0,0 +1,2 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Co-Op Mode" id="codename.coop"/>
+5
View File
@@ -0,0 +1,5 @@
solo
opponent
coop
coop-switched
botplay
+4
View File
@@ -0,0 +1,4 @@
{
"displayName": "Opponent Mode",
"modeID": "codename.opponent"
}
+2
View File
@@ -0,0 +1,2 @@
<!DOCTYPE codename-engine-game-mode>
<mode name="Opponent Mode" id="codename.opponent"/>
+4
View File
@@ -0,0 +1,4 @@
{
"displayName": "Solo Mode",
"modeID": "codename.solo"
}
+2
View File
@@ -0,0 +1,2 @@
<!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="440" y="110"/> <girlfriend x="540" y="110"/>
<dad/> <dad/>
<boyfriend x="1030" camyoffset="-150" camxoffset="-150"/> <boyfriend x="1030" camyoffset="-150" camxoffset="-150"/>
File diff suppressed because it is too large Load Diff
+26 -26
View File
@@ -1,28 +1,28 @@
{"ATLAS":{"SPRITES":[ {"ATLAS": {"SPRITES":[
{"SPRITE":{"name":"0","x":1662,"y":282,"w":194,"h":117,"rotated":false}}, {"SPRITE" : {"name": "0000","x":1636,"y":275,"w":183,"h":115,"rotated": false}},
{"SPRITE":{"name":"1","x":1790,"y":417,"w":92,"h":136,"rotated":false}}, {"SPRITE" : {"name": "0001","x":215,"y":360,"w":84,"h":140,"rotated": false}},
{"SPRITE":{"name":"2","x":1193,"y":539,"w":259,"h":123,"rotated":false}}, {"SPRITE" : {"name": "0002","x":419,"y":358,"w":251,"h":121,"rotated": false}},
{"SPRITE":{"name":"3","x":483,"y":553,"w":125,"h":68,"rotated":false}}, {"SPRITE" : {"name": "0003","x":1636,"y":394,"w":120,"h":65,"rotated": false}},
{"SPRITE":{"name":"4","x":1662,"y":417,"w":110,"h":127,"rotated":false}}, {"SPRITE" : {"name": "0004","x":4,"y":360,"w":108,"h":126,"rotated": false}},
{"SPRITE":{"name":"5","x":1886,"y":0,"w":105,"h":149,"rotated":false}}, {"SPRITE" : {"name": "0005","x":674,"y":358,"w":102,"h":147,"rotated": false}},
{"SPRITE":{"name":"6","x":1886,"y":167,"w":97,"h":130,"rotated":false}}, {"SPRITE" : {"name": "0006","x":116,"y":360,"w":95,"h":130,"rotated": false}},
{"SPRITE":{"name":"7","x":1398,"y":0,"w":470,"h":264,"rotated":false}}, {"SPRITE" : {"name": "0007","x":2038,"y":4,"w":1,"h":4,"rotated": false}},
{"SPRITE":{"name":"8","x":867,"y":0,"w":513,"h":254,"rotated":false}}, {"SPRITE" : {"name": "0008","x":4,"y":4,"w":411,"h":352,"rotated": false}},
{"SPRITE":{"name":"9","x":0,"y":372,"w":465,"h":270,"rotated":false}}, {"SPRITE" : {"name": "0009","x":419,"y":4,"w":398,"h":350,"rotated": false}},
{"SPRITE":{"name":"10","x":1900,"y":315,"w":101,"h":118,"rotated":false}}, {"SPRITE" : {"name": "0010","x":1303,"y":275,"w":329,"h":162,"rotated": false}},
{"SPRITE":{"name":"11","x":867,"y":272,"w":308,"h":367,"rotated":false}}, {"SPRITE" : {"name": "0011","x":303,"y":360,"w":90,"h":103,"rotated": false}},
{"SPRITE":{"name":"12","x":483,"y":371,"w":338,"h":164,"rotated":false}}, {"SPRITE" : {"name": "0012","x":1823,"y":4,"w":211,"h":236,"rotated": false}},
{"SPRITE":{"name":"13","x":0,"y":0,"w":422,"h":354,"rotated":false}}, {"SPRITE" : {"name": "0013","x":1823,"y":244,"w":208,"h":235,"rotated": false}},
{"SPRITE":{"name":"14","x":440,"y":0,"w":409,"h":353,"rotated":false}}, {"SPRITE" : {"name": "0014","x":821,"y":258,"w":478,"h":262,"rotated": false}},
{"SPRITE":{"name":"15","x":1900,"y":451,"w":92,"h":103,"rotated":false}}, {"SPRITE" : {"name": "0015","x":821,"y":4,"w":521,"h":250,"rotated": false}},
{"SPRITE":{"name":"16","x":1193,"y":282,"w":218,"h":239,"rotated":false}}, {"SPRITE" : {"name": "0016","x":1346,"y":4,"w":473,"h":267,"rotated": false}}
{"SPRITE":{"name":"17","x":1429,"y":282,"w":215,"h":237,"rotated":false}}]}, ]},
"meta":{ "meta": {
"app":"Adobe Animate (Better TA Extension)", "app": "Adobe Animate",
"version":"24.0.2.12", "version": "22.0.5.191",
"image":"spritemap1.png", "image": "spritemap1.png",
"format":"RGBA8888", "format": "RGBA8888",
"size":{"w":2031,"h":662}, "size": {"w":2043,"h":524},
"resolution":"1" "resolution": "1"
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because it is too large Load Diff
@@ -1,40 +1,58 @@
{"ATLAS":{"SPRITES":[ {"ATLAS": {"SPRITES":[
{"SPRITE":{"name":"0","x":349,"y":867,"w":254,"h":136,"rotated":false}}, {"SPRITE" : {"name": "0000","x":994,"y":1029,"w":187,"h":118,"rotated": false}},
{"SPRITE":{"name":"1","x":1648,"y":328,"w":287,"h":363,"rotated":false}}, {"SPRITE" : {"name": "0001","x":1953,"y":524,"w":84,"h":140,"rotated": false}},
{"SPRITE":{"name":"2","x":1571,"y":709,"w":185,"h":117,"rotated":false}}, {"SPRITE" : {"name": "0002","x":471,"y":430,"w":258,"h":137,"rotated": false}},
{"SPRITE":{"name":"3","x":1686,"y":855,"w":86,"h":136,"rotated":false}}, {"SPRITE" : {"name": "0003","x":1300,"y":641,"w":122,"h":66,"rotated": false}},
{"SPRITE":{"name":"4","x":349,"y":1021,"w":122,"h":68,"rotated":false}}, {"SPRITE" : {"name": "0004","x":1825,"y":1078,"w":108,"h":126,"rotated": false}},
{"SPRITE":{"name":"5","x":1774,"y":709,"w":110,"h":128,"rotated":false}}, {"SPRITE" : {"name": "0005","x":360,"y":695,"w":103,"h":149,"rotated": false}},
{"SPRITE":{"name":"6","x":621,"y":867,"w":105,"h":148,"rotated":false}}, {"SPRITE" : {"name": "0006","x":1945,"y":867,"w":95,"h":130,"rotated": false}},
{"SPRITE":{"name":"7","x":1571,"y":844,"w":97,"h":131,"rotated":false}}, {"SPRITE" : {"name": "0007","x":2034,"y":4,"w":1,"h":4,"rotated": false}},
{"SPRITE":{"name":"8","x":800,"y":1,"w":543,"h":308,"rotated":false}}, {"SPRITE" : {"name": "0008","x":943,"y":650,"w":309,"h":375,"rotated": false}},
{"SPRITE":{"name":"9","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":"10","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":"11","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":"12","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":"13","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":"14","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":"15","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":"16","x":1363,"y":1,"w":543,"h":308,"rotated":false}}, {"SPRITE" : {"name": "0016","x":782,"y":1029,"w":208,"h":235,"rotated": false}},
{"SPRITE":{"name":"17","x":1,"y":444,"w":771,"h":404,"rotated":false}}, {"SPRITE" : {"name": "0017","x":782,"y":910,"w":117,"h":83,"rotated": false}},
{"SPRITE":{"name":"18","x":791,"y":700,"w":302,"h":369,"rotated":false}}, {"SPRITE" : {"name": "0018","x":994,"y":1151,"w":117,"h":83,"rotated": false}},
{"SPRITE":{"name":"19","x":1111,"y":957,"w":120,"h":86,"rotated":false}}, {"SPRITE" : {"name": "0019","x":1937,"y":1108,"w":103,"h":83,"rotated": false}},
{"SPRITE":{"name":"20","x":1790,"y":855,"w":120,"h":86,"rotated":false}}, {"SPRITE" : {"name": "0020","x":1115,"y":1151,"w":117,"h":83,"rotated": false}},
{"SPRITE":{"name":"21","x":1790,"y":959,"w":106,"h":86,"rotated":false}}, {"SPRITE" : {"name": "0021","x":1654,"y":714,"w":287,"h":360,"rotated": false}},
{"SPRITE":{"name":"22","x":1249,"y":957,"w":120,"h":86,"rotated":false}}, {"SPRITE" : {"name": "0022","x":471,"y":571,"w":179,"h":67,"rotated": false}},
{"SPRITE":{"name":"23","x":0,"y":0,"w":781,"h":425,"rotated":false}}, {"SPRITE" : {"name": "0023","x":654,"y":571,"w":118,"h":75,"rotated": false}},
{"SPRITE":{"name":"24","x":0,"y":867,"w":331,"h":164,"rotated":false}}, {"SPRITE" : {"name": "0024","x":1966,"y":223,"w":55,"h":112,"rotated": false}},
{"SPRITE":{"name":"25","x":799,"y":328,"w":413,"h":354,"rotated":false}}, {"SPRITE" : {"name": "0025","x":1966,"y":4,"w":64,"h":106,"rotated": false}},
{"SPRITE":{"name":"26","x":1230,"y":328,"w":400,"h":353,"rotated":false}}, {"SPRITE" : {"name": "0026","x":1185,"y":1029,"w":67,"h":70,"rotated": false}},
{"SPRITE":{"name":"27","x":1387,"y":954,"w":90,"h":103,"rotated":false}}, {"SPRITE" : {"name": "0027","x":337,"y":1182,"w":122,"h":62,"rotated": false}},
{"SPRITE":{"name":"28","x":1111,"y":700,"w":213,"h":239,"rotated":false}}, {"SPRITE" : {"name": "0028","x":1647,"y":1078,"w":174,"h":97,"rotated": false}},
{"SPRITE":{"name":"29","x":1342,"y":699,"w":211,"h":237,"rotated":false}}]}, {"SPRITE" : {"name": "0029","x":360,"y":870,"w":82,"h":14,"rotated": false}},
"meta":{ {"SPRITE" : {"name": "0030","x":1647,"y":1179,"w":128,"h":61,"rotated": false}},
"app":"Adobe Animate (Better TA Extension)", {"SPRITE" : {"name": "0031","x":463,"y":1182,"w":124,"h":58,"rotated": false}},
"version":"24.0.2.12", {"SPRITE" : {"name": "0032","x":1966,"y":114,"w":59,"h":105,"rotated": false}},
"image":"spritemap1.png", {"SPRITE" : {"name": "0033","x":1953,"y":360,"w":87,"h":160,"rotated": false}},
"format":"RGBA8888", {"SPRITE" : {"name": "0034","x":1945,"y":714,"w":92,"h":149,"rotated": false}},
"size":{"w":1965,"h":1089}, {"SPRITE" : {"name": "0035","x":1426,"y":641,"w":104,"h":42,"rotated": false}},
"resolution":"1" {"SPRITE" : {"name": "0036","x":1953,"y":692,"w":73,"h":18,"rotated": false}},
{"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: 453 KiB

After

Width:  |  Height:  |  Size: 228 KiB

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

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 165 KiB

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

Before

Width:  |  Height:  |  Size: 535 KiB

After

Width:  |  Height:  |  Size: 344 KiB

-2
View File
@@ -433,8 +433,6 @@
<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.">
+3 -3
View File
@@ -4,7 +4,7 @@
<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" ref="old" /> <git name="openfl" url="https://github.com/CodenameCrew/cne-openfl" />
<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 --> <!-- <git name="lime" url="https://github.com/CodenameCrew/cne-lime" /> disabled for now until fixed -->
@@ -14,9 +14,11 @@
<!-- 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" skipDeps="true" /> <git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
<lib name="funkin-modchart" version="1.2.4" 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" />
@@ -31,8 +33,6 @@
<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" />
<git name="flixel-animate" url="https://github.com/MaybeMaru/flixel-animate/" skipDeps="true"/>
</if> </if>
<!-- Hxcpp Building --> <!-- Hxcpp Building -->
+10 -36
View File
@@ -3,23 +3,20 @@
<!-- _________________________ 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.yoshman29.codenameengine" file="CodenameEngine" packageName="com.codenamecrew.codenameengine"
package="com.yoshman29.codenameengine" package="com.codenamecrew.codenameengine"
version="1.0.1" company="Yoshman29" /> version="1.0.1" company="CodenameCrew" />
<!-- <!--
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE! CHANGE THE SAVE PATH & NAME FOR YOUR HARDCODED MOD HERE!
--> -->
<haxedef name="SAVE_PATH" value="CodenameEngine"/> <haxedef name="SAVE_PATH" value="CodenameCrew/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" />
<!-- DO NOT REMOVE!! --> <!-- DO NOT REMOVE!! ALLOWS ME TO DO REGIONS LIKE IN C# AND KEEP CODE ORGANIZED. REMOVING IT WOULD BREAK THE ENGINE -->
<haxedef name="REGION" /> <haxedef name="REGION" />
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file <!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
@@ -72,11 +69,7 @@
<!-- Comment this out to prevent extending classes in hscript. <!-- Comment this out to prevent extending classes in hscript.
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 support for abstracts in hscript.
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define 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" />
@@ -119,24 +112,6 @@
<!-- Disable a optimization, to allow reflection to use more functions --> <!-- Disable a optimization, to allow reflection to use more functions -->
<define name="FLX_NO_GENERIC" /> <define name="FLX_NO_GENERIC" />
<!-- _______________________________ Display Server ______________________________ -->
<haxedef name="LIME_CMD" value="${project.command}"/>
<define name="LIME_DISPLAY" if="${project.command == update} || ${project.command == display} || display || display_details" />
<haxedef name="LIME_DISPLAY" if="LIME_DISPLAY" />
<section if="LIME_DISPLAY">
<!-- For display server -->
<undefine name="COMPILE_ALL_CLASSES" />
<unset name="COMPILE_ALL_CLASSES" />
<undefine name="HSCRIPT_ABSTRACT_SUPPORT" />
<unset name="HSCRIPT_ABSTRACT_SUPPORT" />
<undefine name="CUSTOM_CLASSES" />
<unset name="CUSTOM_CLASSES" />
</section>
<!-- _______________________________ Libraries ______________________________ --> <!-- _______________________________ Libraries ______________________________ -->
<!-- KEEP BOTH OF THESE AS THERE ARE SOME PARTICULAR CASES! --> <!-- KEEP BOTH OF THESE AS THERE ARE SOME PARTICULAR CASES! -->
@@ -152,7 +127,8 @@
<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="flixel-animate" /> <haxelib name="flxanimate" />
<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" />
@@ -194,7 +170,7 @@
<!-- ______________________________ Haxedefines _____________________________ --> <!-- ______________________________ Haxedefines _____________________________ -->
<haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()" unless="display" /> <haxeflag name="--macro" value="funkin.backend.system.macros.BuildInfo.printBuildInfo()" />
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" /> <haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
@@ -241,10 +217,8 @@
<haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/> <haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/>
</section> </section>
<section if="COMPILE_ALL_CLASSES"> <section if="COMPILE_ALL_CLASSES">
<haxeflag name="-dce" value="no" /> <haxeflag name="-dce" value="no" />
<haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" /> <haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" />
</section> </section>
+92
View File
@@ -0,0 +1,92 @@
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;
}
}
+144 -86
View File
@@ -1,7 +1,5 @@
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;
@@ -9,6 +7,7 @@ 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;
@@ -16,10 +15,6 @@ 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)
{ {
@@ -48,7 +43,7 @@ enum abstract XMLAnimType(Int)
} }
} }
class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents
{ {
public var extra:Map<String, Dynamic> = []; public var extra:Map<String, Dynamic> = [];
@@ -61,9 +56,6 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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;
/** /**
@@ -74,7 +66,9 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
public var beatOffset:Int = 0; public var beatOffset:Int = 0;
public var skipNegativeBeats:Bool = false; public var skipNegativeBeats:Bool = false;
public var animateSettings:FlxAnimateSettings = {}; public var animateAtlas:FlxAnimate;
@:noCompletion public var atlasPlayingAnim:String;
@:noCompletion public var atlasPath:String;
var _rect2:FlxRect; var _rect2:FlxRect;
@@ -91,7 +85,6 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
} }
moves = false; moves = false;
applyStageMatrix = true;
} }
/** /**
@@ -107,6 +100,8 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
@: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;
@@ -116,6 +111,8 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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;
} }
@@ -126,6 +123,8 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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()) {
@@ -143,7 +142,15 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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);
frames = Paths.getFrames(path, true, null, null, animateSettings); if (Assets.exists('$noExt/Animation.json'))
{
atlasPath = noExt;
animateAtlas = new FlxAnimate(x, y, noExt);
}
else
{
frames = Paths.getFrames(path, true);
}
return this; return this;
} }
@@ -175,9 +182,48 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
// ANIMATE ATLAS DRAWING // ANIMATE ATLAS DRAWING
#if REGION #if REGION
public override function draw()
{
if (animateAtlas != null)
{
copyAtlasValues();
animateAtlas.draw();
}
else
{
super.draw();
}
}
public function copyAtlasValues()
{
@: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];
@@ -214,6 +260,25 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
); );
} }
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)
@@ -228,6 +293,22 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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>();
@@ -259,7 +340,13 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
Force = anim != null && anim.forced; Force = anim != null && anim.forced;
} }
animation.play(AnimName, Force, Reversed, Frame); if (animateAtlas != null)
{
@: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);
@@ -268,7 +355,7 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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, animateAtlasLabel:Bool = false) public inline function addAnim(name:String, prefix:String, frameRate:Float = 24, ?looped:Bool, ?forced:Bool, ?indices:Array<Int>, x:Float = 0, y:Float = 0, animType:XMLAnimType = NONE)
{ {
return XMLUtil.addAnimToSprite(this, { return XMLUtil.addAnimToSprite(this, {
name: name, name: name,
@@ -279,16 +366,22 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
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, FlxAnimateAnimation> { public function getAnim(name:String):OneOfTwo<FlxAnimation, FlxSymbolAnimation>
{
if(animateAtlas != null)
return animateAtlas.anim.getByName(name);
return animation.getByName(name); return animation.getByName(name);
} }
@@ -299,26 +392,40 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
return FlxPoint.weak(0, 0); return FlxPoint.weak(0, 0);
} }
public inline function hasAnim(AnimName:String):Bool public inline function hasAnim(AnimName:String):Bool @:privateAccess
return animation.exists(AnimName); return animateAtlas != null ? (animateAtlas.anim.animsMap.exists(AnimName)
|| animateAtlas.anim.symbolDictionary.exists(AnimName)) : animation.exists(AnimName);
public inline function getAnimName() public inline function getAnimName() {
return animation.name; return (animateAtlas != null) ? atlasPlayingAnim : animation.name;
}
public inline function isAnimReversed():Bool public inline function isAnimReversed():Bool {
return animation.curAnim?.reversed ?? false; return animateAtlas != null ? animateAtlas.anim.reversed : animation.curAnim != null ? animation.curAnim.reversed : false;
}
public inline function getNameList():Array<String> public inline function getNameList():Array<String> {
return animation.getNameList(); if (animateAtlas != null)
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 animation.curAnim?.finished ?? true; return animateAtlas != null ? animateAtlas.anim.finished : (animation.curAnim != null ? animation.curAnim.finished : true);
}
public inline function isAnimAtEnd() public inline function isAnimAtEnd() {
return animation.curAnim?.isAtEnd ?? false; return animateAtlas != null ? animateAtlas.anim.isAtEnd : (animation.curAnim != null ? animation.curAnim.isAtEnd : false);
}
override function updateAnimation(elapsed:Float) { override function updateAnimation(elapsed:Float) {
if (animEnabled) if (animEnabled)
@@ -340,59 +447,10 @@ class FunkinSprite extends FlxAnimate implements IBeatReceiver implements IOffse
return beatInterval = v; return beatInterval = v;
} }
@:noCompletion @:noCompletion private inline function get_globalCurFrame() {
@:deprecated("`FunkinSprite.animateAtlas` is deprecated, just use `FunkinSprite` instead") return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0);
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) {
if (animation.curAnim != null) return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val);
animation.curAnim.curFrame = val;
return val;
}
override function prepareDrawMatrix(matrix:FlxMatrix, camera:FlxCamera):Void {
matrix.translate(-origin.x, -origin.y);
if (frameOffsetAngle != null && frameOffsetAngle != angle)
{
var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
var cos = Math.cos(angleOff);
var sin = Math.sin(angleOff);
// cos doesnt need to be negated
matrix.rotateWithTrig(cos, -sin);
matrix.translate(-frameOffset.x, -frameOffset.y);
matrix.rotateWithTrig(cos, sin);
}
else
matrix.translate(-frameOffset.x, -frameOffset.y);
matrix.translate(origin.x, origin.y);
super.prepareDrawMatrix(matrix, camera);
applyZoomFactor(matrix, camera);
}
function applyZoomFactor(matrix:FlxMatrix, camera:FlxCamera) {
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;
} }
} }
+6 -2
View File
@@ -114,8 +114,12 @@ class MusicBeatTransition extends MusicBeatSubstate {
} }
if (transitionSprite != null) { if (transitionSprite != null) {
transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height); if (transitionSprite.animateAtlas == null) {
transitionSprite.updateHitbox(); transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height);
transitionSprite.updateHitbox();
} else {
transitionSprite.screenCenter();
}
} }
} }
@@ -5,7 +5,6 @@ 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> = [];
@@ -142,16 +141,6 @@ 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;
} }
@@ -149,22 +149,6 @@ 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
+3 -9
View File
@@ -8,7 +8,6 @@ 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;
@@ -175,9 +174,6 @@ 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');
@@ -201,7 +197,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, ?animateSettings:FlxAnimateSettings) { public static function getFrames(key:String, assetsPath:Bool = false, ?library:String, ?ext:String = null) {
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)
@@ -209,7 +205,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, animateSettings); return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true, ext), false, null, false, ext);
} }
/** /**
@@ -246,7 +242,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, ?animateSettings:FlxAnimateSettings):FlxFramesCollection { static function loadFrames(path:String, Unique:Bool = false, Key:String = null, SkipAtlasCheck:Bool = false, SkipMultiCheck:Bool = false, ?Ext:String = null):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;
@@ -267,8 +263,6 @@ 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,8 +108,6 @@ 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
@@ -33,9 +33,6 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
zip = SysZip.openFromFile(basePath); zip = SysZip.openFromFile(basePath);
zip.read(); zip.read();
for(entry in zip.entries) { for(entry in zip.entries) {
if (entry.fileName.length < 0 || entry.fileName.endsWith("/"))
continue;
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry; lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry;
nameMap.set(entry.fileName.toLowerCase(), entry.fileName); nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
} }
@@ -157,10 +154,6 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return content; return content;
} }
public override function list(type:String):Array<String> {
return[for(k=>e in nameMap) '$prefix$e'];
}
// Backwards compat // Backwards compat
@:noCompletion public var zipPath(get, set):String; @:noCompletion public var zipPath(get, set):String;
+35
View File
@@ -97,6 +97,41 @@ 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", []);
+1 -2
View File
@@ -32,8 +32,7 @@ typedef ChartMetaData = {
public var ?icon:String; public var ?icon:String;
public var ?color:FlxColor; public var ?color:FlxColor;
public var ?coopAllowed:Bool; public var ?excludedGameModes:Array<String>;
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
@@ -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" => animate.FlxAnimate, "FlxAnimate" => funkin.backend.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,
@@ -1,5 +1,7 @@
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
@@ -14,11 +16,26 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
*/ */
public var variant:String; public var variant:String;
/** /**
* Whenever opponent mode is enabled or not. * Used game mode's data.
*/ */
public var opponentMode:Bool; public var gameMode:FreeplayGameMode;
/**
* Whenever coop mode is enabled.
*/ // Backwards compat
public var coopMode:Bool; @:noCompletion public var opponentMode(get, set):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;
}
} }
+7 -3
View File
@@ -13,7 +13,7 @@ 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()) @:build(funkin.backend.system.macros.FlagMacro.build("onReset"))
class Flags { class Flags {
public static var overridenFlags:Map<String, Bool> = []; public static var overridenFlags:Map<String, Bool> = [];
@@ -312,6 +312,11 @@ 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) {
@@ -332,9 +337,8 @@ 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
} }
/** /**
@@ -1,134 +0,0 @@
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 LIME_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,9 +19,9 @@ class DefinesMacro {
// INTERNAL MACROS // INTERNAL MACROS
#if REGION #if REGION
private static macro function __getDefines():Expr { private static macro function __getDefines() {
#if display #if display
return macro $v{([]:Map<String, Dynamic>)}; return macro $v{[]};
#else #else
return macro $v{Context.getDefines()}; return macro $v{Context.getDefines()};
#end #end
@@ -19,7 +19,7 @@ class FlagMacro {
return false; return false;
} }
public static function build():Array<Field> { public static function build(resetCallback:String):Array<Field> {
var fields = Context.getBuildFields(); var fields = Context.getBuildFields();
var clRef = Context.getLocalClass(); var clRef = Context.getLocalClass();
@@ -248,7 +248,10 @@ class FlagMacro {
access: [APublic, AStatic], access: [APublic, AStatic],
kind: FFun({ kind: FFun({
args: [], args: [],
expr: macro $b{resetExprs}, expr: macro {
$b{resetExprs};
$i{resetCallback}();
},
ret: macro: Void ret: macro: Void
}), }),
pos: Context.currentPos(), pos: Context.currentPos(),
@@ -64,7 +64,7 @@ class GitCommitMacro {
// INTERNAL MACROS // INTERNAL MACROS
#if REGION #if REGION
private static macro function __getCommitHash() { private static macro function __getCommitHash() {
#if LIME_DISPLAY #if display
return macro $v{"-"}; return macro $v{"-"};
#else #else
try { try {
@@ -78,7 +78,7 @@ class GitCommitMacro {
} }
private static macro function __getCommitNumber() { private static macro function __getCommitNumber() {
#if LIME_DISPLAY #if display
return macro $v{0}; return macro $v{0};
#else #else
try { try {
@@ -92,7 +92,7 @@ class GitCommitMacro {
} }
private static macro function __getCommitHashLong() { private static macro function __getCommitHashLong() {
#if LIME_DISPLAY #if display
return macro $v{"-"}; return macro $v{"-"};
#else #else
try { try {
@@ -106,7 +106,7 @@ class GitCommitMacro {
} }
private static macro function __getCommitMessage() { private static macro function __getCommitMessage() {
#if LIME_DISPLAY #if display
return macro $v{"-"}; return macro $v{"-"};
#else #else
try { try {
@@ -124,7 +124,7 @@ class GitCommitMacro {
} }
private static macro function __getCommitAuthor() { private static macro function __getCommitAuthor() {
#if LIME_DISPLAY #if display
return macro $v{"-"}; return macro $v{"-"};
#else #else
try { try {
@@ -138,8 +138,8 @@ class GitCommitMacro {
} }
private static macro function __getCommitDate() { private static macro function __getCommitDate() {
#if LIME_DISPLAY #if display
return macro Date.now(); return macro $v{Date.now()};
#else #else
try { try {
// get the time in UTC format // get the time in UTC format
@@ -164,7 +164,7 @@ class GitCommitMacro {
} }
private static macro function __getCurrentBranch() { private static macro function __getCurrentBranch() {
#if LIME_DISPLAY #if display
return macro $v{"-"}; return macro $v{"-"};
#else #else
try { try {
@@ -179,7 +179,7 @@ class GitCommitMacro {
} }
private static macro function __getHasUncommittedChanges() { private static macro function __getHasUncommittedChanges() {
#if LIME_DISPLAY #if display
return macro $v{false}; return macro $v{false};
#else #else
try { try {
@@ -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", "animate" "DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting"
]) ])
Compiler.include(inc); Compiler.include(inc);
+7 -28
View File
@@ -21,6 +21,7 @@ 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;
@@ -33,8 +34,6 @@ 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;
@@ -575,29 +574,6 @@ 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
@@ -1276,20 +1252,23 @@ final class CoolUtil
return animsList; return animsList;
} }
public static function getAnimsListFromAtlas(atlas:AnimationJson):Array<String> { public static function getAnimsListFromAtlas(atlas:AnimAtlas):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) for (symbol in atlas.SD.S)
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> {
return getAnimsListFromFrames(spr.frames); if (spr.animateAtlas != null) {
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
+136 -8
View File
@@ -34,23 +34,151 @@ 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 = FakeCamera.instance; var nc:FakeCamera = isAnimateAtlas ? FakeCallCamera.instance : 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(sprite is IPrePostDraw) { if(isAnimateAtlas) {
var postDraw = cast(sprite, IPrePostDraw); var cnc = FakeCallCamera.instance;
postDraw.preDraw(); var sprite = funkinSprite;
sprite.drawComplex(nc); var oldOnDraw = cnc.onDraw;
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 {
sprite.drawComplex(nc); if(sprite is IPrePostDraw) {
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;
} }
+8 -24
View File
@@ -14,7 +14,6 @@ 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;
@@ -128,9 +127,6 @@ 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;
@@ -229,8 +225,7 @@ 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
}); });
} }
@@ -266,8 +261,7 @@ 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;
@@ -279,7 +273,6 @@ 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;
} }
@@ -306,23 +299,15 @@ 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.frames is FlxAnimateFrames) { if (sprite is FunkinSprite && cast(sprite, FunkinSprite).animateAtlas != null) {
var animateAnim = cast(sprite, FunkinSprite).animateAtlas.anim;
if(animData.anim == null) if(animData.anim == null)
return MISSING_PROPERTY; return MISSING_PROPERTY;
var animateAnim = cast(sprite, FunkinSprite).anim; if (animData.indices != null && animData.indices.length > 0)
animateAnim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop);
if (animData.label) { else
if (animData.indices != null && animData.indices.length > 0) animateAnim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop);
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)
@@ -478,7 +463,6 @@ 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;
} }
@@ -2,13 +2,18 @@ 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;
@@ -21,7 +26,6 @@ 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;
@@ -112,20 +116,13 @@ 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, translate("looping"), animData.loop, 0, true); loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, "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));
@@ -207,7 +204,6 @@ 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);
@@ -235,12 +231,18 @@ class CharacterAnimButton extends UIButton {
} }
var oldName:String = anim; var oldName:String = anim;
if (parent.character.animateAtlas != null) {
var animSymbol:FlxSymbolAnimation = __getAnimationSymbol();
var flxAnimation:FlxAnimation = __getFlxAnimation(); parent.character.animateAtlas.anim.animsMap.remove(anim);
flxAnimation.name = newName; parent.character.animateAtlas.anim.animsMap.set(newName, animSymbol);
} 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;
@@ -252,7 +254,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:DisplayAnimFrameEntry = parent.displayAnimsFramesList[anim]; var displayFrame:{scale:Float, animBounds:Rectangle, frame:Int} = parent.displayAnimsFramesList[anim];
parent.displayAnimsFramesList.remove(anim); parent.displayAnimsFramesList.remove(anim);
parent.displayAnimsFramesList.set(newName, displayFrame); parent.displayAnimsFramesList.set(newName, displayFrame);
@@ -274,20 +276,23 @@ class CharacterAnimButton extends UIButton {
var oldAnim:String = animData.anim; var oldAnim:String = animData.anim;
animData.anim = newAnim; animData.anim = newAnim;
var flxAnimation:FlxAnimation = __getFlxAnimation(); if (parent.character.animateAtlas != null)
flxAnimation.prefix = newAnim; __refreshAnimation();
else {
var flxAnimation:FlxAnimation = __getFlxAnimation();
flxAnimation.prefix = newAnim;
refreshAnimation(__getFlxAnimation(), data); refreshFlxAnimationFrames(flxAnimation, animData);
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);
@@ -325,8 +330,13 @@ class CharacterAnimButton extends UIButton {
var oldFPS:Float = animData.fps; var oldFPS:Float = animData.fps;
animData.fps = newFPS; animData.fps = newFPS;
var flxAnimation:FlxAnimation = __getFlxAnimation(); if (parent.character.animateAtlas != null) {
flxAnimation.frameRate = newFPS; var animSymbol:FlxSymbolAnimation = __getAnimationSymbol();
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);
@@ -341,8 +351,13 @@ class CharacterAnimButton extends UIButton {
animData.loop = newLooping; animData.loop = newLooping;
var flxAnimation:FlxAnimation = __getFlxAnimation(); if (parent.character.animateAtlas != null) {
flxAnimation.looped = animData.loop; var animSymbol:FlxSymbolAnimation = __getAnimationSymbol();
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);
@@ -352,19 +367,6 @@ 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();
@@ -401,6 +403,50 @@ 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)); FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND));
@@ -420,8 +466,8 @@ class CharacterAnimButton extends UIButton {
super.draw(); super.draw();
if (!closed && parent.displayAnimsFramesList.exists(anim)) { if (!closed && parent.displayAnimsFramesList.exists(anim)) {
var displayData:DisplayAnimFrameEntry = parent.displayAnimsFramesList.get(anim); var displayData:{frame:Int, scale:Float, animBounds:Rectangle} = parent.displayAnimsFramesList.get(anim);
parent.displayWindowSprite.frame = displayData.frame; parent.displayWindowSprite.frame = parent.displayWindowSprite.frames.frames[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();
@@ -475,26 +521,16 @@ class CharacterAnimButton extends UIButton {
return parent.character.animation._animations[anim]; return parent.character.animation._animations[anim];
} }
public inline function refreshAnimation(anim:FlxAnimation, animData:AnimData) @:privateAccess { public inline function __getAnimationSymbol():Null<FlxSymbolAnimation> @:privateAccess {
var refreshed:Bool = false; if (!parent.character.animateAtlas.anim.animsMap.exists(anim))
XMLUtil.addAnimToSprite(parent.character, data);
parent.character.animation.remove(animData.name); return parent.character.animateAtlas.anim.animsMap[anim];
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 {
refreshAnimation(__getFlxAnimation(), data); if (parent.character.animateAtlas != null) refreshSymbolKeyFrames(__getAnimationSymbol(), data);
else refreshFlxAnimationFrames(__getFlxAnimation(), data);
if (valid) { if (valid) {
parent.buildAnimDisplay(anim, data); parent.buildAnimDisplay(anim, data);
@@ -8,24 +8,15 @@ 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, DisplayAnimFrameEntry> = []; public var displayAnimsFramesList:Map<String, {scale:Float, animBounds:Rectangle, frame:OneOfTwo<Int, String>}> = [];
public var animButtons:Map<String, CharacterAnimButton> = []; public var animButtons:Map<String, CharacterAnimButton> = [];
public var animsList:Array<String> = []; public var animsList:Array<String> = [];
@@ -39,7 +30,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
buttonCameras.pixelPerfectRender = true; buttonCameras.pixelPerfectRender = true;
if (Assets.exists(Paths.image('characters/${character.sprite}'))) if (Assets.exists(Paths.image('characters/${character.sprite}')) && character.animateAtlas == null)
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();
@@ -78,45 +69,34 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
} }
public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess { public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess {
var anim:FlxAnimation = character.animation._animations[anim.name]; if (character.animateAtlas == null) {
if (anim == null) return; var anim:FlxAnimation = character.animation._animations[anim.name];
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, { displayAnimsFramesList.set(name, {frame: anim.frames.getDefault([0])[0], scale: 104/Math.max(animBounds.width, animBounds.height), animBounds: animBounds});
frame: character.frames.frames[anim.frames.getDefault([0])[0]], } else {
scale: 104/Math.max(animBounds.width, animBounds.height), character.storeAtlasState();
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)); FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
@@ -154,7 +134,6 @@ 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);
} }
@@ -554,9 +554,6 @@ 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);
@@ -628,9 +625,6 @@ 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);
@@ -942,7 +936,6 @@ 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,8 +4,6 @@ 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;
@@ -16,35 +14,39 @@ 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();
var animName = animation.curAnim?.name ?? null; if (animateAtlas != null) {
var animFrame = animation.curAnim?.curFrame ?? 0; storeAtlasState();
var animFrameTimer = animation.curAnim?._frameTimer ?? 0; for (anim in ghosts) {
animateAtlas.anim.play(anim, true, false, 0);
setAnimOffset(anim);
for (anim in ghosts) @:privateAccess { alpha = 0.4; color = 0xFFAEAEAE;
alpha = 0.4; color = 0xFFAEAEAE; super.draw();
}
var flxanim:FlxAnimation = animation._animations.get(anim); if (ghosts.length > 0) restoreAtlasState();
animation.play(anim, true, false, flxanim.frames.length - 1);
setAnimOffset(anim);
super.draw();
} }
else {
for (anim in ghosts) @:privateAccess {
alpha = 0.4; color = 0xFFAEAEAE;
if (ghosts.length > 0) var flxanim:FlxAnimation = animation._animations.get(anim);
frame = frames.frames[animation.frameIndex]; var frameIndex:Int = flxanim.frames.getDefault([0])[flxanim.frames.length - 1];
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;
@@ -52,12 +54,8 @@ 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
animation.play(animName, true, false, animFrame); setAnimOffset(animateAtlas != null ? atlasPlayingAnim : animation.name);
setAnimOffset(animName);
if (animation.curAnim != null)
animation.curAnim._frameTimer = animFrameTimer;
}
super.draw(); super.draw();
} }
@@ -72,45 +70,22 @@ 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 { // This gets annoying lmao -lunar
if (!isAnimate) return null; private var atlasState:AtlasState = null;
public function storeAtlasState():AtlasState @:privateAccess {
var wasInvalidFrame:Bool = !colorTransform.__isDefault(false); return atlasState = {
colorTransform.__identity(); oldAnim: atlasPlayingAnim,
oldFrame: animateAtlas.anim.curFrame,
var animName = animation.curAnim?.name ?? null; oldTick: animateAtlas.anim._tick,
var animFrame = animation.curAnim?.curFrame ?? 0; oldPlaying: animateAtlas.anim.isPlaying,
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 public function restoreAtlasState(state:AtlasState = null) @:privateAccess {
// it's gone now <3 -yosh 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,6 +1,7 @@
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;
@@ -183,15 +184,31 @@ class CharacterPropertiesWindow extends UISliceSprite {
character.sprite = sprite; character.sprite = sprite;
animsWindow.displayWindowSprite.animation.reset(); animsWindow.displayWindowSprite.animation.reset();
animsWindow.clearDisplayAnims(); animsWindow.displayAnimsFramesList.clear();
if (animsWindow.displayWindowGraphic != null) if (animsWindow.displayWindowGraphic != null)
animsWindow.displayWindowGraphic.destroy(); animsWindow.displayWindowGraphic.destroy();
character.frames = Paths.getFrames(path, true); if (Assets.exists('$noExt/Animation.json')) {
if (character.animateAtlas == null) {
character.animation.reset();
character.animateAtlas = new FlxAnimate(character.x, character.y);
} else
character.animateAtlas.anim.stop();
animsWindow.displayWindowSprite.loadGraphicFromSprite(character); character.atlasPath = noExt;
if (Assets.exists(Paths.image('characters/${character.sprite}'))) character.animateAtlas.loadAtlas(noExt);
animsWindow.displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false)); } else {
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
@@ -1,5 +1,6 @@
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;
@@ -21,8 +22,7 @@ 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 opponentModeCheckbox:UICheckbox; public var excludedGameModesList:UIAutoCompleteButtonList;
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,25 +78,29 @@ 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) -> {updateIcon(newIcon);} iconTextBox.onChange = (newIcon:String) -> iconSprite.setIcon(newIcon);
add(iconTextBox); add(iconTextBox);
addLabelOn(iconTextBox, translate("icon")); addLabelOn(iconTextBox, translate("icon"));
updateIcon(metadata.icon); updateIcon(metadata.icon);
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translate("opponentMode"), metadata.opponentModeAllowed); add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID]));
add(opponentModeCheckbox); excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
addLabelOn(opponentModeCheckbox, translate("modesAllowed")); excludedGameModesList.cameraSpacing = 0;
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translate("coopAllowed"), metadata.coopAllowed); for (mode in metadata.excludedGameModes)
add(coopAllowedCheckbox); excludedGameModesList.add(new UIAutoCompleteButtonList.UIAutoCompleteButton(0, 0, excludedGameModesList, excludedGameModesList.suggestItems, mode));
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(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", ")); difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, metadata.difficulties.join(", "));
add(difficultiesTextBox); add(difficultiesTextBox);
addLabelOn(difficultiesTextBox, translate("difficulties")); addLabelOn(difficultiesTextBox, translate("difficulties"));
@@ -111,9 +115,6 @@ 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();
@@ -161,8 +162,7 @@ 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.opponentModeAllowed = opponentModeCheckbox.checked; meta.excludedGameModes = [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()],
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;
@@ -1,5 +1,6 @@
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;
@@ -37,8 +38,7 @@ 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 opponentModeCheckbox:UICheckbox; public var excludedGameModesList:UIAutoCompleteButtonList;
public var coopAllowedCheckbox:UICheckbox;
public var colorWheel:UIColorwheel; public var colorWheel:UIColorwheel;
public var difficultiesTextBox:UITextBox; public var difficultiesTextBox:UITextBox;
@@ -166,24 +166,19 @@ class SongCreationScreen extends UISubstateWindow {
updateIcon("face"); updateIcon("face");
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translateMeta("opponentMode"), true); 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]));
menuDataGroup.add(opponentModeCheckbox); excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
addLabelOn(opponentModeCheckbox, translateMeta("modesAllowed")); excludedGameModesList.cameraSpacing = 0;
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translateMeta("coopAllowed"), true); colorWheel = new UIColorwheel(iconTextBox.x, excludedGameModesList.y, 0xFFFFFF);
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(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, ""); difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, "");
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));
@@ -433,8 +428,7 @@ 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,
opponentModeAllowed: opponentModeCheckbox.checked, excludedGameModes: [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()],
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 -2
View File
@@ -377,7 +377,9 @@ 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.animation.curAnim.numFrames - 1; var lastIndx = (char.animateAtlas != null) ?
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();
@@ -563,7 +565,9 @@ 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.animation.curAnim.numFrames - 1; var lastIndx = (char.animateAtlas != null) ?
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();
@@ -0,0 +1,50 @@
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);
}
}
+3 -3
View File
@@ -3,6 +3,7 @@ package funkin.editors.ui;
import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfTwo;
import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormat;
import flixel.text.FlxText.FlxTextFormatMarkerPair; import flixel.text.FlxText.FlxTextFormatMarkerPair;
import flxanimate.data.SpriteMapData.AnimateAtlas;
import flixel.graphics.frames.FlxFramesCollection; import flixel.graphics.frames.FlxFramesCollection;
import haxe.Json; import haxe.Json;
import haxe.io.Bytes; import haxe.io.Bytes;
@@ -10,7 +11,6 @@ import haxe.io.Path;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import sys.FileSystem; import sys.FileSystem;
import sys.io.File; import sys.io.File;
import animate.FlxAnimateJson;
using StringTools; using StringTools;
using funkin.backend.utils.BitmapUtil; using funkin.backend.utils.BitmapUtil;
@@ -122,7 +122,7 @@ class UIImageExplorer extends UIFileExplorer {
for(file in files) { for(file in files) {
if(SPRITEMAP_JSON_REGEX.match(file)) { if(SPRITEMAP_JSON_REGEX.match(file)) {
var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, file]))); var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, file])));
var json:SpritemapJson = Json.parse(content); var json:AnimateAtlas = Json.parse(content);
if(json.meta.image.toLowerCase() == fileName.toLowerCase()) if(json.meta.image.toLowerCase() == fileName.toLowerCase())
foundSpritemapJson = true; foundSpritemapJson = true;
} else if(file == "Animation.json") } else if(file == "Animation.json")
@@ -147,7 +147,7 @@ class UIImageExplorer extends UIFileExplorer {
spritemaps.sort(Reflect.compare); spritemaps.sort(Reflect.compare);
for(spritemap in spritemaps) { for(spritemap in spritemaps) {
var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, spritemap]))); var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, spritemap])));
var json:SpritemapJson = Json.parse(content); var json:AnimateAtlas = Json.parse(content);
var imageToFind:String = json.meta.image.toLowerCase(); var imageToFind:String = json.meta.image.toLowerCase();
for(file in files) { for(file in files) {
// lowercase since windows does case insensitive stuff, so we use lowercase to match behavior on mac and linux // lowercase since windows does case insensitive stuff, so we use lowercase to match behavior on mac and linux
+9 -32
View File
@@ -1,5 +1,6 @@
package funkin.game; package funkin.game;
import sys.FileSystem;
import flixel.util.FlxSpriteUtil; import flixel.util.FlxSpriteUtil;
import openfl.display.Graphics; import openfl.display.Graphics;
import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfTwo;
@@ -25,7 +26,6 @@ import haxe.Exception;
import haxe.io.Path; import haxe.io.Path;
import haxe.xml.Access; import haxe.xml.Access;
import openfl.geom.ColorTransform; import openfl.geom.ColorTransform;
import animate.FlxAnimateFrames;
using StringTools; using StringTools;
@@ -89,8 +89,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
scripts.call("create"); scripts.call("create");
buildCharacter(xml); buildCharacter(xml);
scripts.call("postCreate"); scripts.call("postCreate");
} }
@:noCompletion var __swappedLeftRightAnims:Bool = false; @:noCompletion var __swappedLeftRightAnims:Bool = false;
@@ -120,7 +118,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
} }
for (i in variants) { for (i in variants) {
CoolUtil.swapAnims(animation, 'singLEFT$i', 'singRIGHT$i'); CoolUtil.switchAnimFrames(animation.getByName('singRIGHT$i'), animation.getByName('singLEFT$i'));
switchOffset('singLEFT$i', 'singRIGHT$i'); switchOffset('singLEFT$i', 'singRIGHT$i');
} }
@@ -159,9 +157,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (event.cancelled) return; if (event.cancelled) return;
if (isDanceLeftDanceRight) if (isDanceLeftDanceRight)
playAnim(((danced = !danced) ? 'danceLeft' : 'danceRight') + idleSuffix, false, DANCE); playAnim(((danced = !danced) ? 'danceLeft' : 'danceRight') + idleSuffix, DANCE);
else else
playAnim('idle' + idleSuffix, false, DANCE); playAnim('idle' + idleSuffix, DANCE);
} }
public function tryDance() { public function tryDance() {
@@ -205,11 +203,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
@:noCompletion var __reverseDrawProcedure:Bool = false; @:noCompletion var __reverseDrawProcedure:Bool = false;
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect { public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect {
if (isFlippedOffsets()) { if (__reverseDrawProcedure) {
flipX = !flipX;
scale.x *= -1; scale.x *= -1;
var bounds:FlxRect = super.getScreenBounds(newRect, camera); var bounds:FlxRect = super.getScreenBounds(newRect, camera);
flipX = !flipX;
scale.x *= -1; scale.x *= -1;
return bounds; return bounds;
} }
@@ -295,7 +291,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
super.playAnim(event.animName, event.force, event.context, event.reverse, event.startingFrame); super.playAnim(event.animName, event.force, event.context, event.reverse, event.startingFrame);
offset.set((isPlayer != playerOffsets) ? globalOffset.x : -globalOffset.x, -globalOffset.y); offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y);
if (event.context == SING || event.context == MISS) if (event.context == SING || event.context == MISS)
lastHit = Conductor.songPosition; lastHit = Conductor.songPosition;
} }
@@ -356,7 +352,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
buildCharacter(xml); buildCharacter(xml);
} }
public function buildCharacter(xml:Access) { public inline function buildCharacter(xml:Access) {
for(node in xml.elements) for(node in xml.elements)
switch(node.name) { switch(node.name) {
case "use-extension" | "extension" | "ext": case "use-extension" | "extension" | "ext":
@@ -386,22 +382,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
updateHitbox(); updateHitbox();
} }
if (xml.x.exists("antialiasing")) antialiasing = (xml.x.get("antialiasing") == "true"); if (xml.x.exists("antialiasing")) antialiasing = (xml.x.get("antialiasing") == "true");
if (xml.x.exists("applyStageMatrix")) applyStageMatrix = (xml.x.get("applyStageMatrix") == "true");
if (xml.x.exists("sprite")) sprite = xml.x.get("sprite"); if (xml.x.exists("sprite")) sprite = xml.x.get("sprite");
if (xml.x.exists("swfMode")) animateSettings.swfMode = (xml.x.get("swfMode") == "true");
if (xml.x.exists("cacheOnLoad")) animateSettings.cacheOnLoad = (xml.x.get("cacheOnLoad") == "true");
if (xml.x.exists("filterQuality")) {
var val = xml.x.get("filterQuality");
var qualityInt = Std.parseInt(val);
if (qualityInt != null) {
animateSettings.filterQuality = cast(qualityInt, FilterQuality);
} else {
var values = ["high", "medium", "low", "rudy"];
var index = values.indexOf(val.toLowerCase());
if (index > -1)
animateSettings.filterQuality = cast(index, FilterQuality);
}
}
var hasInterval:Bool = xml.x.exists("interval"); var hasInterval:Bool = xml.x.exists("interval");
if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval")); if (hasInterval) beatInterval = Std.parseInt(xml.x.get("interval"));
@@ -440,10 +421,10 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public static var characterProperties:Array<String> = [ public static var characterProperties:Array<String> = [
"x", "y", "sprite", "scale", "antialiasing", "x", "y", "sprite", "scale", "antialiasing",
"flipX", "camx", "camy", "isPlayer", "icon", "flipX", "camx", "camy", "isPlayer", "icon",
"color", "gameOverChar", "holdTime", "applyStageMatrix" "color", "gameOverChar", "holdTime"
]; ];
public static var characterAnimProperties:Array<String> = [ public static var characterAnimProperties:Array<String> = [
"name", "anim", "label", "x", "y", "fps", "loop", "indices" "name", "anim", "x", "y", "fps", "loop", "indices"
]; ];
public inline function buildXML(?animsOrder:Array<String>):Xml { public inline function buildXML(?animsOrder:Array<String>):Xml {
@@ -470,7 +451,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (!antialiasing) xml.set("antialiasing", antialiasing == true ? "true" : "false"); if (!antialiasing) xml.set("antialiasing", antialiasing == true ? "true" : "false");
if (isPlayer) xml.set("isPlayer", isPlayer == true ? "true" : "false"); if (isPlayer) xml.set("isPlayer", isPlayer == true ? "true" : "false");
if (isAnimate) xml.set("applyStageMatrix", applyStageMatrix ? "true" : "false");
var anims:Array<AnimData> = []; var anims:Array<AnimData> = [];
if (animsOrder != null) { if (animsOrder != null) {
@@ -496,9 +476,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (anim.indices.length > 0) if (anim.indices.length > 0)
animXml.set("indices", CoolUtil.formatNumberRange(anim.indices)); animXml.set("indices", CoolUtil.formatNumberRange(anim.indices));
if (anim.label)
animXml.set("label", "true");
xml.addChild(animXml); xml.addChild(animXml);
} }
+18 -5
View File
@@ -161,6 +161,7 @@ class HealthIcon extends FunkinSprite
var iconSize:Int = 0; var iconSize:Int = 0;
var iconIsPlayer = xmlValid ? xmlData.get("facing").getDefault("right").toLowerCase() == "left" : false; var iconIsPlayer = xmlValid ? xmlData.get("facing").getDefault("right").toLowerCase() == "left" : false;
animateAtlas = null; // reset
if (this.animated) if (this.animated)
loadSprite(Paths.image(newIconPath)); loadSprite(Paths.image(newIconPath));
else { else {
@@ -183,6 +184,8 @@ class HealthIcon extends FunkinSprite
if(!animation.onFinishEnd.has(animFinishCallback)) if(!animation.onFinishEnd.has(animFinishCallback))
animation.onFinishEnd.add(animFinishCallback); animation.onFinishEnd.add(animFinishCallback);
if(animateAtlas != null && !animateAtlas.anim.onFinishEnd.has(animFinishCallback))
animateAtlas.anim.onFinishEnd.add(animFinishCallback);
var parsedSteps:Map<Int, String> = []; var parsedSteps:Map<Int, String> = [];
@@ -230,10 +233,10 @@ class HealthIcon extends FunkinSprite
offsetY = Std.parseFloat(node.get("offsety")).getDefault(0); offsetY = Std.parseFloat(node.get("offsety")).getDefault(0);
addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), false, null, null, offsetX, offsetY); // don't allow looping for transitions addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), false, null, null, offsetX, offsetY); // don't allow looping for transitions
if (animation.exists(animName)) if (animateAtlas == null && animation.exists(animName))
animation.getByName(animName).flipX = isPlayer != iconIsPlayer; animation.getByName(animName).flipX = isPlayer != iconIsPlayer;
case "anim": case "anim":
if (!this.animated) { if (this.animated == false) {
Logs.trace('Icon ${char} data <anim> is not allowed when not animated', WARNING); Logs.trace('Icon ${char} data <anim> is not allowed when not animated', WARNING);
continue; continue;
} }
@@ -267,7 +270,7 @@ class HealthIcon extends FunkinSprite
looped = node.get("loop").toLowerCase() == "true"; looped = node.get("loop").toLowerCase() == "true";
addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), looped, null, null, offsetX, offsetY); addAnim(animName, node.get("anim"), Std.parseInt(node.get("fps")).getDefault(24), looped, null, null, offsetX, offsetY);
if (animation.exists(animName)) if (animateAtlas == null && animation.exists(animName))
animation.getByName(animName).flipX = isPlayer != iconIsPlayer; animation.getByName(animName).flipX = isPlayer != iconIsPlayer;
case "step": case "step":
if (!node.exists("percent")) { if (!node.exists("percent")) {
@@ -311,8 +314,18 @@ class HealthIcon extends FunkinSprite
curAnimState = data.animState; curAnimState = data.animState;
} }
setGraphicSize(150); if (animateAtlas != null) {
updateHitbox(); @:bypassAccessor
frameWidth = 150;
@:bypassAccessor
frameHeight = 150;
extraOffsets.x -= frameWidth / 2;
extraOffsets.y -= frameHeight / 2;
updateHitbox();
} else {
setGraphicSize(150);
updateHitbox();
}
defaultScale = (xmlValid && xmlData.exists("scale")) ? Std.parseFloat(xmlData.get("scale")).getDefault(scale.x) : scale.x; defaultScale = (xmlValid && xmlData.exists("scale")) ? Std.parseFloat(xmlData.get("scale")).getDefault(scale.x) : scale.x;
scale.set(defaultScale, defaultScale); scale.set(defaultScale, defaultScale);
+4
View File
@@ -20,6 +20,8 @@ class Splash extends FunkinSprite
@:privateAccess { @:privateAccess {
spr.setPosition(source.x, source.y); spr.setPosition(source.x, source.y);
spr.frames = source.frames; spr.frames = source.frames;
if (source.animateAtlas != null && source.atlasPath != null)
spr.loadSprite(source.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;
@@ -27,6 +29,8 @@ class Splash extends FunkinSprite
spr.scale.set(source.scale.x, source.scale.y); spr.scale.set(source.scale.x, source.scale.y);
spr.scrollFactor.set(source.scrollFactor.x, source.scrollFactor.y); spr.scrollFactor.set(source.scrollFactor.x, source.scrollFactor.y);
spr.skew.set(source.skew.x, source.skew.y); spr.skew.set(source.skew.x, source.skew.y);
spr.transformMatrix = source.transformMatrix;
spr.matrixExposed = source.matrixExposed;
spr.zoomFactor = source.zoomFactor; spr.zoomFactor = source.zoomFactor;
spr.animOffsets = source.animOffsets.copy(); spr.animOffsets = source.animOffsets.copy();
} }
+2 -3
View File
@@ -174,9 +174,8 @@ class Strum extends FlxSprite {
if (daNote.isSustainNote) offset.y -= height * 0.5; if (daNote.isSustainNote) offset.y -= height * 0.5;
if (Std.int(daNote.__noteAngle % 360) != 0) { if (Std.int(daNote.__noteAngle % 360) != 0) {
var noteAngle = FlxMath.fastSinCos(daNote.__noteAngle / PIX180); var noteAngleCos = FlxMath.fastCos(daNote.__noteAngle / PIX180);
var noteAngleCos = noteAngle.cos; var noteAngleSin = FlxMath.fastSin(daNote.__noteAngle / PIX180);
var noteAngleSin = noteAngle.sin;
var aOffset:FlxPoint = FlxPoint.get( var aOffset:FlxPoint = FlxPoint.get(
(daNote.origin.x / daNote.scale.x) - daNote.offset.x, (daNote.origin.x / daNote.scale.x) - daNote.offset.x,
+161 -66
View File
@@ -9,6 +9,7 @@ import funkin.backend.scripting.events.menu.freeplay.*;
import funkin.backend.system.Conductor; import funkin.backend.system.Conductor;
import funkin.game.HealthIcon; import funkin.game.HealthIcon;
import funkin.savedata.FunkinSave; import funkin.savedata.FunkinSave;
import haxe.io.Path;
using StringTools; using StringTools;
@@ -34,6 +35,11 @@ class FreeplayState extends MusicBeatState
*/ */
public var curDiffMetaKeys:Array<String> = []; public var curDiffMetaKeys:Array<String> = [];
/*
* Array containing all labels for game modes.
*/
public var gameModeLabels:Array<FreeplayGameMode> = [];
/** /**
* Currently selected song * Currently selected song
*/ */
@@ -43,9 +49,9 @@ class FreeplayState extends MusicBeatState
*/ */
public var curDifficulty:Int = 1; public var curDifficulty:Int = 1;
/** /**
* Currently selected coop/opponent mode * Currently selected game mode
*/ */
public var curCoopMode:Int = 0; public var curGameMode:Int = 0;
/** /**
* Text containing the score info (PERSONAL BEST: 0) * Text containing the score info (PERSONAL BEST: 0)
@@ -58,9 +64,9 @@ class FreeplayState extends MusicBeatState
public var diffText:FlxText; public var diffText:FlxText;
/** /**
* Text containing the current coop/opponent mode ([KEYBINDS] Co-Op mode) * Text containing the current game mode, for example: ([KEYBINDS] Co-Op mode)
*/ */
public var coopText:FlxText; public var gameModeText:FlxText;
/** /**
* Currently lerped score. Is updated to go towards `intendedScore`. * Currently lerped score. Is updated to go towards `intendedScore`.
@@ -130,6 +136,7 @@ class FreeplayState extends MusicBeatState
} }
updateCurSong(); updateCurSong();
gameModeLabels = FreeplayGameMode.get();
DiscordUtil.call("onMenuLoaded", ["Freeplay"]); DiscordUtil.call("onMenuLoaded", ["Freeplay"]);
@@ -177,14 +184,15 @@ class FreeplayState extends MusicBeatState
diffText.font = scoreText.font; diffText.font = scoreText.font;
add(diffText); add(diffText);
coopText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24); gameModeText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24);
coopText.font = scoreText.font; gameModeText.font = scoreText.font;
add(coopText); add(gameModeText);
add(scoreText); add(scoreText);
changeSelection(0, true); changeSelection(0, true);
changeCoopMode(0, true); changeDiff(0, true);
changeGameMode(0, true);
interpColor = new FlxInterpolateColor(bg.color); interpColor = new FlxInterpolateColor(bg.color);
} }
@@ -239,17 +247,17 @@ class FreeplayState extends MusicBeatState
if (canSelect) { if (canSelect) {
changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel); changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0)); changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable changeGameMode((controls.CHANGE_MODE ? 1 : 0));
// putting it before so that its actually smooth // putting it before so that its actually smooth
updateOptionsAlpha(); updateOptionsAlpha();
} }
scoreText.text = TEXT_FREEPLAY_SCORE.format([Math.round(lerpScore)]); scoreText.text = TEXT_FREEPLAY_SCORE.format([Math.round(lerpScore)]);
scoreBG.scale.set(MathUtil.maxSmart(diffText.width, scoreText.width, coopText.width) + 8, (coopText.visible ? coopText.y + coopText.height : 66)); scoreBG.scale.set(MathUtil.maxSmart(diffText.width, scoreText.width, gameModeText.width) + 8, (gameModeText.visible ? gameModeText.y + gameModeText.height : 66));
scoreBG.updateHitbox(); scoreBG.updateHitbox();
scoreBG.x = FlxG.width - scoreBG.width; scoreBG.x = FlxG.width - scoreBG.width;
scoreText.x = coopText.x = scoreBG.x + 4; scoreText.x = gameModeText.x = scoreBG.x + 4;
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2)); diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
interpColor.fpsLerpTo(curSong.color, 0.0625); interpColor.fpsLerpTo(curSong.color, 0.0625);
@@ -310,31 +318,13 @@ class FreeplayState extends MusicBeatState
select(); select();
} }
var __opponentMode:Bool = false;
var __coopMode:Bool = false;
function updateCoopModes() {
__opponentMode = false;
__coopMode = false;
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
__opponentMode = curCoopMode % 2 == 1;
__coopMode = curCoopMode >= 2;
} else if (curSong.coopAllowed) {
__coopMode = curCoopMode == 1;
} else if (curSong.opponentModeAllowed) {
__opponentMode = curCoopMode == 1;
}
}
/** /**
* Selects the current song. * Selects the current song.
*/ */
public function select() { public function select() {
updateCoopModes();
if (curDifficulties.length == 0) return; if (curDifficulties.length == 0) return;
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, __opponentMode, __coopMode)); var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, gameModeLabels[curGameMode]));
if (event.cancelled) return; if (event.cancelled) return;
@@ -393,7 +383,7 @@ class FreeplayState extends MusicBeatState
intendedScore = 0; intendedScore = 0;
return; return;
} }
updateCoopModes();
var changes:Array<HighscoreChange> = []; var changes:Array<HighscoreChange> = [];
if (__coopMode) changes.push(CCoopMode); if (__coopMode) changes.push(CCoopMode);
if (__opponentMode) changes.push(COpponentMode); if (__opponentMode) changes.push(COpponentMode);
@@ -401,45 +391,37 @@ class FreeplayState extends MusicBeatState
intendedScore = saveData.score; intendedScore = saveData.score;
} }
/**
* Array containing all labels for Co-Op / Opponent modes.
*/
public var coopLabels:Array<String> = [
TU.translate("freeplay.solo"),
TU.translate("freeplay.opponentMode"),
TU.translate("freeplay.coopMode"),
TU.translate("freeplay.coopModeSwitched")
];
/** /**
* Change the current coop mode context. * Change the current coop mode context.
* @param change How much to change * @param change How much to change
* @param force Force the change, even if `change` is equal to 0. * @param force Force the change, even if `change` is equal to 0.
*/ */
public function changeCoopMode(change:Int = 0, force:Bool = false) { public function changeGameMode(change:Int = 0, force:Bool = false) {
if (change == 0 && !force) return; if (change == 0 && !force) return;
if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed; var i = 0;
var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change)); var allowed = getAllowedGameModesID();
var wrapped = FlxMath.wrap(curGameMode + change, 0, gameModeLabels.length - 1);
if (event.cancelled) return; while (!allowed.contains(gameModeLabels[wrapped].modeID)) { // Skipping the blacklisted ones - Nex
wrapped = FlxMath.wrap(wrapped + change, 0, gameModeLabels.length - 1);
curCoopMode = event.value; if (i++ > allowed.length) {
curGameMode = 0; // Idk honestly if I should modify the other event's variables - Nex
updateScore(); break;
}
var coopBinds = [CoolUtil.keyToString(Options.P1_CHANGE_MODE[0]), CoolUtil.keyToString(Options.P2_CHANGE_MODE[0])].filter(x -> x != "---");
if (coopBinds.length == 2 && coopBinds[1] == coopBinds[0]) coopBinds.pop();
else if (coopBinds.length == 0) coopBinds.push("---");
var key = '[${coopBinds.join(" / ")}] ';
if (bothEnabled) {
coopText.text = key + coopLabels[curCoopMode];
} else {
coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)];
} }
var e = EventManager.get(MenuChangeEvent).recycle(curGameMode, wrapped, change);
event("onChangeCoopMode", e); // Backwards compat - Nex
if (event("onChangeGameMode", e).cancelled) return;
// Getting from scratch the allowed game modes just in case they changed when the event got called - Nex
if (gameModeText.visible = getAllowedGameModesID().length > 0) gameModeText.text = "[TAB] " + gameModeLabels[curGameMode = e.value].modeName;
updateScore();
}
public inline function getAllowedGameModesID() {
var excluded = songs[curSelected].excludedGameModes;
return [for (mode in gameModeLabels) if (!excluded.contains(mode.modeID)) mode.modeID];
} }
/** /**
@@ -465,13 +447,12 @@ class FreeplayState extends MusicBeatState
} }
changeDiff(0, true); changeDiff(0, true);
changeGameMode(0, true);
#if PRELOAD_ALL #if PRELOAD_ALL
autoplayElapsed = 0; autoplayElapsed = 0;
songInstPlaying = false; songInstPlaying = false;
#end #end
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
} }
function updateOptionsAlpha() { function updateOptionsAlpha() {
@@ -486,7 +467,7 @@ class FreeplayState extends MusicBeatState
iconArray[curSelected].alpha = selectedAlpha; iconArray[curSelected].alpha = selectedAlpha;
for (i=>item in grpSongs.members) for (i => item in grpSongs.members)
{ {
item.targetY = i - curSelected; item.targetY = i - curSelected;
@@ -517,6 +498,118 @@ class FreeplayState extends MusicBeatState
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null) else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
curSong = song; curSong = song;
} }
// Backwards compat
@:noCompletion public function changeCoopMode(change:Int = 0, force:Bool = false) return changeGameMode(change, force);
@:noCompletion public var coopText(get, set):FlxText;
@:noCompletion private function get_coopText() return gameModeText;
@:noCompletion private function set_coopText(val:FlxText) return gameModeText = val;
@:noCompletion function updateCoopModes() {}
@:noCompletion var __opponentMode(get, default):Bool = false; // God this is so cursed - Nex
@:noCompletion private function get___opponentMode() return getAllowedGameModesID().contains("codename.opponent");
@:noCompletion var __coopMode(get, default):Bool = false;
@:noCompletion private function get___coopMode() return getAllowedGameModesID().contains("codename.coop");
@:noCompletion var curCoopMode(get, set):Int;
@:noCompletion private function get_curCoopMode() return curGameMode;
@:noCompletion private function set_curCoopMode(val:Int) return curGameMode = val;
@:noCompletion var coopLabels(get, set):Array<String>;
@:noCompletion private function get_coopLabels() return [for(mode in gameModeLabels) mode.modeName];
@:noCompletion private function set_coopLabels(names:Array<String>) {
gameModeLabels = [for(name in names) {
if (name.startsWith("[TAB] ")) name = name.substr("[TAB] ".length);
new FreeplayGameMode(name, switch(name) {
case "Solo": "codename.solo";
case "Opponent Mode": "codename.opponent";
case "Co-Op Mode": "codename.coop";
case "Co-Op Mode (Switched)": "codename.coop-opponent";
default: name.toLowerCase().replace(" ", "-");
}, switch(name) {
case "Solo": ["solo"];
case "Opponent Mode": ["opponent"];
case "Co-Op Mode": ["coop"];
case "Co-Op Mode (Switched)": ["coop-switched", "opponent", "coop"];
default: null;
});
}];
return names;
}
}
/**
* Class used for the Freeplay menu game modes.
*
* **NOTE**: `fields` is useable not just for boolean values!!
*/
class FreeplayGameMode {
public var modeName:String;
public var modeID:String;
public var scripts(default, set):Array<String>;
public var fields(default, set):Dynamic;
@:noCompletion function set_scripts(val:Array<String>) return scripts = (val == null ? [] : val);
@:noCompletion function set_fields(val:Dynamic) return fields = (val == null ? {} : val);
public function new(modeName:String, modeID:String, ?scripts:Array<String>, ?fields:Dynamic) {
this.modeName = modeName;
this.modeID = modeID;
this.scripts = scripts;
this.fields = fields;
}
public function toModifiers():Array<Dynamic> {
var fields = this.fields;
var modifiers:Array<Dynamic> = [];
for (field in {var _ = Reflect.fields(fields); _.sort(Reflect.compare); _;}) {
var val:Dynamic = Reflect.field(fields, field);
if (val != null && val != false) { // allow true, and floats/ints
modifiers.push(field);
modifiers.push(val);
}
}
return modifiers;
}
public static inline function generateDefault() return new FreeplayGameMode("Solo Mode", "solo");
public static inline function getGameModesFromSource(source:AssetSource = BOTH, useTxt:Bool = true):Array<FreeplayGameMode> {
var path = 'data/gamemodes';
var txt = Paths.txt('gamemodes/list');
var list = useTxt && Paths.assetsTree.existsSpecific(txt, "TEXT", source) ? CoolUtil.coolTextFile(txt) : Paths.getFolderContent(path, false, source);
return [for (file in list) {
if (useTxt) file += ".json";
else if (Path.extension(file) != "json") continue;
var meta:FreeplayGameMode = null;
try {
var data = CoolUtil.parseJson(Paths.file(path + "/" + file)); var id = data.modeID;
meta = new FreeplayGameMode(CoolUtil.getDefault(data.displayName, id), id, [Path.withoutExtension(file)].concat(CoolUtil.getDefault(data.scripts, [])), data.fields);
} catch(e) Logs.trace('Failed to load game mode metadata for $file ($path): ${Std.string(e)}', ERROR);
if (meta != null) meta;
}];
}
public static function get(useTxt:Bool = true):Array<FreeplayGameMode> {
var list:Array<FreeplayGameMode>;
switch(Flags.GAME_MODES_LIST_MOD_MODE) {
case 'prepend':
list = getGameModesFromSource(MODS, useTxt).concat(getGameModesFromSource(SOURCE, useTxt));
case 'append':
list = getGameModesFromSource(SOURCE, useTxt).concat(getGameModesFromSource(MODS, useTxt));
case 'override':
list = getGameModesFromSource(BOTH, useTxt);
default /*case 'oneOFtwo'*/:
if ((list = getGameModesFromSource(MODS, useTxt)).length == 0)
list = getGameModesFromSource(SOURCE, useTxt);
}
return list;
}
} }
class FreeplaySonglist { class FreeplaySonglist {
@@ -553,7 +646,9 @@ class FreeplaySonglist {
case 'append': case 'append':
songList.getSongsFromSource(SOURCE, useTxt); songList.getSongsFromSource(SOURCE, useTxt);
songList.getSongsFromSource(MODS, useTxt); songList.getSongsFromSource(MODS, useTxt);
default /*case 'override'*/: case 'override':
songList.getSongsFromSource(BOTH, useTxt);
default /*case 'oneOFtwo'*/:
if (songList.getSongsFromSource(MODS, useTxt)) if (songList.getSongsFromSource(MODS, useTxt))
songList.getSongsFromSource(SOURCE, useTxt); songList.getSongsFromSource(SOURCE, useTxt);
} }
+11 -4
View File
@@ -342,10 +342,17 @@ class StoryWeeklist {
public function new() {} public function new() {}
public function getWeeksFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) { public function getWeeksFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) {
var path:String = Paths.txt('weeks/weeks'); var weeksFound:Array<String> = null;
var weeksFound:Array<String> = useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source) ? CoolUtil.coolTextFile(path) : if (useTxt) {
[for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)]; var oldPath:String = Paths.txt('weeks/weeks');
var newPath:String = Paths.txt('config/weeks');
if (Paths.assetsTree.existsSpecific(newPath, "TEXT", source)) weeksFound = CoolUtil.coolTextFile(newPath);
else if (Paths.assetsTree.existsSpecific(oldPath, "TEXT", source)) {
Logs.warn("data/weeks/weeks.txt is deprecated and will be removed in the future. Please move the file to data/config/", DARKYELLOW, "StoryWeeklist");
weeksFound = CoolUtil.coolTextFile(oldPath);
}
}
if (weeksFound == null) weeksFound = [for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)];
if (weeksFound.length > 0) { if (weeksFound.length > 0) {
for (w in weeksFound) { for (w in weeksFound) {
var week = Week.loadWeek(w, loadCharactersData); var week = Week.loadWeek(w, loadCharactersData);