mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
Enhance office room layout and interactions
- Updated room_office2.tmj to adjust object positions and dimensions for better alignment. - Added new objects and refined existing ones to improve gameplay experience. - Modified TiledItemPool in rooms.js to support regular and table items separately, ensuring proper item matching and prioritization. - Improved interaction handling for swivel chairs, allowing them to be kicked and spin upon collision with walls. - Updated object physics to handle swivel chair collisions dynamically, enhancing realism. - Revised scenario details in cybok_heist.json for clarity and improved narrative flow.
This commit is contained in:
@@ -81,6 +81,18 @@
|
||||
"width":78,
|
||||
"x":118,
|
||||
"y":146.666666666667
|
||||
},
|
||||
{
|
||||
"gid":541,
|
||||
"height":41,
|
||||
"id":98,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":253.333333333333,
|
||||
"y":176
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -493,7 +505,7 @@
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":11,
|
||||
"nextobjectid":98,
|
||||
"nextobjectid":99,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.11.2",
|
||||
|
||||
@@ -89,6 +89,18 @@
|
||||
"width":78,
|
||||
"x":118,
|
||||
"y":146.666666666667
|
||||
},
|
||||
{
|
||||
"gid":541,
|
||||
"height":41,
|
||||
"id":98,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":253.333333333333,
|
||||
"y":176
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -501,7 +513,7 @@
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":11,
|
||||
"nextobjectid":98,
|
||||
"nextobjectid":99,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.11.2",
|
||||
|
||||
@@ -47,12 +47,12 @@
|
||||
{
|
||||
"data":[0, 101, 0, 0, 0, 0, 0, 0, 101, 0,
|
||||
0, 107, 0, 0, 0, 0, 0, 0, 107, 0,
|
||||
420, 0, 0, 0, 0, 0, 0, 0, 0, 420,
|
||||
444, 0, 0, 0, 0, 0, 0, 0, 0, 444,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
420, 0, 0, 0, 0, 0, 0, 0, 0, 420,
|
||||
444, 0, 0, 0, 0, 0, 0, 0, 0, 444,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
@@ -210,8 +210,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":16,
|
||||
"x":92,
|
||||
"y":121
|
||||
"x":234.936288088643,
|
||||
"y":179.725761772853
|
||||
},
|
||||
{
|
||||
"gid":198,
|
||||
@@ -246,8 +246,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":9,
|
||||
"x":254.5,
|
||||
"y":171.5
|
||||
"x":246.005078485688,
|
||||
"y":171.869344413666
|
||||
},
|
||||
|
||||
{
|
||||
@@ -283,8 +283,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":17,
|
||||
"x":209.954755309326,
|
||||
"y":170.630655586334
|
||||
"x":208.108033240997,
|
||||
"y":172.477377654663
|
||||
},
|
||||
{
|
||||
"gid":205,
|
||||
@@ -319,8 +319,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":15,
|
||||
"x":241.5,
|
||||
"y":132.5
|
||||
"x":244.454755309326,
|
||||
"y":132.869344413666
|
||||
},
|
||||
{
|
||||
"gid":219,
|
||||
@@ -392,8 +392,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":24,
|
||||
"x":258,
|
||||
"y":180.5
|
||||
"x":252.82917820868,
|
||||
"y":179.391966759003
|
||||
},
|
||||
{
|
||||
"gid":338,
|
||||
@@ -487,8 +487,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":16,
|
||||
"x":217.25,
|
||||
"y":183
|
||||
"x":218.358033240997,
|
||||
"y":183.738688827331
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -562,64 +562,77 @@
|
||||
"y":66
|
||||
},
|
||||
{
|
||||
"gid":412,
|
||||
"gid":404,
|
||||
"height":32,
|
||||
"id":144,
|
||||
"id":152,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":209.669436749769,
|
||||
"y":216.923361034164
|
||||
},
|
||||
{
|
||||
"gid":411,
|
||||
"height":32,
|
||||
"id":145,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":29.0600184672207,
|
||||
"y":170.755309325946
|
||||
"x":99.2354570637119,
|
||||
"y":228.373037857802
|
||||
},
|
||||
{
|
||||
"gid":405,
|
||||
"height":32,
|
||||
"id":146,
|
||||
"id":153,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":242.54108956602,
|
||||
"x":101.082179132041,
|
||||
"y":114.245614035088
|
||||
},
|
||||
{
|
||||
"gid":403,
|
||||
"height":32,
|
||||
"id":154,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":181.599261311173,
|
||||
"y":91.7156048014774
|
||||
},
|
||||
{
|
||||
"gid":407,
|
||||
"height":32,
|
||||
"id":155,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":211.146814404432,
|
||||
"y":223.940904893813
|
||||
},
|
||||
{
|
||||
"gid":409,
|
||||
"height":32,
|
||||
"id":157,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":217.795013850416,
|
||||
"y":172.602031394275
|
||||
},
|
||||
|
||||
{
|
||||
"gid":412,
|
||||
"height":32,
|
||||
"id":158,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":27.9519852262234,
|
||||
"y":172.971375807941
|
||||
},
|
||||
{
|
||||
"gid":402,
|
||||
"height":32,
|
||||
"id":148,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":181.229916897507,
|
||||
"y":79.8965835641736
|
||||
},
|
||||
{
|
||||
"gid":400,
|
||||
"height":32,
|
||||
"id":149,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":75.5974145891043,
|
||||
"y":222.46352723915
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -776,6 +789,30 @@
|
||||
"width":26,
|
||||
"x":34.2760849492151,
|
||||
"y":216.662049861496
|
||||
},
|
||||
{
|
||||
"gid":261,
|
||||
"height":17,
|
||||
"id":160,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":23,
|
||||
"x":114.946445060018,
|
||||
"y":66.617728531856
|
||||
},
|
||||
{
|
||||
"gid":264,
|
||||
"height":17,
|
||||
"id":161,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":22,
|
||||
"x":184.752539242844,
|
||||
"y":66.9870729455217
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -795,7 +832,7 @@
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":11,
|
||||
"nextobjectid":151,
|
||||
"nextobjectid":162,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.11.2",
|
||||
@@ -892,7 +929,7 @@
|
||||
"margin":0,
|
||||
"name":"objects",
|
||||
"spacing":0,
|
||||
"tilecount":278,
|
||||
"tilecount":302,
|
||||
"tileheight":359,
|
||||
"tiles":[
|
||||
{
|
||||
@@ -2589,12 +2626,159 @@
|
||||
"image":"..\/objects\/smartscreen.png",
|
||||
"imageheight":34,
|
||||
"imagewidth":48
|
||||
},
|
||||
{
|
||||
"id":297,
|
||||
"image":"..\/objects\/chair-white-1.aseprite",
|
||||
"imageheight":32,
|
||||
"imagewidth":32
|
||||
},
|
||||
{
|
||||
"id":298,
|
||||
"image":"..\/objects\/fingerprint_kit.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":10
|
||||
},
|
||||
|
||||
{
|
||||
"id":299,
|
||||
"image":"..\/objects\/fingerprint_small.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":18
|
||||
},
|
||||
{
|
||||
"id":300,
|
||||
"image":"..\/objects\/key-ring.png",
|
||||
"imageheight":27,
|
||||
"imagewidth":18
|
||||
},
|
||||
{
|
||||
"id":301,
|
||||
"image":"..\/objects\/notes.png",
|
||||
"imageheight":14,
|
||||
"imagewidth":27
|
||||
},
|
||||
{
|
||||
"id":302,
|
||||
"image":"..\/objects\/notes5.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":25
|
||||
},
|
||||
{
|
||||
"id":303,
|
||||
"image":"..\/objects\/pc.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":36
|
||||
},
|
||||
{
|
||||
"id":304,
|
||||
"image":"..\/objects\/pin-cracker-large.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":32
|
||||
},
|
||||
{
|
||||
"id":305,
|
||||
"image":"..\/objects\/pin-cracker.png",
|
||||
"imageheight":13,
|
||||
"imagewidth":12
|
||||
},
|
||||
{
|
||||
"id":306,
|
||||
"image":"..\/objects\/plant-large11-top-ani1.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":307,
|
||||
"image":"..\/objects\/plant-large11-top-ani2.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":308,
|
||||
"image":"..\/objects\/plant-large11-top-ani3.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
|
||||
{
|
||||
"id":309,
|
||||
"image":"..\/objects\/plant-large11-top-ani4.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":310,
|
||||
"image":"..\/objects\/plant-large12-top-ani1.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":311,
|
||||
"image":"..\/objects\/plant-large12-top-ani2.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":312,
|
||||
"image":"..\/objects\/plant-large12-top-ani3.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":313,
|
||||
"image":"..\/objects\/plant-large12-top-ani4.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":314,
|
||||
"image":"..\/objects\/plant-large12-top-ani5.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":315,
|
||||
"image":"..\/objects\/plant-large13-top-ani1.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":316,
|
||||
"image":"..\/objects\/plant-large13-top-ani2.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":317,
|
||||
"image":"..\/objects\/plant-large13-top-ani3.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":318,
|
||||
"image":"..\/objects\/plant-large13-top-ani4.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
|
||||
{
|
||||
"id":319,
|
||||
"image":"..\/objects\/text_file.png",
|
||||
"imageheight":16,
|
||||
"imagewidth":16
|
||||
},
|
||||
{
|
||||
"id":320,
|
||||
"image":"..\/objects\/workstation.png",
|
||||
"imageheight":18,
|
||||
"imagewidth":24
|
||||
}],
|
||||
"tilewidth":221
|
||||
},
|
||||
{
|
||||
"columns":6,
|
||||
"firstgid":420,
|
||||
"firstgid":444,
|
||||
"image":"..\/tiles\/door_side_sheet_32.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":192,
|
||||
@@ -2607,7 +2791,7 @@
|
||||
},
|
||||
{
|
||||
"columns":10,
|
||||
"firstgid":426,
|
||||
"firstgid":450,
|
||||
"image":"..\/tiles\/rooms\/room14.png",
|
||||
"imageheight":320,
|
||||
"imagewidth":320,
|
||||
@@ -2620,7 +2804,7 @@
|
||||
},
|
||||
{
|
||||
"columns":10,
|
||||
"firstgid":526,
|
||||
"firstgid":550,
|
||||
"image":"..\/tiles\/rooms\/room18.png",
|
||||
"imageheight":320,
|
||||
"imagewidth":320,
|
||||
|
||||
@@ -55,12 +55,12 @@
|
||||
{
|
||||
"data":[0, 101, 0, 0, 0, 0, 0, 0, 101, 0,
|
||||
0, 107, 0, 0, 0, 0, 0, 0, 107, 0,
|
||||
420, 0, 0, 0, 0, 0, 0, 0, 0, 420,
|
||||
444, 0, 0, 0, 0, 0, 0, 0, 0, 444,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
420, 0, 0, 0, 0, 0, 0, 0, 0, 420,
|
||||
444, 0, 0, 0, 0, 0, 0, 0, 0, 444,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
"height":10,
|
||||
@@ -218,8 +218,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":16,
|
||||
"x":92,
|
||||
"y":121
|
||||
"x":234.936288088643,
|
||||
"y":179.725761772853
|
||||
},
|
||||
{
|
||||
"gid":198,
|
||||
@@ -254,8 +254,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":9,
|
||||
"x":254.5,
|
||||
"y":171.5
|
||||
"x":246.005078485688,
|
||||
"y":171.869344413666
|
||||
},
|
||||
|
||||
{
|
||||
@@ -291,8 +291,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":17,
|
||||
"x":209.954755309326,
|
||||
"y":170.630655586334
|
||||
"x":208.108033240997,
|
||||
"y":172.477377654663
|
||||
},
|
||||
{
|
||||
"gid":205,
|
||||
@@ -327,8 +327,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":15,
|
||||
"x":241.5,
|
||||
"y":132.5
|
||||
"x":244.454755309326,
|
||||
"y":132.869344413666
|
||||
},
|
||||
{
|
||||
"gid":219,
|
||||
@@ -400,8 +400,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":24,
|
||||
"x":258,
|
||||
"y":180.5
|
||||
"x":252.82917820868,
|
||||
"y":179.391966759003
|
||||
},
|
||||
{
|
||||
"gid":338,
|
||||
@@ -495,8 +495,8 @@
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":16,
|
||||
"x":217.25,
|
||||
"y":183
|
||||
"x":218.358033240997,
|
||||
"y":183.738688827331
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -570,64 +570,77 @@
|
||||
"y":66
|
||||
},
|
||||
{
|
||||
"gid":412,
|
||||
"gid":404,
|
||||
"height":32,
|
||||
"id":144,
|
||||
"id":152,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":209.669436749769,
|
||||
"y":216.923361034164
|
||||
},
|
||||
{
|
||||
"gid":411,
|
||||
"height":32,
|
||||
"id":145,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":29.0600184672207,
|
||||
"y":170.755309325946
|
||||
"x":99.2354570637119,
|
||||
"y":228.373037857802
|
||||
},
|
||||
{
|
||||
"gid":405,
|
||||
"height":32,
|
||||
"id":146,
|
||||
"id":153,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":242.54108956602,
|
||||
"x":101.082179132041,
|
||||
"y":114.245614035088
|
||||
},
|
||||
{
|
||||
"gid":403,
|
||||
"height":32,
|
||||
"id":154,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":181.599261311173,
|
||||
"y":91.7156048014774
|
||||
},
|
||||
{
|
||||
"gid":407,
|
||||
"height":32,
|
||||
"id":155,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":211.146814404432,
|
||||
"y":223.940904893813
|
||||
},
|
||||
{
|
||||
"gid":409,
|
||||
"height":32,
|
||||
"id":157,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":217.795013850416,
|
||||
"y":172.602031394275
|
||||
},
|
||||
|
||||
{
|
||||
"gid":412,
|
||||
"height":32,
|
||||
"id":158,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":27.9519852262234,
|
||||
"y":172.971375807941
|
||||
},
|
||||
{
|
||||
"gid":402,
|
||||
"height":32,
|
||||
"id":148,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":181.229916897507,
|
||||
"y":79.8965835641736
|
||||
},
|
||||
{
|
||||
"gid":400,
|
||||
"height":32,
|
||||
"id":149,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":32,
|
||||
"x":75.5974145891043,
|
||||
"y":222.46352723915
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -784,6 +797,30 @@
|
||||
"width":26,
|
||||
"x":34.2760849492151,
|
||||
"y":216.662049861496
|
||||
},
|
||||
{
|
||||
"gid":261,
|
||||
"height":17,
|
||||
"id":160,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":23,
|
||||
"x":114.946445060018,
|
||||
"y":66.617728531856
|
||||
},
|
||||
{
|
||||
"gid":264,
|
||||
"height":17,
|
||||
"id":161,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":22,
|
||||
"x":184.752539242844,
|
||||
"y":66.9870729455217
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
@@ -803,7 +840,7 @@
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":11,
|
||||
"nextobjectid":151,
|
||||
"nextobjectid":162,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.11.2",
|
||||
@@ -842,7 +879,7 @@
|
||||
"margin":0,
|
||||
"name":"objects",
|
||||
"spacing":0,
|
||||
"tilecount":278,
|
||||
"tilecount":302,
|
||||
"tileheight":359,
|
||||
"tiles":[
|
||||
{
|
||||
@@ -2539,19 +2576,166 @@
|
||||
"image":"..\/objects\/smartscreen.png",
|
||||
"imageheight":34,
|
||||
"imagewidth":48
|
||||
},
|
||||
{
|
||||
"id":297,
|
||||
"image":"..\/objects\/chair-white-1.aseprite",
|
||||
"imageheight":32,
|
||||
"imagewidth":32
|
||||
},
|
||||
{
|
||||
"id":298,
|
||||
"image":"..\/objects\/fingerprint_kit.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":10
|
||||
},
|
||||
|
||||
{
|
||||
"id":299,
|
||||
"image":"..\/objects\/fingerprint_small.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":18
|
||||
},
|
||||
{
|
||||
"id":300,
|
||||
"image":"..\/objects\/key-ring.png",
|
||||
"imageheight":27,
|
||||
"imagewidth":18
|
||||
},
|
||||
{
|
||||
"id":301,
|
||||
"image":"..\/objects\/notes.png",
|
||||
"imageheight":14,
|
||||
"imagewidth":27
|
||||
},
|
||||
{
|
||||
"id":302,
|
||||
"image":"..\/objects\/notes5.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":25
|
||||
},
|
||||
{
|
||||
"id":303,
|
||||
"image":"..\/objects\/pc.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":36
|
||||
},
|
||||
{
|
||||
"id":304,
|
||||
"image":"..\/objects\/pin-cracker-large.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":32
|
||||
},
|
||||
{
|
||||
"id":305,
|
||||
"image":"..\/objects\/pin-cracker.png",
|
||||
"imageheight":13,
|
||||
"imagewidth":12
|
||||
},
|
||||
{
|
||||
"id":306,
|
||||
"image":"..\/objects\/plant-large11-top-ani1.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":307,
|
||||
"image":"..\/objects\/plant-large11-top-ani2.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":308,
|
||||
"image":"..\/objects\/plant-large11-top-ani3.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
|
||||
{
|
||||
"id":309,
|
||||
"image":"..\/objects\/plant-large11-top-ani4.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":310,
|
||||
"image":"..\/objects\/plant-large12-top-ani1.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":311,
|
||||
"image":"..\/objects\/plant-large12-top-ani2.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":312,
|
||||
"image":"..\/objects\/plant-large12-top-ani3.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":313,
|
||||
"image":"..\/objects\/plant-large12-top-ani4.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":314,
|
||||
"image":"..\/objects\/plant-large12-top-ani5.png",
|
||||
"imageheight":75,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":315,
|
||||
"image":"..\/objects\/plant-large13-top-ani1.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":316,
|
||||
"image":"..\/objects\/plant-large13-top-ani2.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":317,
|
||||
"image":"..\/objects\/plant-large13-top-ani3.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
{
|
||||
"id":318,
|
||||
"image":"..\/objects\/plant-large13-top-ani4.png",
|
||||
"imageheight":88,
|
||||
"imagewidth":64
|
||||
},
|
||||
|
||||
{
|
||||
"id":319,
|
||||
"image":"..\/objects\/text_file.png",
|
||||
"imageheight":16,
|
||||
"imagewidth":16
|
||||
},
|
||||
{
|
||||
"id":320,
|
||||
"image":"..\/objects\/workstation.png",
|
||||
"imageheight":18,
|
||||
"imagewidth":24
|
||||
}],
|
||||
"tilewidth":221
|
||||
},
|
||||
{
|
||||
"firstgid":420,
|
||||
"firstgid":444,
|
||||
"source":"..\/..\/..\/assets\/rooms\/door_side_sheet_32.tsx"
|
||||
},
|
||||
{
|
||||
"firstgid":426,
|
||||
"firstgid":450,
|
||||
"source":"room14.tsx"
|
||||
},
|
||||
{
|
||||
"firstgid":526,
|
||||
"firstgid":550,
|
||||
"source":"room18.tsx"
|
||||
}],
|
||||
"tilewidth":32,
|
||||
|
||||
244
js/core/rooms.js
244
js/core/rooms.js
@@ -103,11 +103,12 @@ let gameRef = null;
|
||||
*/
|
||||
class TiledItemPool {
|
||||
constructor(objectsByLayer, map) {
|
||||
this.itemsByType = {}; // Regular items indexed by type
|
||||
this.itemsByType = {}; // Regular items (non-table) indexed by type
|
||||
this.tableItemsByType = {}; // Regular table items indexed by type
|
||||
this.conditionalItemsByType = {}; // Conditional items indexed by type
|
||||
this.conditionalTableItemsByType = {}; // Conditional table items indexed by type
|
||||
this.reserved = new Set(); // Track reserved items to prevent reuse
|
||||
this.map = map; // Store map for tileset lookups
|
||||
this.reserved = new Set(); // Track reserved items to prevent reuse
|
||||
this.map = map; // Store map for tileset lookups
|
||||
|
||||
this.populateFromLayers(objectsByLayer);
|
||||
}
|
||||
@@ -147,9 +148,16 @@ class TiledItemPool {
|
||||
/**
|
||||
* Populate pool from Tiled object layers
|
||||
* Indexes items by their base type for efficient lookup
|
||||
*
|
||||
* Priority order for matching:
|
||||
* 1. Regular items (non-table)
|
||||
* 2. Regular table items
|
||||
* 3. Conditional items (non-table)
|
||||
* 4. Conditional table items
|
||||
*/
|
||||
populateFromLayers(objectsByLayer) {
|
||||
this.itemsByType = this.indexByType(objectsByLayer.items || []);
|
||||
this.tableItemsByType = this.indexByType(objectsByLayer.table_items || []);
|
||||
this.conditionalItemsByType = this.indexByType(objectsByLayer.conditional_items || []);
|
||||
this.conditionalTableItemsByType = this.indexByType(objectsByLayer.conditional_table_items || []);
|
||||
}
|
||||
@@ -175,15 +183,28 @@ class TiledItemPool {
|
||||
|
||||
/**
|
||||
* Find best matching item for a scenario object
|
||||
* Searches in priority order: regular → conditional → conditional table items
|
||||
* Skips reserved items to prevent reuse
|
||||
* Searches in strict priority order:
|
||||
* 1. Regular items (items layer)
|
||||
* 2. Regular table items (table_items layer)
|
||||
* 3. Conditional items (conditional_items layer)
|
||||
* 4. Conditional table items (conditional_table_items layer)
|
||||
*
|
||||
* This ensures unconditional items are ALWAYS used before conditional items.
|
||||
* For multiple requests of the same type, exhausts each layer before moving to next.
|
||||
*
|
||||
* Skips reserved items to prevent reuse.
|
||||
* Returns the matched item or null if no match found
|
||||
*/
|
||||
findMatchFor(scenarioObj) {
|
||||
const searchType = scenarioObj.type;
|
||||
|
||||
// Search priority: regular items first, then conditional, then table items
|
||||
const searchOrder = [this.itemsByType, this.conditionalItemsByType, this.conditionalTableItemsByType];
|
||||
// Search priority: unconditional layers first, then conditional layers
|
||||
const searchOrder = [
|
||||
this.itemsByType, // Regular items
|
||||
this.tableItemsByType, // Regular table items (NEW - CRITICAL FIX)
|
||||
this.conditionalItemsByType, // Conditional items
|
||||
this.conditionalTableItemsByType // Conditional table items
|
||||
];
|
||||
|
||||
for (const indexedItems of searchOrder) {
|
||||
const candidates = indexedItems[searchType] || [];
|
||||
@@ -217,10 +238,12 @@ class TiledItemPool {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all unreserved items across all layers
|
||||
* Used to process background decoration items
|
||||
* NOTE: Only returns regular items, NOT conditional items
|
||||
* Conditional items should ONLY be created when explicitly requested by scenario
|
||||
* Get all unreserved items across all regular (unconditional) layers
|
||||
* Used to process background decoration items that weren't used by scenario
|
||||
*
|
||||
* NOTE: Returns BOTH regular items AND regular table items, but NOT conditional items.
|
||||
* Conditional items should ONLY be created when explicitly requested by scenario.
|
||||
* This ensures conditional items stay hidden until the scenario needs them.
|
||||
*/
|
||||
getUnreservedItems() {
|
||||
const unreserved = [];
|
||||
@@ -235,8 +258,10 @@ class TiledItemPool {
|
||||
});
|
||||
};
|
||||
|
||||
// Only process regular items - conditional items should NOT be auto-created
|
||||
// Process both regular items and regular table items
|
||||
// Exclude conditional items - they should only appear when scenario explicitly requests them
|
||||
collectUnreserved(this.itemsByType);
|
||||
collectUnreserved(this.tableItemsByType);
|
||||
|
||||
return unreserved;
|
||||
}
|
||||
@@ -904,44 +929,9 @@ export function createRoom(roomId, roomData, position) {
|
||||
tableGroups.push(group);
|
||||
});
|
||||
|
||||
// Process table items and assign them to groups
|
||||
if (objectsByLayer.table_items) {
|
||||
objectsByLayer.table_items.forEach(obj => {
|
||||
const processedObj = processObject(obj, position, roomId, 'table_item', map);
|
||||
if (processedObj) {
|
||||
// Find the closest table
|
||||
const closestTable = findClosestTable(processedObj.sprite, tableObjects);
|
||||
if (closestTable) {
|
||||
const group = tableGroups.find(g => g.table === closestTable);
|
||||
if (group) {
|
||||
group.items.push(processedObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Conditional table items are now handled by scenario matching system
|
||||
|
||||
// Set z-index ordering for each group (table first, then items from north to south)
|
||||
tableGroups.forEach(group => {
|
||||
// Table is already at the correct depth
|
||||
console.log(`Setting up group for table at depth ${group.baseDepth}`);
|
||||
|
||||
// Sort items from north to south (lower Y values first)
|
||||
group.items.sort((a, b) => a.sprite.y - b.sprite.y);
|
||||
|
||||
// Set items to share the same base depth as the table
|
||||
group.items.forEach((item, index) => {
|
||||
// Table items don't need elevation - they're grouped with the table
|
||||
const itemDepth = group.baseDepth + (index + 1) * 0.01; // Slight offset for proper ordering
|
||||
item.sprite.setDepth(itemDepth);
|
||||
|
||||
// No elevation for table items
|
||||
item.sprite.elevation = 0;
|
||||
console.log(`Set item ${item.sprite.name} to depth ${itemDepth} (north to south order, no elevation)`);
|
||||
});
|
||||
});
|
||||
// NOTE: Table items (both regular and conditional) are now processed through the item pool
|
||||
// in processScenarioObjectsWithConditionalMatching(). They will be handled there
|
||||
// with proper priority ordering (regular table items before conditional ones).
|
||||
|
||||
// Build a set of inventory items that should NOT be created as sprites
|
||||
const inventoryItemTypes = new Set();
|
||||
@@ -957,8 +947,9 @@ export function createRoom(roomId, roomData, position) {
|
||||
// Process scenario objects with conditional item matching first
|
||||
const usedItems = processScenarioObjectsWithConditionalMatching(roomId, position, objectsByLayer, map);
|
||||
|
||||
// Process all non-conditional items (chairs, plants, etc.)
|
||||
// Give them default properties if not used in scenario
|
||||
// Process all non-conditional items (chairs, plants, lamps, PCs, etc.)
|
||||
// These should ALWAYS be visible (not conditional)
|
||||
// They get default properties if not customized by scenario
|
||||
if (objectsByLayer.items) {
|
||||
objectsByLayer.items.forEach(obj => {
|
||||
const imageName = getImageNameFromObjectWithMap(obj, map);
|
||||
@@ -975,12 +966,19 @@ export function createRoom(roomId, roomData, position) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if this base type was used by scenario objects
|
||||
if (imageName && (usedItems.has(imageName) || usedItems.has(baseType))) {
|
||||
console.log(`Skipping regular item ${imageName} (baseType: ${baseType}) - used by scenario object`);
|
||||
// Skip if this exact item was used by scenario objects
|
||||
// BUT: Create it anyway if we haven't used ALL items of this type
|
||||
if (imageName && usedItems.has(imageName)) {
|
||||
console.log(`Skipping regular item ${imageName} (exact item used by scenario)`);
|
||||
return;
|
||||
}
|
||||
processObject(obj, position, roomId, 'item', map);
|
||||
|
||||
// Process the item and store it
|
||||
const result = processObject(obj, position, roomId, 'item', map);
|
||||
if (result && result.sprite) {
|
||||
// Store unconditional items in the objects collection so they're revealed
|
||||
rooms[roomId].objects[result.sprite.objectId] = result.sprite;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1021,22 +1019,28 @@ export function createRoom(roomId, roomData, position) {
|
||||
usedItem = itemPool.findMatchFor(scenarioObj);
|
||||
|
||||
if (usedItem) {
|
||||
// Check if this is a table item by searching which layer it came from
|
||||
// Check which layer this item came from to determine if it's a table item
|
||||
const imageName = itemPool.getImageNameFromObject(usedItem);
|
||||
const baseType = itemPool.extractBaseTypeFromImageName(imageName);
|
||||
|
||||
// Determine if item came from table items layer
|
||||
if (itemPool.conditionalTableItemsByType[baseType] &&
|
||||
itemPool.conditionalTableItemsByType[baseType].includes(usedItem)) {
|
||||
// Determine source layer and log appropriately
|
||||
let sourceLayer = 'unknown';
|
||||
if (itemPool.itemsByType[baseType] && itemPool.itemsByType[baseType].includes(usedItem)) {
|
||||
sourceLayer = 'items (regular)';
|
||||
isTableItem = false;
|
||||
} else if (itemPool.tableItemsByType[baseType] && itemPool.tableItemsByType[baseType].includes(usedItem)) {
|
||||
sourceLayer = 'table_items (regular)';
|
||||
isTableItem = true;
|
||||
} else if (itemPool.conditionalItemsByType[baseType] && itemPool.conditionalItemsByType[baseType].includes(usedItem)) {
|
||||
sourceLayer = 'conditional_items';
|
||||
isTableItem = false;
|
||||
} else if (itemPool.conditionalTableItemsByType[baseType] && itemPool.conditionalTableItemsByType[baseType].includes(usedItem)) {
|
||||
sourceLayer = 'conditional_table_items';
|
||||
isTableItem = true;
|
||||
console.log(`Using conditional table item for ${objType}`);
|
||||
} else if (itemPool.conditionalItemsByType[baseType] &&
|
||||
itemPool.conditionalItemsByType[baseType].includes(usedItem)) {
|
||||
console.log(`Using conditional item for ${objType}`);
|
||||
} else {
|
||||
console.log(`Using regular item for ${objType}`);
|
||||
}
|
||||
|
||||
console.log(`Using ${objType} from ${sourceLayer} layer`);
|
||||
|
||||
// Create sprite from matched item
|
||||
sprite = createSpriteFromMatch(usedItem, scenarioObj, position, roomId, index, map);
|
||||
|
||||
@@ -1059,7 +1063,7 @@ export function createRoom(roomId, roomData, position) {
|
||||
|
||||
// No elevation for table items
|
||||
sprite.elevation = 0;
|
||||
group.items.push({ sprite, type: 'conditional_table_item' });
|
||||
group.items.push({ sprite, type: sourceLayer });
|
||||
|
||||
// Store table items in objects collection so interaction system can find them
|
||||
rooms[roomId].objects[sprite.objectId] = sprite;
|
||||
@@ -1078,8 +1082,73 @@ export function createRoom(roomId, roomData, position) {
|
||||
setDepthAndStore(sprite, position, roomId, false);
|
||||
}
|
||||
});
|
||||
|
||||
// Re-sort table groups after adding scenario items to maintain north-to-south order
|
||||
|
||||
// 3. Process unreserved Tiled items (existing background decoration items)
|
||||
// These are unconditional items that were not used by any scenario object
|
||||
const unreservedItems = itemPool.getUnreservedItems();
|
||||
|
||||
// Separate table items from regular items for special processing
|
||||
const unreservedTableItems = [];
|
||||
const unreservedRegularItems = [];
|
||||
|
||||
unreservedItems.forEach(tiledItem => {
|
||||
const imageName = itemPool.getImageNameFromObject(tiledItem);
|
||||
|
||||
// Skip if this exact item was already used by scenario objects
|
||||
if (usedItems.has(imageName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a table item by seeing if it's in tableItemsByType
|
||||
const baseType = itemPool.extractBaseTypeFromImageName(imageName);
|
||||
if (itemPool.tableItemsByType[baseType] &&
|
||||
itemPool.tableItemsByType[baseType].includes(tiledItem)) {
|
||||
unreservedTableItems.push(tiledItem);
|
||||
} else {
|
||||
unreservedRegularItems.push(tiledItem);
|
||||
}
|
||||
});
|
||||
|
||||
// Process regular unreserved items (chairs, lamps, etc.)
|
||||
unreservedRegularItems.forEach(tiledItem => {
|
||||
const imageName = itemPool.getImageNameFromObject(tiledItem);
|
||||
|
||||
// Use processObject to create sprite with all properties (collision, animation, etc.)
|
||||
const result = processObject(tiledItem, position, roomId, 'item', map);
|
||||
if (result && result.sprite) {
|
||||
// Store unreserved items so they're revealed
|
||||
rooms[roomId].objects[result.sprite.objectId] = result.sprite;
|
||||
console.log(`Added unreserved item ${imageName} to room objects`);
|
||||
}
|
||||
});
|
||||
|
||||
// Process unreserved table items - need to group them with tables and set depth
|
||||
unreservedTableItems.forEach(tiledItem => {
|
||||
const imageName = itemPool.getImageNameFromObject(tiledItem);
|
||||
|
||||
// Use processObject to create sprite with all properties
|
||||
const result = processObject(tiledItem, position, roomId, 'table_item', map);
|
||||
if (result && result.sprite) {
|
||||
// Find the closest table to group this item with
|
||||
if (tableObjects.length > 0) {
|
||||
const closestTable = findClosestTable(result.sprite, tableObjects);
|
||||
if (closestTable) {
|
||||
const group = tableGroups.find(g => g.table === closestTable);
|
||||
if (group) {
|
||||
group.items.push(result);
|
||||
console.log(`Added unreserved table item ${imageName} to table group`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No tables, just store it as a regular item
|
||||
rooms[roomId].objects[result.sprite.objectId] = result.sprite;
|
||||
console.log(`Added unreserved table item ${imageName} to room objects (no tables to group with)`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Final re-sort and depth assignment for all table groups
|
||||
// (includes both scenario and unreserved table items)
|
||||
tableGroups.forEach(group => {
|
||||
// Sort items from north to south (lower Y values first)
|
||||
group.items.sort((a, b) => a.sprite.y - b.sprite.y);
|
||||
@@ -1092,30 +1161,13 @@ export function createRoom(roomId, roomData, position) {
|
||||
|
||||
// No elevation for table items
|
||||
item.sprite.elevation = 0;
|
||||
console.log(`Re-sorted item ${item.sprite.name} to depth ${itemDepth} (north to south order, no elevation)`);
|
||||
console.log(`Final depth: table item ${item.sprite.name} to depth ${itemDepth} (position ${index + 1} of ${group.items.length})`);
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Process unreserved Tiled items (existing background decoration items)
|
||||
const unreservedItems = itemPool.getUnreservedItems();
|
||||
unreservedItems.forEach(tiledItem => {
|
||||
const imageName = itemPool.getImageNameFromObject(tiledItem);
|
||||
const baseType = itemPool.extractBaseTypeFromImageName(imageName);
|
||||
|
||||
// Skip if this base type was already used by scenario objects
|
||||
if (!usedItems.has(imageName) && !usedItems.has(baseType)) {
|
||||
const sprite = gameRef.add.sprite(
|
||||
Math.round(position.x + tiledItem.x),
|
||||
Math.round(position.y + tiledItem.y - tiledItem.height),
|
||||
imageName
|
||||
);
|
||||
|
||||
// Apply Tiled properties for unreserved items too
|
||||
applyTiledProperties(sprite, tiledItem);
|
||||
|
||||
// Set depth and store
|
||||
setDepthAndStore(sprite, position, roomId, false);
|
||||
}
|
||||
// Store all group items in room objects
|
||||
group.items.forEach(item => {
|
||||
rooms[roomId].objects[item.sprite.objectId] = item.sprite;
|
||||
});
|
||||
});
|
||||
|
||||
// Log summary of item usage
|
||||
@@ -1123,6 +1175,9 @@ export function createRoom(roomId, roomData, position) {
|
||||
Object.entries(itemPool.itemsByType).forEach(([baseType, items]) => {
|
||||
console.log(`Regular items for ${baseType}: ${items.length} available`);
|
||||
});
|
||||
Object.entries(itemPool.tableItemsByType).forEach(([baseType, items]) => {
|
||||
console.log(`Regular table items for ${baseType}: ${items.length} available`);
|
||||
});
|
||||
Object.entries(itemPool.conditionalItemsByType).forEach(([baseType, items]) => {
|
||||
console.log(`Conditional items for ${baseType}: ${items.length} available`);
|
||||
});
|
||||
@@ -1399,6 +1454,13 @@ export function createRoom(roomId, roomData, position) {
|
||||
console.log(`Applied default properties to ${type} ${imageName} -> ${cleanName}`);
|
||||
}
|
||||
|
||||
// Make swivel chairs interactable but don't highlight them
|
||||
if (sprite.isSwivelChair) {
|
||||
sprite.interactable = true;
|
||||
sprite.noInteractionHighlight = true;
|
||||
console.log(`Marked swivel chair ${sprite.objectId} as interactable (no highlight)`);
|
||||
}
|
||||
|
||||
// Note: Click handling is now done by the main scene's pointerdown handler
|
||||
// which checks for all objects at the clicked position
|
||||
|
||||
|
||||
@@ -65,6 +65,11 @@ export function checkObjectInteractions() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip highlighting for objects marked with noInteractionHighlight (like swivel chairs)
|
||||
if (obj.noInteractionHighlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip objects outside viewport for performance (if viewport bounds available)
|
||||
if (viewBounds && (
|
||||
obj.x < viewBounds.left ||
|
||||
@@ -262,7 +267,45 @@ export function handleObjectInteraction(sprite) {
|
||||
scenarioData: sprite.scenarioData
|
||||
});
|
||||
|
||||
if (!sprite || !sprite.scenarioData) {
|
||||
if (!sprite) {
|
||||
console.warn('Invalid sprite');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle swivel chair interaction - send it flying!
|
||||
if (sprite.isSwivelChair && sprite.body) {
|
||||
const player = window.player;
|
||||
if (player) {
|
||||
// Calculate direction from player to chair
|
||||
const dx = sprite.x - player.x;
|
||||
const dy = sprite.y - player.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0) {
|
||||
// Normalize the direction vector
|
||||
const dirX = dx / distance;
|
||||
const dirY = dy / distance;
|
||||
|
||||
// Apply a strong kick velocity
|
||||
const kickForce = 600; // Pixels per second
|
||||
sprite.body.setVelocity(dirX * kickForce, dirY * kickForce);
|
||||
|
||||
// Trigger spin direction calculation for visual rotation
|
||||
if (window.calculateChairSpinDirection) {
|
||||
window.calculateChairSpinDirection(player, sprite);
|
||||
}
|
||||
|
||||
// Show feedback message
|
||||
console.log('SWIVEL CHAIR KICKED', {
|
||||
chairName: sprite.name,
|
||||
velocity: { x: dirX * kickForce, y: dirY * kickForce }
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sprite.scenarioData) {
|
||||
console.warn('Invalid sprite or missing scenario data');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,14 @@ export function setupChairCollisions(chair) {
|
||||
if (room.wallCollisionBoxes) {
|
||||
room.wallCollisionBoxes.forEach(wallBox => {
|
||||
if (wallBox.body) {
|
||||
game.physics.add.collider(chair, wallBox);
|
||||
// Add collision callback for swivel chairs to modify spin on wall hit
|
||||
if (chair.isSwivelChair) {
|
||||
game.physics.add.collider(chair, wallBox, () => {
|
||||
handleChairWallCollision(chair);
|
||||
});
|
||||
} else {
|
||||
game.physics.add.collider(chair, wallBox);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -68,7 +75,14 @@ export function setupChairCollisions(chair) {
|
||||
room.doorSprites.forEach(doorSprite => {
|
||||
// Only collide with closed doors (doors that haven't been opened)
|
||||
if (doorSprite.body && doorSprite.body.immovable) {
|
||||
game.physics.add.collider(chair, doorSprite);
|
||||
// Add collision callback for swivel chairs to modify spin on wall hit
|
||||
if (chair.isSwivelChair) {
|
||||
game.physics.add.collider(chair, doorSprite, () => {
|
||||
handleChairWallCollision(chair);
|
||||
});
|
||||
} else {
|
||||
game.physics.add.collider(chair, doorSprite);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -133,6 +147,28 @@ export function setupExistingChairsWithNewRoom(roomId) {
|
||||
console.log(`Set up chair collisions for room ${roomId} with ${window.chairs.length} existing chairs`);
|
||||
}
|
||||
|
||||
// Handle collision between swivel chair and wall - modify spin on impact
|
||||
function handleChairWallCollision(chair) {
|
||||
if (!chair.isSwivelChair) return;
|
||||
|
||||
// When chair hits a wall, reverse the spin direction and give it a speed boost
|
||||
// This creates a dynamic "bounce" effect
|
||||
if (chair.spinDirection !== 0) {
|
||||
chair.spinDirection *= -1; // Reverse spin
|
||||
|
||||
// Give spin animation a nudge - speed it up temporarily
|
||||
// Add a boost to the rotation speed (up to 30% faster, but cap at max)
|
||||
const speedBoost = 1.3;
|
||||
chair.rotationSpeed = Math.min(chair.rotationSpeed * speedBoost, chair.maxRotationSpeed);
|
||||
|
||||
console.log('Chair hit wall - spin reversed with boost', {
|
||||
newDirection: chair.spinDirection,
|
||||
newRotationSpeed: chair.rotationSpeed,
|
||||
maxRotationSpeed: chair.maxRotationSpeed
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate chair spin direction based on contact point
|
||||
export function calculateChairSpinDirection(player, chair) {
|
||||
if (!chair.isSwivelChair) return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"scenario_brief": "You are a cyber security student tasked with recovering the Professor's backup of the CyBOK LaTeX source files for the CyBOK 1.1 release. According to legend, the HDD is stored in a safe in the Professor's office. Follow the clues scattered around the department to find the safe code. Time to put your physical security skills to the test! Good luck, and try not to get caught... or expelled.",
|
||||
"scenario_brief": "You are a cyber security student tasked with recovering the Professor's backup of the CyBOK LaTeX source files for the CyBOK 1.1 release. According to legend, the HDD is stored in a safe in the Professor's office. Follow the clues scattered around the department office to find the safe code. Time to put your physical security skills to the test! Good luck, and try not to get caught... or expelled.",
|
||||
"endGoal": "Recover the CyBOK LaTeX source HDD",
|
||||
"startRoom": "reception",
|
||||
"rooms": {
|
||||
@@ -24,12 +24,12 @@
|
||||
"name": "Lockpicking Hint Notice",
|
||||
"takeable": true,
|
||||
"readable": true,
|
||||
"text": "DEPARTMENT NOTICE:\n\nMany doors use pin tumbler locks.\n\nTip: Apply tension to the lock while manipulating the pins with a pick.\n\nDo NOT attempt this in front of colleagues.",
|
||||
"text": "DEPARTMENT NOTICE:\n\nMany doors use pin tumbler locks.\n\nTip: Using a lockpick set, apply tension to the lock while manipulating the pins with a pick.\n\nDo NOT attempt this in front of colleagues.",
|
||||
"observations": "A cautionary notice about lockpicking techniques"
|
||||
},
|
||||
{
|
||||
"type": "bag",
|
||||
"name": "Lockpicking Backpack",
|
||||
"name": "Heist Gear Backpack",
|
||||
"contents": [
|
||||
{
|
||||
"type": "lockpick",
|
||||
@@ -60,10 +60,10 @@
|
||||
},
|
||||
"objects": [
|
||||
{
|
||||
"type": "bag1",
|
||||
"name": "Old Messenger Bag",
|
||||
"type": "briefcase",
|
||||
"name": "Professor's briefcase",
|
||||
"takeable": false,
|
||||
"observations": "A worn messenger bag sitting in the corner, partially open",
|
||||
"observations": "A worn briefcase sitting in the corner, partially open",
|
||||
"contents": [
|
||||
{
|
||||
"type": "notes",
|
||||
@@ -122,7 +122,7 @@
|
||||
"name": "Using Cyber Chef to Decode Base64",
|
||||
"takeable": true,
|
||||
"readable": true,
|
||||
"text": "CYBER CHEF TIPS:\n\nTo decode Base64 strings, use Cyber Chef's 'From Base64' operation.\n\nExample:\nInput: Y3lib2syMDI0\nOperation: From Base64\nOutput: cybok2024\n\nCyber Chef is a powerful tool for all your encoding and decoding needs!",
|
||||
"text": "CYBER CHEF TIPS:\n\nTo decode Base64 strings, use Cyber Chef's 'From Base64' operation.\n\nExample:\nInput: Y3lib2syMDI0\nOperation: From Base64\nOutput: cybok2024\n\nCyber Chef is a powerful tool for all your encoding and decoding needs!\n\nA good clue that a string is Base64 is if it ends with '=' or '=='.\n\nHappy decoding!",
|
||||
"observations": "A quick reference guide for using Cyber Chef to decode Base64"
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user