Refactor tilemap layers and collision system to consolidate into single floor layer with improved ladder logic and button/barrier interactions

This commit is contained in:
Brian Fertig 2026-02-23 20:32:03 -07:00
parent be160ed954
commit ab56b161fa
9 changed files with 202 additions and 288 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

View File

@ -8,26 +8,48 @@
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 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, 11, 11, 11, 11, 11, 42, 42, 42, 42, 42, 18, 1, 1, 2, 0,
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, 32, 0, 18, 0, 0, 0, 0,
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, 32, 0, 18, 0, 0, 0, 0,
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, 32, 0, 18, 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, 10, 0, 21, 32, 0, 18, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483650, 1, 18, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 21, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 21, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 21, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 21, 0, 19, 0, 0, 32, 0, 0, 0, 0, 0,
3, 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 4, 5, 6, 7, 3, 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 4, 5, 4, 5,
13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 17], 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15],
"height":16, "height":16,
"width":16, "width":16,
"x":0, "x":0,
"y":0 "y":0
}, },
{ {
"data":[23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 27, "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 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,
18, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 5, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14, 15, 14, 15, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":16,
"y":0
},
{
"data":[23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 25,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 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,126 +69,40 @@
"width":16, "width":16,
"x":0, "x":0,
"y":16 "y":16
},
{
"data":[24, 25, 24, 25, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":16,
"y":16
}], }],
"height":48, "height":32,
"id":1, "id":1,
"name":"floor", "name":"floor",
"opacity":1, "opacity":1,
"startx":-16, "startx":0,
"starty":-16, "starty":0,
"type":"tilelayer", "type":"tilelayer",
"visible":true, "visible":true,
"width":32, "width":32,
"x":0, "x":0,
"y":0 "y":0
},
{
"chunks":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483650, 1, 1, 1, 1, 1, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2147483650, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 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, 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, 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],
"height":16,
"width":16,
"x":0,
"y":0
}],
"height":16,
"id":2,
"name":"platforms",
"opacity":1,
"startx":0,
"starty":0,
"type":"tilelayer",
"visible":true,
"width":16,
"x":0,
"y":0
},
{
"chunks":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 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, 18, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0,
0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 18, 8, 0, 0, 0, 0,
0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0,
0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0,
0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0,
0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0,
0, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":0,
"y":0
}],
"height":16,
"id":3,
"name":"ladders",
"opacity":1,
"startx":0,
"starty":0,
"type":"tilelayer",
"visible":true,
"width":16,
"x":0,
"y":0
},
{
"chunks":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 9, 0, 32, 0, 0, 0, 0, 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, 21, 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, 21, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":16,
"width":16,
"x":0,
"y":0
}],
"height":16,
"id":4,
"name":"barriers",
"opacity":1,
"startx":0,
"starty":0,
"type":"tilelayer",
"visible":true,
"width":16,
"x":0,
"y":0
}], }],
"nextlayerid":5, "nextlayerid":5,
"nextobjectid":1, "nextobjectid":1,

View File

@ -9,26 +9,31 @@
<tile id="2"> <tile id="2">
<properties> <properties>
<property name="floor" type="bool" value="true"/> <property name="floor" type="bool" value="true"/>
<property name="solid" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="3"> <tile id="3">
<properties> <properties>
<property name="floor" type="bool" value="true"/> <property name="floor" type="bool" value="true"/>
<property name="solid" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="4"> <tile id="4">
<properties> <properties>
<property name="floor" type="bool" value="true"/> <property name="floor" type="bool" value="true"/>
<property name="solid" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="5"> <tile id="5">
<properties> <properties>
<property name="floor" type="bool" value="true"/> <property name="floor" type="bool" value="true"/>
<property name="solid" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="6"> <tile id="6">
<properties> <properties>
<property name="floor" type="bool" value="true"/> <property name="floor" type="bool" value="true"/>
<property name="solid" type="bool" value="true"/>
</properties> </properties>
</tile> </tile>
<tile id="7"> <tile id="7">

View File

@ -10,6 +10,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,8,0,0,0,0,
0,11,11,11,11,11,42,42,42,42,42,18,1,1,2,0,
0,0,0,0,0,0,0,0,21,32,0,18,0,0,0,0,
0,0,0,0,0,0,0,0,21,32,0,18,0,0,0,0,
0,0,0,0,0,0,0,0,21,32,0,18,0,0,0,0,
0,0,8,0,0,0,10,0,21,32,0,18,0,0,0,0,
2147483650,1,18,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,18,0,0,21,0,0,0,0,32,0,0,0,0,0,
0,0,18,0,0,21,0,0,0,0,32,0,0,0,0,0,
0,0,18,0,0,21,0,0,0,0,32,0,0,0,0,0,
0,0,18,0,0,21,0,19,0,0,32,0,0,0,0,0,
3,4,5,6,4,5,6,4,5,6,4,5,4,5,4,5,
13,14,15,15,15,15,15,15,15,15,15,15,15,15,14,15
</chunk>
<chunk x="16" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -18,14 +33,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, 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, 18,1,1,1,1,2,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
3,4,5,6,4,5,6,4,5,6,4,5,4,5,6,7, 18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
13,14,15,15,15,15,15,15,15,15,15,15,15,15,16,17 18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
18,0,0,29,0,0,0,0,0,0,0,0,0,0,0,0,
4,5,4,5,6,7,0,0,0,0,0,0,0,0,0,0,
14,15,14,15,16,17,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>
<chunk x="0" y="16" width="16" height="16"> <chunk x="0" y="16" width="16" height="16">
23,24,25,25,25,25,25,25,25,25,25,25,25,25,26,27, 23,24,25,25,25,25,25,25,25,25,25,25,25,25,24,25,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -42,69 +60,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
</chunk> </chunk>
</data> <chunk x="16" y="16" width="16" height="16">
</layer> 24,25,24,25,26,27,0,0,0,0,0,0,0,0,0,0,
<layer id="2" name="platforms" width="30" height="20">
<data encoding="csv">
<chunk x="0" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,2147483650,1,1,1,1,1,2,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2147483650,1,1,1,1,1,1,1,1,1,1,1,2,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,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,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
</chunk>
</data>
</layer>
<layer id="3" name="ladders" width="30" height="20">
<data encoding="csv">
<chunk x="0" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,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,18,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,
0,8,0,0,0,0,0,0,0,0,18,8,0,0,0,0,
0,18,0,0,0,0,0,0,0,0,0,18,0,0,0,0,
0,18,0,0,0,0,0,0,0,0,0,18,0,0,0,0,
0,18,0,0,0,0,0,0,0,0,0,18,0,0,0,0,
0,18,0,0,0,0,0,0,0,0,0,18,0,0,0,0,
0,18,0,0,0,0,0,0,0,0,0,18,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
</data>
</layer>
<layer id="4" name="barriers" width="30" height="20">
<data encoding="csv">
<chunk x="0" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,9,0,32,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,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,21,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,21,0,20,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk> </chunk>

View File

@ -5,11 +5,11 @@ export default class Player {
* @param {Phaser.Scene} scene * @param {Phaser.Scene} scene
* @param {number} x World pixel x (center) * @param {number} x World pixel x (center)
* @param {number} y World pixel y (center) * @param {number} y World pixel y (center)
* @param {Phaser.Tilemaps.TilemapLayer} laddersLayer * @param {Phaser.Tilemaps.TilemapLayer} floorLayer Single combined floor layer
*/ */
constructor(scene, x, y, laddersLayer) { constructor(scene, x, y, floorLayer) {
this._scene = scene; this._scene = scene;
this._laddersLayer = laddersLayer; this._floorLayer = floorLayer;
this.lives = 3; this.lives = 3;
this.onLadder = false; this.onLadder = false;
@ -35,6 +35,12 @@ export default class Player {
frameRate: 1, frameRate: 1,
repeat: 0, repeat: 0,
}); });
scene.anims.create({
key: 'climb',
frames: scene.anims.generateFrameNumbers('player', { start: 2, end: 3 }),
frameRate: 8,
repeat: -1,
});
this._cursors = scene.input.keyboard.createCursorKeys(); this._cursors = scene.input.keyboard.createCursorKeys();
this._jumpKey = this._cursors.up; this._jumpKey = this._cursors.up;
@ -55,10 +61,10 @@ export default class Player {
const body = sprite.body; const body = sprite.body;
// Tile at player center (for body-of-ladder detection) // Tile at player center (for body-of-ladder detection)
const centerTile = this._laddersLayer.getTileAtWorldXY(sprite.x, sprite.y); const centerTile = this._floorLayer.getTileAtWorldXY(sprite.x, sprite.y);
// Tile just below body bottom (body bottom = sprite.y + 120; +2 puts us inside // Tile just below body bottom (body bottom = sprite.y + 120; +2 puts us inside
// the tile when standing on it, since physics places body bottom at tile top) // the tile when standing on it, since physics places body bottom at tile top)
const feetTile = this._laddersLayer.getTileAtWorldXY(sprite.x, sprite.y + 122); const feetTile = this._floorLayer.getTileAtWorldXY(sprite.x, sprite.y + 122);
const centerIsLadder = centerTile && TilePropertyHelper.isLadder(centerTile.index); const centerIsLadder = centerTile && TilePropertyHelper.isLadder(centerTile.index);
const feetIsLadderTop = feetTile && TilePropertyHelper.isLadderTop(feetTile.index); const feetIsLadderTop = feetTile && TilePropertyHelper.isLadderTop(feetTile.index);
@ -152,8 +158,12 @@ export default class Player {
const sprite = this._sprite; const sprite = this._sprite;
if (this.onLadder) { if (this.onLadder) {
sprite.anims.stop(); if (sprite.body.velocity.y !== 0) {
sprite.setFrame(0); sprite.anims.play('climb', true);
} else {
sprite.anims.stop();
sprite.setFrame(2);
}
return; return;
} }

View File

@ -4,6 +4,8 @@ import TilePropertyHelper, {
FLOOR_GIDS, FLOOR_GIDS,
SOLID_FLOOR_GID, SOLID_FLOOR_GID,
LADDER_TOP_GID, LADDER_TOP_GID,
BUTTON_GIDS,
BARRIER_SOLID_GIDS,
} from '../systems/TilePropertyHelper.js'; } from '../systems/TilePropertyHelper.js';
export default class Level1Scene extends Phaser.Scene { export default class Level1Scene extends Phaser.Scene {
@ -78,25 +80,19 @@ export default class Level1Scene extends Phaser.Scene {
// If tiles render blank, verify with: console.log(map.tilesets[0].name) // If tiles render blank, verify with: console.log(map.tilesets[0].name)
const tileset = map.addTilesetImage('platforms', 'platforms', 128, 128, 0, 0); const tileset = map.addTilesetImage('platforms', 'platforms', 128, 128, 0, 0);
// Single combined layer — all tile types live here
this._floorLayer = map.createLayer('floor', tileset, 0, 0); this._floorLayer = map.createLayer('floor', tileset, 0, 0);
this._platformsLayer = map.createLayer('platforms', tileset, 0, 0);
this._laddersLayer = map.createLayer('ladders', tileset, 0, 0);
this._barriersLayer = map.createLayer('barriers', tileset, 0, 0);
// ------------------------- // Only register collision on GIDs that should ever block the player.
// Tile collision setup // Omitting ladder rungs and barrier_open tiles prevents Phaser's face-culling
// ------------------------- // from stripping the top face off solid tiles that sit adjacent to those tiles.
this._floorLayer.setCollision([
// Floor layer: one-way — only collide when player falls down ...FLOOR_GIDS, // one-way floor surfaces
this._floorLayer.setCollision(FLOOR_GIDS); SOLID_FLOOR_GID, // impassable wall tile
LADDER_TOP_GID, // ladder top (one-way surface)
// Platforms layer: floor tiles (one-way) + the one solid wall tile ...BUTTON_GIDS, // buttons (both pressed/unpressed) — always act as floor
this._platformsLayer.setCollision([...FLOOR_GIDS, SOLID_FLOOR_GID]); ...BARRIER_SOLID_GIDS, // solid barriers — ButtonBarrierSystem manages toggling
]);
// Ladder layer: only the top rung acts as a one-way surface
this._laddersLayer.setCollision([LADDER_TOP_GID]);
// Barrier layer: initial collision set by ButtonBarrierSystem.init()
// ------------------------- // -------------------------
// Player // Player
@ -104,57 +100,50 @@ export default class Level1Scene extends Phaser.Scene {
// Spawn at tile (3, 11) — center of tile in world pixels // Spawn at tile (3, 11) — center of tile in world pixels
const spawnX = 3 * 128 + 64; // 448 const spawnX = 3 * 128 + 64; // 448
const spawnY = 11 * 128 + 64; // 1472 const spawnY = 11 * 128 + 64; // 1472
this._player = new Player(this, spawnX, spawnY, this._laddersLayer); this._player = new Player(this, spawnX, spawnY, this._floorLayer);
const playerSprite = this._player.sprite; const playerSprite = this._player.sprite;
// ------------------------- // -------------------------
// Button / Barrier system // Button / Barrier system
// ------------------------- // -------------------------
this._bbSystem = new ButtonBarrierSystem(this, this._barriersLayer); this._bbSystem = new ButtonBarrierSystem(this, this._floorLayer);
this._bbSystem.init(); this._bbSystem.init();
// ------------------------- // -------------------------
// Physics colliders // Physics collider — single layer, all tile types
// ------------------------- // -------------------------
// Floor layer: one-way (pass through from below, land from above)
this.physics.add.collider( this.physics.add.collider(
playerSprite, playerSprite,
this._floorLayer, this._floorLayer,
null, null,
(player, tile) => player.body.velocity.y >= 0,
this
);
// Platforms layer: one-way for floor tiles; full collision for solid wall tile
this.physics.add.collider(
playerSprite,
this._platformsLayer,
null,
(player, tile) => {
if (tile.index === SOLID_FLOOR_GID) return true;
return player.body.velocity.y >= 0;
},
this
);
// Ladder layer: top rung as one-way floor; disabled while actively climbing
this.physics.add.collider(
playerSprite,
this._laddersLayer,
null,
(player, tile) => !this._player.onLadder && player.body.velocity.y >= 0,
this
);
// Barriers layer: dynamic — processCallback checks current solid state per tile
this.physics.add.collider(
playerSprite,
this._barriersLayer,
null,
(player, tile) => { (player, tile) => {
const props = TilePropertyHelper.getPropsByGID(tile.index); const props = TilePropertyHelper.getPropsByGID(tile.index);
return !!(props && props.solid); if (!props || Object.keys(props).length === 0) return false;
// Ladder rungs: no collision; top rung: one-way surface (unless climbing)
if (props.ladder) {
if (!props.top) return false;
// Also require the player's feet to be near the tile's top surface.
// Without this, a player walking horizontally at a row below the
// ladder top has velocity.y=0 (>= 0 passes) and gets blocked sideways.
return !this._player.onLadder
&& player.body.velocity.y >= 0
&& player.body.bottom <= tile.pixelY + 32;
}
// Barriers: collision determined by current tile's solid state
if (props.barrier) return !!(props.solid);
// Buttons: unpressed = one-way floor; pressed = passable
if (props.button) return !props.pressed && player.body.velocity.y >= 0;
// Floor tiles: solid floor is impassable all sides; regular floor is one-way
if (props.floor) {
if (props.solid) return true;
return player.body.velocity.y >= 0;
}
return false;
}, },
this this
); );
@ -216,7 +205,7 @@ export default class Level1Scene extends Phaser.Scene {
const feetX = sprite.x; const feetX = sprite.x;
const feetY = sprite.y + 120; // approx bottom of physics body const feetY = sprite.y + 120; // approx bottom of physics body
const tile = this._barriersLayer.getTileAtWorldXY(feetX, feetY + 4); const tile = this._floorLayer.getTileAtWorldXY(feetX, feetY + 4);
if (!tile || tile.index <= 0) return; if (!tile || tile.index <= 0) return;
if (this._bbSystem.isButtonTile(tile.index)) { if (this._bbSystem.isButtonTile(tile.index)) {

View File

@ -1,13 +1,13 @@
import TilePropertyHelper, { COLOR_TILES } from './TilePropertyHelper.js'; import TilePropertyHelper, { COLOR_TILES, BARRIER_SOLID_GIDS, BARRIER_OPEN_GIDS, BUTTON_GIDS, BUTTON_ON_GIDS } from './TilePropertyHelper.js';
export default class ButtonBarrierSystem { export default class ButtonBarrierSystem {
/** /**
* @param {Phaser.Scene} scene * @param {Phaser.Scene} scene
* @param {Phaser.Tilemaps.TilemapLayer} barriersLayer * @param {Phaser.Tilemaps.TilemapLayer} floorLayer
*/ */
constructor(scene, barriersLayer) { constructor(scene, floorLayer) {
this._scene = scene; this._scene = scene;
this._barriersLayer = barriersLayer; this._floorLayer = floorLayer;
this._buttons = []; // [{ col, row, color }] this._buttons = []; // [{ col, row, color }]
this._barriers = []; // [{ col, row, color }] this._barriers = []; // [{ col, row, color }]
@ -15,11 +15,12 @@ export default class ButtonBarrierSystem {
} }
/** /**
* Scan the barriers layer to build button/barrier registries and set initial * Scan the floor layer to build button/barrier registries and set initial
* collision state. Call once after the layer is created. * barrier state (all passable — no button pressed yet). Call once after
* the layer is created.
*/ */
init() { init() {
const layer = this._barriersLayer; const layer = this._floorLayer;
layer.forEachTile((tile) => { layer.forEachTile((tile) => {
if (tile.index <= 0) return; if (tile.index <= 0) return;
@ -36,12 +37,23 @@ export default class ButtonBarrierSystem {
} }
}); });
this._rebuildCollision(); // Set initial barrier states: active color (if any) → solid, all others → passable
for (const bar of this._barriers) {
const gids = COLOR_TILES[bar.color];
const newGID = bar.color === this._activeColor
? gids.barrier_solid
: gids.barrier_open;
this._floorLayer.putTileAt(newGID, bar.col, bar.row);
}
// putTileAt doesn't automatically update Phaser's per-tile collision flags,
// so explicitly sync the barrier tiles' collision state now.
this._refreshCollision();
} }
/** /**
* Press the button at the given tile coordinates. * Press the button at the given tile coordinates.
* Updates button/barrier tile indices and rebuilds collision. * Active color's barriers become solid; all other barriers become passable.
* @param {number} col Tile column * @param {number} col Tile column
* @param {number} row Tile row * @param {number} row Tile row
*/ */
@ -56,19 +68,33 @@ export default class ButtonBarrierSystem {
for (const btn of this._buttons) { for (const btn of this._buttons) {
const gids = COLOR_TILES[btn.color]; const gids = COLOR_TILES[btn.color];
const newGID = btn.color === this._activeColor ? gids.btn_on : gids.btn_off; const newGID = btn.color === this._activeColor ? gids.btn_on : gids.btn_off;
this._barriersLayer.putTileAt(newGID, btn.col, btn.row); this._floorLayer.putTileAt(newGID, btn.col, btn.row);
} }
// Update all barrier tiles // Active color's barriers → solid; all others → passable
for (const bar of this._barriers) { for (const bar of this._barriers) {
const gids = COLOR_TILES[bar.color]; const gids = COLOR_TILES[bar.color];
const newGID = bar.color === this._activeColor const newGID = bar.color === this._activeColor
? gids.barrier_open ? gids.barrier_solid
: gids.barrier_solid; : gids.barrier_open;
this._barriersLayer.putTileAt(newGID, bar.col, bar.row); this._floorLayer.putTileAt(newGID, bar.col, bar.row);
} }
this._rebuildCollision(); this._refreshCollision();
}
/**
* Sync Phaser's per-tile collision flags for all button and barrier tiles.
* putTileAt changes a tile's index but does NOT reset its collision flags,
* so we must explicitly enable/disable collision after any putTileAt call.
* The recalculateFaces=true argument (third param) also fixes face-culling
* on solid tiles adjacent to newly-passable tiles.
*/
_refreshCollision() {
this._floorLayer.setCollision(BUTTON_GIDS, true, true); // btn_off = solid
this._floorLayer.setCollision(BUTTON_ON_GIDS, false, true); // btn_on = passable
this._floorLayer.setCollision(BARRIER_SOLID_GIDS, true, true);
this._floorLayer.setCollision(BARRIER_OPEN_GIDS, false, true);
} }
/** /**
@ -79,35 +105,4 @@ export default class ButtonBarrierSystem {
const props = TilePropertyHelper.getPropsByGID(gid); const props = TilePropertyHelper.getPropsByGID(gid);
return !!(props.button && !props.pressed); return !!(props.button && !props.pressed);
} }
/**
* Rebuild which tiles in the barriers layer have active collision.
* Solid: all unpressed buttons + all non-active-color solid barriers.
* Passable: active-color barriers + pressed button.
*/
_rebuildCollision() {
const solidGIDs = [];
for (const [color, gids] of Object.entries(COLOR_TILES)) {
if (color !== this._activeColor) {
solidGIDs.push(gids.btn_off);
solidGIDs.push(gids.barrier_solid);
}
// Active color's btn_on and barrier_open remain passable (not added)
}
// setCollision(indices, collides, recalculate)
// Passing false to a second call clears existing, then we set only solidGIDs
this._barriersLayer.setCollision(solidGIDs, true, true);
// Explicitly remove collision from active-color tiles (in case of leftovers)
if (this._activeColor) {
const gids = COLOR_TILES[this._activeColor];
this._barriersLayer.setCollision(
[gids.btn_on, gids.barrier_open],
false,
true
);
}
}
} }

View File

@ -84,6 +84,15 @@ export const SOLID_BARRIER_GIDS = [
21, 31, 41, 51, // solid barriers: red, blue, yellow, green 21, 31, 41, 51, // solid barriers: red, blue, yellow, green
]; ];
// Unpressed button GIDs — solid, player can land on them
export const BUTTON_GIDS = [9, 10, 29, 30];
// Pressed button GIDs — passable, player walks/falls through
export const BUTTON_ON_GIDS = [19, 20, 39, 40];
// Barrier GIDs separated by state (used for dynamic setCollision updates)
export const BARRIER_SOLID_GIDS = [21, 31, 41, 51];
export const BARRIER_OPEN_GIDS = [22, 32, 42, 52];
export default class TilePropertyHelper { export default class TilePropertyHelper {
/** Returns property object for a 0-based tile ID (TSX convention). */ /** Returns property object for a 0-based tile ID (TSX convention). */
static getProps(tileId) { static getProps(tileId) {