Compare commits

...

5 Commits

18 changed files with 230 additions and 118 deletions

View File

@@ -1 +1,4 @@
Ember Keeper is a platformer 2D game where you have to escape the outside temperatures, while collecting firewood to keep yourself warm while being out in the open. Ember Keeper is a platformer 2D game where you have to escape the outside temperatures, while collecting firewood to keep yourself warm while being out in the open.
The game is locked at 60 FPS since every interaction depends on that and i am lazy to introduce delta time.
The game can be speedrunned and there is an option for hitboxes as well.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="100" height="10" tilewidth="70" tileheight="70" infinite="0" nextlayerid="11" nextobjectid="20"> <map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="100" height="10" tilewidth="70" tileheight="70" infinite="0" nextlayerid="13" nextobjectid="21">
<tileset firstgid="1" source="../sheet.tsx"/> <tileset firstgid="1" source="../sheet.tsx"/>
<objectgroup id="3" name="spawn"> <objectgroup id="3" name="spawn">
<object id="3" name="player spawn" x="289" y="466.667"> <object id="3" name="player spawn" x="289" y="466.667">
@@ -10,10 +10,10 @@
<object id="8" name="Spikes hurt!" x="545.333" y="481.333"> <object id="8" name="Spikes hurt!" x="545.333" y="481.333">
<point/> <point/>
</object> </object>
<object id="9" name="&lt;- get this tree to get warmer" x="158.667" y="45"> <object id="9" name="&lt;- get this tree to get warmer" x="101.334" y="154">
<point/> <point/>
</object> </object>
<object id="10" name="Beware! Slippery tiles here." x="833.333" y="472"> <object id="10" name="Beware! Slippery tiles here." x="1129.33" y="426.667">
<point/> <point/>
</object> </object>
<object id="11" name="Beware! Slippery tiles here." x="636" y="200"> <object id="11" name="Beware! Slippery tiles here." x="636" y="200">
@@ -46,13 +46,30 @@
<object id="19" name="Parkour isnt hard, or is it..." x="4832" y="98"> <object id="19" name="Parkour isnt hard, or is it..." x="4832" y="98">
<point/> <point/>
</object> </object>
<object id="20" name="Easter egg below" x="902.333" y="535.333">
<point/>
</object>
</objectgroup> </objectgroup>
<layer id="11" name="checkpoints" width="100" height="10">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<layer id="1" name="walls" width="100" height="10"> <layer id="1" name="walls" width="100" height="10">
<data encoding="csv"> <data encoding="csv">
17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
17,17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
17,17,17,17,17,17,17,17,0,0,0,0,0,0,0,0,19,19,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,9,0,0,9,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,17,17,17,17,0,17,0,0,0,0,0,0,0,0,19,19,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
17,17,17,17,17,17,0,17,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 17,17,17,17,17,17,0,17,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,22,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,22,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -65,12 +82,12 @@
<data encoding="csv"> <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,47,47,47,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,46,47,47,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,60,60,60,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,60,60,60,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
60,0,43,43,45,45,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 60,0,43,43,45,45,0,0,60,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,46,46,47,47,47,47,47,48,48,48,48,46,46,46,46,46,48,48,48,48,48,48,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,46,46,47,47,47,47,47,48,48,48,48,46,46,46,46,46,48,48,48,48,48,48,46,46,46,46,47,47,47,47,0,0,0,0,
0,0,0,0,0,0,0,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,61,62,0,36,36,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,61,62,0,36,36,36,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,17,17,17,0,60,0,0,0,0,0,0,0,0,0,0,0,0,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,17,17,17,0,60,0,0,0,0,0,0,0,0,0,0,0,0,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data> </data>
@@ -78,11 +95,11 @@
<layer id="6" name="ice" width="100" height="10"> <layer id="6" name="ice" width="100" height="10">
<data encoding="csv"> <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,13,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 11,11,11,11,11,11,0,11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,11,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,19,19,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,19,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,19,19,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,19,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,11,11,0,0,0,0,0,11,11,0,0,0,0,0,11,11,11,0,0,0,0,0,0,0,0,0,19,17,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0,0,8,22,0,0,0,0,0,0,0,0,0,0,0,0,0,46,46,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,11,11,0,0,0,0,0,11,11,0,0,0,0,0,11,11,11,0,0,0,0,0,0,0,0,0,19,17,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,0,0,0,0,8,22,0,0,0,0,0,0,0,0,0,0,0,0,0,46,46,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -91,9 +108,9 @@
</layer> </layer>
<layer id="9" name="trees" width="100" height="10"> <layer id="9" name="trees" width="100" height="10">
<data encoding="csv"> <data encoding="csv">
0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

View File

@@ -1,7 +1,7 @@
import arcade, arcade.gui, json, time, os import arcade, arcade.gui, json, time, os
from utils.constants import FOLLOW_DECAY_CONST, GRAVITY, PLAYER_MOVEMENT_SPEED, PLAYER_JUMP_SPEED, GRID_PIXEL_SIZE, PLAYER_JUMP_COOLDOWN, LEFT_RIGHT_DIAGONAL_ID, RIGHT_LEFT_DIAGONAL_ID from utils.constants import FOLLOW_DECAY_CONST, GRAVITY, PLAYER_MOVEMENT_SPEED, PLAYER_JUMP_SPEED, GRID_PIXEL_SIZE, PLAYER_JUMP_COOLDOWN, LEFT_RIGHT_DIAGONAL_ID, RIGHT_LEFT_DIAGONAL_ID, AVAILABLE_LEVELS, RESTART_DELAY, button_style
from utils.preload import tilemaps, player_still_animation, player_jump_animation, player_walk_animation, freeze_sound, background_sound from utils.preload import tilemaps, player_still_animation, player_jump_animation, player_walk_animation, freeze_sound, background_sound, button_texture, button_hovered_texture
class Game(arcade.gui.UIView): class Game(arcade.gui.UIView):
def __init__(self, pypresence_client, level_num): def __init__(self, pypresence_client, level_num):
@@ -40,12 +40,25 @@ class Game(arcade.gui.UIView):
walls=[self.scene["walls"], self.scene["ice"]] walls=[self.scene["walls"], self.scene["ice"]]
) )
self.warmth = 100 self.camera_shake = arcade.camera.grips.ScreenShake2D(
self.camera_sprites.view_data,
max_amplitude=5,
acceleration_duration=0.2,
falloff_time=0.3,
shake_frequency=10.0,
)
self.warmth = 50
self.direction = "right" self.direction = "right"
self.last_jump = time.perf_counter() self.last_jump = time.perf_counter()
self.start = time.perf_counter() self.start = time.perf_counter()
self.trees = 0 self.trees = 0
self.collected_trees = [] self.collected_trees = []
self.checkpoints_hit = set()
self.restart_start = time.perf_counter()
self.restarting = False
self.won = False
self.won_time = None
self.level_texts = [] self.level_texts = []
@@ -61,15 +74,22 @@ class Game(arcade.gui.UIView):
with open("data.json", "r") as file: with open("data.json", "r") as file:
self.data = json.load(file) self.data = json.load(file)
else: else:
self.data = {} self.data = {
f"{level_num}_best_time": 9999
for level_num in range(AVAILABLE_LEVELS)
}
self.data.update({
f"{level_num}_tries": 0
for level_num in range(AVAILABLE_LEVELS)
})
self.high_score = self.data.get("high_score", 9999) self.best_time = self.data.get(f"{self.level_num}_best_time", 9999)
self.tries = self.data.get("tries", 1) self.tries = self.data.get("tries", 1)
if self.high_score == 9999: if self.best_time == 9999:
self.no_highscore = True self.no_besttime = True
self.high_score = 0 self.best_time = 0
else: else:
self.no_highscore = False self.no_besttime = False
self.scene.add_sprite("Player", self.player) self.scene.add_sprite("Player", self.player)
@@ -82,29 +102,44 @@ class Game(arcade.gui.UIView):
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.info_label = self.anchor.add(arcade.gui.UILabel(text=f"Time took: 0s High Score: {self.high_score}s Trees: 0 Tries: {self.tries}", font_size=20), anchor_x="center", anchor_y="top") self.info_label = self.anchor.add(arcade.gui.UILabel(text=f"Time took: 0s Best Time: {self.best_time}s Trees: 0 Tries: {self.tries}", font_size=20), anchor_x="center", anchor_y="top")
def reset(self, reached_end=False): def reset(self, reached_end=False):
self.warmth = 100 self.camera_shake.start()
self.trees = 0
self.player.change_x, self.player.change_y = 0, 0
self.player.position = self.spawn_position
self.start = time.perf_counter()
self.tries += 1
for collected_tree in self.collected_trees: if not reached_end:
self.scene["trees"].append(collected_tree) self.warmth = 50
self.trees = 0
self.player.change_x, self.player.change_y = 0, 0
self.player.position = self.spawn_position
self.tries += 1
self.restart_start = time.perf_counter()
self.restarting = True
if self.no_besttime:
self.best_time = 9999
else:
if self.no_besttime:
self.no_besttime = False
self.anchor.add(arcade.gui.UILabel(text=f"Level Complete! Time: {self.won_time}s\nBest Time: {self.best_time}s", multiline=True, font_size=30), anchor_x="center", anchor_y="center")
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
self.won = True
if not self.checkpoints_hit:
self.start = time.perf_counter()
for level_text in self.level_texts: for level_text in self.level_texts:
level_text.text = level_text.original_text level_text.text = level_text.original_text
self.collected_trees = []
if not reached_end and self.no_highscore:
self.high_score = 9999
elif reached_end and self.no_highscore:
self.no_highscore = False
self.update_data_file() self.update_data_file()
def create_scene(self) -> arcade.Scene: def create_scene(self) -> arcade.Scene:
@@ -120,15 +155,24 @@ class Game(arcade.gui.UIView):
def on_draw(self): def on_draw(self):
self.clear() self.clear()
with self.camera_sprites.activate(): self.camera_shake.update_camera()
self.scene.draw()
for level_text in self.level_texts: if not self.won:
level_text.draw() with self.camera_sprites.activate():
self.scene.draw()
if self.settings.get("hitboxes", False):
self.scene.draw_hit_boxes(arcade.color.RED, 2)
for level_text in self.level_texts:
level_text.draw()
arcade.draw_lbwh_rectangle_filled(self.window.width / 4, 0, (self.window.width / 2), self.window.height / 20, arcade.color.SKY_BLUE)
arcade.draw_lbwh_rectangle_filled(self.window.width / 4, 0, (self.window.width / 2) * (self.warmth / 100), self.window.height / 20, arcade.color.RED)
self.camera_shake.readjust_camera()
self.ui.draw() self.ui.draw()
arcade.draw_lbwh_rectangle_filled(self.window.width / 4, 0, (self.window.width / 2), self.window.height / 20, arcade.color.SKY_BLUE)
arcade.draw_lbwh_rectangle_filled(self.window.width / 4, 0, (self.window.width / 2) * (self.warmth / 100), self.window.height / 20, arcade.color.RED)
def center_camera_to_player(self): def center_camera_to_player(self):
self.camera_sprites.position = arcade.math.smerp_2d( self.camera_sprites.position = arcade.math.smerp_2d(
@@ -150,35 +194,58 @@ class Game(arcade.gui.UIView):
self.player.animation = animation self.player.animation = animation
def on_update(self, delta_time: float): def on_update(self, delta_time: float):
if self.won:
return
if self.restarting:
if time.perf_counter() - self.restart_start >= RESTART_DELAY:
self.restarting = False
else:
return
hit_list = self.physics_engine.update() hit_list = self.physics_engine.update()
self.center_camera_to_player() self.center_camera_to_player()
self.camera_shake.update(delta_time)
if self.player.collides_with_list(self.scene["end"]): if self.player.collides_with_list(self.scene["end"]):
end_time = round(time.perf_counter() - self.start, 2) end_time = round(time.perf_counter() - self.start, 4)
if self.no_highscore or end_time < self.high_score: if self.no_besttime or end_time < self.best_time:
self.high_score = end_time self.best_time = end_time
self.won_time = end_time
self.reset(True) self.reset(True)
return return
if self.no_highscore: if self.no_besttime:
self.high_score = round(time.perf_counter() - self.start, 2) self.best_time = round(time.perf_counter() - self.start, 4)
self.info_label.text = f"Time took: {round(time.perf_counter() - self.start, 2)}s High Score: {self.high_score}s Trees: {self.trees} Tries: {self.tries}" self.info_label.text = f"Time took: {round(time.perf_counter() - self.start, 4)}s Best Time: {self.best_time}s Trees: {self.trees} Tries: {self.tries}"
if self.warmth <= 0 or self.player.collides_with_list(self.scene["spikes"]) or self.player.center_x < 0 or self.player.center_x > tilemaps[self.level_num].width * GRID_PIXEL_SIZE or self.player.center_y < 0: if self.warmth <= 0 or self.player.collides_with_list(self.scene["spikes"]) or self.player.center_y < 0:
self.reset() self.reset()
tree_collisions = self.player.collides_with_list(self.scene["trees"]) if self.player.center_x + self.player.width / 2 < 0:
self.camera_shake.start()
self.player.center_x = self.player.width / 2
if tree_collisions: if self.player.center_x - self.player.width / 2 > tilemaps[self.level_num].width * GRID_PIXEL_SIZE:
for tree in tree_collisions: self.camera_shake.start()
self.trees += 1 self.player.center_x = (tilemaps[self.level_num].width * GRID_PIXEL_SIZE) - self.player.width / 2
self.collected_trees.append(tree)
self.scene["trees"].remove(tree)
self.warmth = self.clamp(self.warmth + 35, 0, 100) for tree in self.player.collides_with_list(self.scene["trees"]):
self.trees += 1
self.collected_trees.append(tree)
self.scene["trees"].remove(tree)
self.warmth = self.clamp(self.warmth + 35, 0, 100)
for checkpoint in self.player.collides_with_list(self.scene["checkpoints"]):
if checkpoint not in self.checkpoints_hit:
self.scene["checkpoints"].remove(checkpoint)
self.checkpoints_hit.add(checkpoint)
self.spawn_position = checkpoint.position + arcade.math.Vec2(-GRID_PIXEL_SIZE / 4, GRID_PIXEL_SIZE)
moved = False moved = False
ice_touch = any([ice_sprite in hit_list for ice_sprite in self.scene["ice"]]) and self.physics_engine.can_jump() ice_touch = any([ice_sprite in hit_list for ice_sprite in self.scene["ice"]]) and self.physics_engine.can_jump()
@@ -204,9 +271,9 @@ class Game(arcade.gui.UIView):
on_right_left_diagonal = any([True for hit in hit_list if hit in self.right_left_diagonal_sprites]) on_right_left_diagonal = any([True for hit in hit_list if hit in self.right_left_diagonal_sprites])
if on_left_right_diagonal or (self.direction == "right" and not on_right_left_diagonal): if on_left_right_diagonal or (self.direction == "right" and not on_right_left_diagonal):
self.player.change_x = self.clamp(self.player.change_x * 0.75, PLAYER_MOVEMENT_SPEED * 0.4, PLAYER_MOVEMENT_SPEED) self.player.change_x = self.clamp(self.player.change_x * 0.75, PLAYER_MOVEMENT_SPEED * 0.3, PLAYER_MOVEMENT_SPEED)
else: else:
self.player.change_x = self.clamp(self.player.change_x * 0.75, -PLAYER_MOVEMENT_SPEED, -PLAYER_MOVEMENT_SPEED * 0.4) self.player.change_x = self.clamp(self.player.change_x * 0.75, -PLAYER_MOVEMENT_SPEED, -PLAYER_MOVEMENT_SPEED * 0.3)
else: else:
self.player.change_x = 0 self.player.change_x = 0
@@ -216,6 +283,7 @@ class Game(arcade.gui.UIView):
self.warmth = self.clamp(self.warmth - 0.15, 0, 100) self.warmth = self.clamp(self.warmth - 0.15, 0, 100)
if self.warmth < 40: if self.warmth < 40:
self.camera_shake.start()
if self.settings.get("sfx", True) and not self.freeze_player.playing: if self.settings.get("sfx", True) and not self.freeze_player.playing:
self.freeze_player.play() self.freeze_player.play()
else: else:
@@ -224,13 +292,14 @@ class Game(arcade.gui.UIView):
if self.player.change_y > 0: if self.player.change_y > 0:
self.change_player_animation(player_jump_animation) self.change_player_animation(player_jump_animation)
elif abs(self.player.change_x) > PLAYER_MOVEMENT_SPEED * 0.4: elif abs(self.player.change_x) > PLAYER_MOVEMENT_SPEED * 0.3:
self.change_player_animation(player_walk_animation) self.change_player_animation(player_walk_animation)
else: else:
self.change_player_animation(player_still_animation) self.change_player_animation(player_still_animation)
for level_text in self.level_texts: for level_text in self.level_texts:
if level_text.change_to_when_hit and self.player.rect.intersection(level_text.rect): if level_text.change_to_when_hit and self.player.rect.intersection(level_text.rect):
self.camera_shake.start()
level_text.text = level_text.change_to_when_hit level_text.text = level_text.change_to_when_hit
self.player.update_animation() self.player.update_animation()
@@ -238,11 +307,14 @@ class Game(arcade.gui.UIView):
def update_data_file(self): def update_data_file(self):
with open("data.json", "w") as file: with open("data.json", "w") as file:
file.write(json.dumps({ file.write(json.dumps({
"high_score": self.high_score, f"{self.level_num}_best_time": self.best_time,
"tries": self.tries f"{self.level_num}_tries": self.tries
}, indent=4)) }, indent=4))
def on_key_press(self, symbol, modifiers): def on_key_press(self, symbol, modifiers):
if symbol == arcade.key.ESCAPE: if symbol == arcade.key.ESCAPE:
from menus.main import Main self.main_exit()
self.window.show_view(Main(self.pypresence_client))
def main_exit(self):
from menus.main import Main
self.window.show_view(Main(self.pypresence_client))

View File

@@ -55,9 +55,16 @@ class Main(arcade.gui.UIView):
self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.play_button.on_click = lambda event: self.play() self.play_button.on_click = lambda event: self.play()
self.statistics_button = self.box.add(arcade.gui.UITextureButton(text="Statistics", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.statistics_button.on_click = lambda event: self.statistics()
self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.settings_button.on_click = lambda event: self.settings() self.settings_button.on_click = lambda event: self.settings()
def statistics(self):
from menus.statistics import Statistics
self.window.show_view(Statistics(self.pypresence_client))
def play(self): def play(self):
from menus.level_selector import LevelSelector from menus.level_selector import LevelSelector
self.window.show_view(LevelSelector(self.pypresence_client)) self.window.show_view(LevelSelector(self.pypresence_client))

View File

@@ -147,22 +147,8 @@ class Settings(arcade.gui.UIView):
width, height = map(int, self.settings_dict['resolution'].split('x')) width, height = map(int, self.settings_dict['resolution'].split('x'))
self.window.set_size(width, height) self.window.set_size(width, height)
if self.settings_dict['vsync']: self.window.set_update_rate(1 / 60)
self.window.set_vsync(True) self.window.set_draw_rate(1 / 60)
display_mode = self.window.display.get_default_screen().get_mode()
refresh_rate = display_mode.rate
self.window.set_update_rate(1 / refresh_rate)
self.window.set_draw_rate(1 / refresh_rate)
elif not self.settings_dict['fps_limit'] == 0:
self.window.set_vsync(False)
self.window.set_update_rate(1 / self.settings_dict['fps_limit'])
self.window.set_draw_rate(1 / self.settings_dict['fps_limit'])
else:
self.window.set_vsync(False)
self.window.set_update_rate(1 / 99999999)
self.window.set_draw_rate(1 / 99999999)
if self.settings_dict['discord_rpc']: if self.settings_dict['discord_rpc']:
if isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session. if isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session.

52
menus/statistics.py Normal file
View File

@@ -0,0 +1,52 @@
import arcade, arcade.gui, json, os
from utils.constants import AVAILABLE_LEVELS, button_style
from utils.preload import button_texture, button_hovered_texture
class Statistics(arcade.gui.UIView):
def __init__(self, pypresence_client):
super().__init__()
self.pypresence_client = pypresence_client
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
self.box = self.anchor.add(arcade.gui.UIBoxLayout(align="left"), anchor_x="center", anchor_y="top")
if os.path.exists("data.json"):
with open("data.json", "r") as file:
self.data = json.load(file)
else:
self.data = {
f"{level_num}_best_time": 9999
for level_num in range(AVAILABLE_LEVELS)
}
self.data.update({
f"{level_num}_tries": 0
for level_num in range(AVAILABLE_LEVELS)
})
self.total_tries = sum([self.data[f"{level_num}_tries"] for level_num in range(AVAILABLE_LEVELS)])
def on_show_view(self):
super().on_show_view()
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
self.box.add(arcade.gui.UILabel("Statistics", font_size=40))
self.box.add(arcade.gui.UISpace(height=self.window.height / 15))
for level_num in range(AVAILABLE_LEVELS):
self.box.add(arcade.gui.UILabel(f"Level {level_num + 1}", font_size=32))
self.box.add(arcade.gui.UILabel(f"High Score: {self.data[f'{level_num}_best_time']}", font_size=24))
self.box.add(arcade.gui.UILabel(f"Tries: {self.data[f'{level_num}_tries']}", font_size=24))
self.box.add(arcade.gui.UISpace(height=self.window.height / 15))
self.box.add(arcade.gui.UILabel(f"Total", font_size=32))
self.box.add(arcade.gui.UILabel(f"Total Tries: {self.total_tries}", font_size=24))
def main_exit(self):
from menus.main import Main
self.window.show_view(Main(self.pypresence_client))

View File

@@ -1,12 +1,12 @@
# This file was autogenerated by uv via the following command: # This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt # uv pip compile pyproject.toml -o requirements.txt
arcade==3.2.0 arcade==3.3.3
# via ember-keeper (pyproject.toml) # via ember-keeper (pyproject.toml)
attrs==25.3.0 attrs==25.3.0
# via pytiled-parser # via pytiled-parser
cffi==1.17.1 cffi==1.17.1
# via pymunk # via pymunk
pillow==11.0.0 pillow==11.3.0
# via arcade # via arcade
pycparser==2.22 pycparser==2.22
# via cffi # via cffi

27
run.py
View File

@@ -14,13 +14,10 @@ pyglet.font.add_directory(os.path.join(script_dir, 'assets', 'fonts'))
from utils.utils import get_closest_resolution, print_debug_info, on_exception from utils.utils import get_closest_resolution, print_debug_info, on_exception
from utils.constants import log_dir, menu_background_color from utils.constants import log_dir, menu_background_color
from menus.main import Main from menus.main import Main
# from utils.preload import theme_sound # needed for preload
from arcade.experimental.controller_window import ControllerWindow from arcade.experimental.controller_window import ControllerWindow
sys.excepthook = on_exception sys.excepthook = on_exception
# __builtins__.print = lambda *args, **kwargs: logging.debug(" ".join(map(str, args)))
if not log_dir in os.listdir(): if not log_dir in os.listdir():
os.makedirs(log_dir) os.makedirs(log_dir)
@@ -58,7 +55,6 @@ if os.path.exists('settings.json'):
fullscreen = settings['window_mode'] == 'Fullscreen' fullscreen = settings['window_mode'] == 'Fullscreen'
style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT
vsync = settings['vsync'] vsync = settings['vsync']
fps_limit = settings['fps_limit']
else: else:
resolution = get_closest_resolution() resolution = get_closest_resolution()
antialiasing = 4 antialiasing = 4
@@ -73,7 +69,6 @@ else:
fullscreen = False fullscreen = False
style = arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_DEFAULT
vsync = True vsync = True
fps_limit = 0
settings = { settings = {
"music": True, "music": True,
@@ -81,38 +76,20 @@ else:
"resolution": f"{resolution[0]}x{resolution[1]}", "resolution": f"{resolution[0]}x{resolution[1]}",
"antialiasing": "4x MSAA", "antialiasing": "4x MSAA",
"window_mode": "Windowed", "window_mode": "Windowed",
"vsync": True,
"fps_limit": 60,
"discord_rpc": True "discord_rpc": True
} }
with open("settings.json", "w") as file: with open("settings.json", "w") as file:
file.write(json.dumps(settings)) file.write(json.dumps(settings))
# if settings.get("music", True):
# theme_sound.play(volume=settings.get("music_volume", 50) / 100, loop=True)
try: try:
window = ControllerWindow(width=resolution[0], height=resolution[1], title='Ember Keeper', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False) window = ControllerWindow(width=resolution[0], height=resolution[1], title='Ember Keeper', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
except (FileNotFoundError, PermissionError) as e: except (FileNotFoundError, PermissionError) as e:
logging.warning(f"Controller support unavailable: {e}. Falling back to regular window.") logging.warning(f"Controller support unavailable: {e}. Falling back to regular window.")
window = arcade.Window(width=resolution[0], height=resolution[1], title='Ember Keeper', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False) window = arcade.Window(width=resolution[0], height=resolution[1], title='Ember Keeper', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
if vsync: window.set_update_rate(1 / 60)
window.set_vsync(True) window.set_draw_rate(1 / 60)
display_mode = window.display.get_default_screen().get_mode()
if display_mode:
refresh_rate = display_mode.rate
else:
refresh_rate = 60
window.set_update_rate(1 / refresh_rate)
window.set_draw_rate(1 / refresh_rate)
elif not fps_limit == 0:
window.set_update_rate(1 / fps_limit)
window.set_draw_rate(1 / fps_limit)
else:
window.set_update_rate(1 / 99999999)
window.set_draw_rate(1 / 99999999)
arcade.set_background_color(menu_background_color) arcade.set_background_color(menu_background_color)

View File

@@ -9,6 +9,7 @@ PLAYER_JUMP_COOLDOWN = 0.25
PLAYER_MOVEMENT_SPEED = 10 PLAYER_MOVEMENT_SPEED = 10
PLAYER_JUMP_SPEED = 28 PLAYER_JUMP_SPEED = 28
GRID_PIXEL_SIZE = 70 GRID_PIXEL_SIZE = 70
RESTART_DELAY = 0.1
LEFT_RIGHT_DIAGONAL_ID = 6 LEFT_RIGHT_DIAGONAL_ID = 6
RIGHT_LEFT_DIAGONAL_ID = 7 RIGHT_LEFT_DIAGONAL_ID = 7
@@ -37,17 +38,14 @@ settings = {
"Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"}, "Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"},
"Resolution": {"type": "option", "options": ["1366x768", "1440x900", "1600x900", "1920x1080", "2560x1440", "3840x2160"], "config_key": "resolution"}, "Resolution": {"type": "option", "options": ["1366x768", "1440x900", "1600x900", "1920x1080", "2560x1440", "3840x2160"], "config_key": "resolution"},
"Anti-Aliasing": {"type": "option", "options": ["None", "2x MSAA", "4x MSAA", "8x MSAA", "16x MSAA"], "config_key": "anti_aliasing", "default": "4x MSAA"}, "Anti-Aliasing": {"type": "option", "options": ["None", "2x MSAA", "4x MSAA", "8x MSAA", "16x MSAA"], "config_key": "anti_aliasing", "default": "4x MSAA"},
"VSync": {"type": "bool", "config_key": "vsync", "default": True},
"FPS Limit": {"type": "slider", "min": 0, "max": 480, "config_key": "fps_limit", "default": 60},
}, },
"Sound": { "Sound": {
"Music": {"type": "bool", "config_key": "music", "default": True},
"SFX": {"type": "bool", "config_key": "sfx", "default": True}, "SFX": {"type": "bool", "config_key": "sfx", "default": True},
"Music Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "music_volume", "default": 50},
"SFX Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "sfx_volume", "default": 50}, "SFX Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "sfx_volume", "default": 50},
}, },
"Miscellaneous": { "Miscellaneous": {
"Discord RPC": {"type": "bool", "config_key": "discord_rpc", "default": True}, "Discord RPC": {"type": "bool", "config_key": "discord_rpc", "default": True},
"Hitboxes": {"type": "bool", "config_key": "hitboxes", "default": False},
}, },
"Credits": {} "Credits": {}
} }