diff --git a/www/html/Game/public/img/gauntlet-assets/1.png b/www/html/Game/public/img/gauntlet-assets/1.png new file mode 100644 index 0000000..2646cbd Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/1.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/2.png b/www/html/Game/public/img/gauntlet-assets/2.png new file mode 100644 index 0000000..58c47cc Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/2.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/3.png b/www/html/Game/public/img/gauntlet-assets/3.png new file mode 100644 index 0000000..b479ba8 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/3.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/Avartar-mark.png b/www/html/Game/public/img/gauntlet-assets/Avartar-mark.png new file mode 100644 index 0000000..45a6644 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/Avartar-mark.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/Avartar.png b/www/html/Game/public/img/gauntlet-assets/Avartar.png new file mode 100644 index 0000000..dabf3ad Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/Avartar.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/BG-01-start.png b/www/html/Game/public/img/gauntlet-assets/BG-01-start.png new file mode 100644 index 0000000..45c4ca6 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/BG-01-start.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/BG-02-loop.png b/www/html/Game/public/img/gauntlet-assets/BG-02-loop.png new file mode 100644 index 0000000..d2f9c59 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/BG-02-loop.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/BG-03-loop.png b/www/html/Game/public/img/gauntlet-assets/BG-03-loop.png new file mode 100644 index 0000000..2b71ebe Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/BG-03-loop.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/BG-04-loop.png b/www/html/Game/public/img/gauntlet-assets/BG-04-loop.png new file mode 100644 index 0000000..8a3a2ab Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/BG-04-loop.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/BG-05-finish.png b/www/html/Game/public/img/gauntlet-assets/BG-05-finish.png new file mode 100644 index 0000000..ed09b3b Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/BG-05-finish.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-done.png b/www/html/Game/public/img/gauntlet-assets/btn-done.png new file mode 100644 index 0000000..6ea7618 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-done.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-joystick-1.png b/www/html/Game/public/img/gauntlet-assets/btn-joystick-1.png new file mode 100644 index 0000000..a521d5a Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-joystick-1.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-joystick-2.png b/www/html/Game/public/img/gauntlet-assets/btn-joystick-2.png new file mode 100644 index 0000000..21b348f Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-joystick-2.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-jump.png b/www/html/Game/public/img/gauntlet-assets/btn-jump.png new file mode 100644 index 0000000..6fd2381 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-jump.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-mic_off.png b/www/html/Game/public/img/gauntlet-assets/btn-mic_off.png new file mode 100644 index 0000000..426093d Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-mic_off.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-mic_on.png b/www/html/Game/public/img/gauntlet-assets/btn-mic_on.png new file mode 100644 index 0000000..755bb05 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-mic_on.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-ready.png b/www/html/Game/public/img/gauntlet-assets/btn-ready.png new file mode 100644 index 0000000..6b50bfc Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-ready.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/btn-start.png b/www/html/Game/public/img/gauntlet-assets/btn-start.png new file mode 100644 index 0000000..cdc7776 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/btn-start.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/mission-complete-select.png b/www/html/Game/public/img/gauntlet-assets/mission-complete-select.png new file mode 100644 index 0000000..0d761e3 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/mission-complete-select.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/mission-complete.png b/www/html/Game/public/img/gauntlet-assets/mission-complete.png new file mode 100644 index 0000000..0b15d97 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/mission-complete.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/obstacles-1.png b/www/html/Game/public/img/gauntlet-assets/obstacles-1.png new file mode 100644 index 0000000..d0a6b7b Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/obstacles-1.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/obstacles-2.png b/www/html/Game/public/img/gauntlet-assets/obstacles-2.png new file mode 100644 index 0000000..9f41af2 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/obstacles-2.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/obstacles-3.png b/www/html/Game/public/img/gauntlet-assets/obstacles-3.png new file mode 100644 index 0000000..366768c Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/obstacles-3.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/obstacles-4.png b/www/html/Game/public/img/gauntlet-assets/obstacles-4.png new file mode 100644 index 0000000..7eed16e Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/obstacles-4.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/obstacles-5.png b/www/html/Game/public/img/gauntlet-assets/obstacles-5.png new file mode 100644 index 0000000..b7b678c Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/obstacles-5.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/player-frame.png b/www/html/Game/public/img/gauntlet-assets/player-frame.png new file mode 100644 index 0000000..02433d8 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/player-frame.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/popup-Howto.png b/www/html/Game/public/img/gauntlet-assets/popup-Howto.png new file mode 100644 index 0000000..a9b8ecf Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/popup-Howto.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/popup-result-Line1.png b/www/html/Game/public/img/gauntlet-assets/popup-result-Line1.png new file mode 100644 index 0000000..07cbd6c Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/popup-result-Line1.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/popup-result-Line2.png b/www/html/Game/public/img/gauntlet-assets/popup-result-Line2.png new file mode 100644 index 0000000..69f13d8 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/popup-result-Line2.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/popup-result.png b/www/html/Game/public/img/gauntlet-assets/popup-result.png new file mode 100644 index 0000000..340a388 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/popup-result.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-1st.png b/www/html/Game/public/img/gauntlet-assets/result-1st.png new file mode 100644 index 0000000..0805bea Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-1st.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-2nd.png b/www/html/Game/public/img/gauntlet-assets/result-2nd.png new file mode 100644 index 0000000..4332a29 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-2nd.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-3rd.png b/www/html/Game/public/img/gauntlet-assets/result-3rd.png new file mode 100644 index 0000000..dfdb3a2 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-3rd.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-Lose_stamp.png b/www/html/Game/public/img/gauntlet-assets/result-Lose_stamp.png new file mode 100644 index 0000000..562b3fe Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-Lose_stamp.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-check.png b/www/html/Game/public/img/gauntlet-assets/result-check.png new file mode 100644 index 0000000..8924fc8 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-check.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-complete.png b/www/html/Game/public/img/gauntlet-assets/result-complete.png new file mode 100644 index 0000000..4b5f062 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-complete.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-gameover.png b/www/html/Game/public/img/gauntlet-assets/result-gameover.png new file mode 100644 index 0000000..610c6ec Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-gameover.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-txt-1.png b/www/html/Game/public/img/gauntlet-assets/result-txt-1.png new file mode 100644 index 0000000..5e7d3d5 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-txt-1.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/result-txt-2.png b/www/html/Game/public/img/gauntlet-assets/result-txt-2.png new file mode 100644 index 0000000..8576e61 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/result-txt-2.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/score-.png b/www/html/Game/public/img/gauntlet-assets/score-.png new file mode 100644 index 0000000..7d629c9 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/score-.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/score-avartar-mark.png b/www/html/Game/public/img/gauntlet-assets/score-avartar-mark.png new file mode 100644 index 0000000..9a6b11a Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/score-avartar-mark.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/score-avartar.png b/www/html/Game/public/img/gauntlet-assets/score-avartar.png new file mode 100644 index 0000000..a4ffad9 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/score-avartar.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/score-num-effect.png b/www/html/Game/public/img/gauntlet-assets/score-num-effect.png new file mode 100644 index 0000000..40d6d5b Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/score-num-effect.png differ diff --git a/www/html/Game/public/img/gauntlet-assets/score.png b/www/html/Game/public/img/gauntlet-assets/score.png new file mode 100644 index 0000000..db271f3 Binary files /dev/null and b/www/html/Game/public/img/gauntlet-assets/score.png differ diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index c2a1a0c..5d095c1 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -5073,14 +5073,15 @@ px = Math.max(0, Math.min(w - 1, px)); py = Math.max(0, Math.min(h - 1, py)); if ((o.gauntletJumpTicks || 0) > 0) return; + const { ch: gauntletCh } = getCharacterFootprintWH(mapData); let sameCell = false; let incomingCol = false; for (let i = 0; i < obstacles.length; i++) { const obs = obstacles[i]; if (!obs) continue; if (obs.kind === 'lane' && typeof obs.y === 'number') { - if (obs.x === px && obs.y === py) sameCell = true; - if (obs.x === px + 1 && obs.y === py) incomingCol = true; + if (obs.x === px && obs.y >= py && obs.y < py + gauntletCh) sameCell = true; + if (obs.x === px + 1 && obs.y >= py && obs.y < py + gauntletCh) incomingCol = true; } if (obs.kind === 'laser' && typeof obs.x === 'number') { if (obs.x === px && gauntletLaserOverlapsPlayerRow(obs, py, h)) sameCell = true; @@ -5105,12 +5106,13 @@ px = Math.max(0, Math.min(w - 1, px)); py = Math.max(0, Math.min(h - 1, py)); const air = (o.gauntletJumpTicks || 0) > 0; + const { ch: gauntletCh2 } = getCharacterFootprintWH(mapData); let advanceX = false; let hitBack = false; for (let i = 0; i < obstacles.length; i++) { const ob = obstacles[i]; if (!ob) continue; - if (ob.kind === 'lane' && typeof ob.y === 'number' && ob.x === px && ob.y === py) { + if (ob.kind === 'lane' && typeof ob.y === 'number' && ob.x === px && ob.y >= py && ob.y < py + gauntletCh2) { if (air) advanceX = true; else hitBack = true; } @@ -9725,20 +9727,22 @@ if (!o) continue; if (o.kind === 'lane' && typeof o.y === 'number') { if (o.drawX < stx - 2 || o.drawX > enx + 2 || o.y < sty || o.y > eny) continue; - const wx = o.drawX * tileSize, wy = o.y * tileSize; - const [sx, sy] = worldToScreen(wx, wy); const size = tileSize * zDraw; + const inner = Math.max(2, size - 4); + const [sxC, syCellBottom] = worldToScreen((o.drawX + 0.5) * tileSize, (o.y + 1) * tileSize); + const dx = sxC - inner / 2; + const dy = syCellBottom - inner; const laneRec = pickGauntletLaneImageRec(o.id); if (laneRec && laneRec.img.complete && laneRec.img.naturalWidth > 0) { try { - ctx.drawImage(laneRec.img, sx + 2, sy + 2, size - 4, size - 4); + ctx.drawImage(laneRec.img, dx, dy, inner, inner); } catch (e) { ctx.fillStyle = '#f7768e'; - ctx.fillRect(sx + 2, sy + 2, size - 4, size - 4); + ctx.fillRect(dx, dy, inner, inner); } } else { ctx.fillStyle = '#f7768e'; - ctx.fillRect(sx + 2, sy + 2, size - 4, size - 4); + ctx.fillRect(dx, dy, inner, inner); } } else if (o.kind === 'laser' && typeof o.drawX === 'number') { if (o.drawX < stx - 2 || o.drawX > enx + 2) continue; diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index 621176d..1b0dc71 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -1462,7 +1462,7 @@ - +
v —
diff --git a/www/html/Game/public/play.js b/www/html/Game/public/play.js index c2a1a0c..5d095c1 100644 --- a/www/html/Game/public/play.js +++ b/www/html/Game/public/play.js @@ -5073,14 +5073,15 @@ px = Math.max(0, Math.min(w - 1, px)); py = Math.max(0, Math.min(h - 1, py)); if ((o.gauntletJumpTicks || 0) > 0) return; + const { ch: gauntletCh } = getCharacterFootprintWH(mapData); let sameCell = false; let incomingCol = false; for (let i = 0; i < obstacles.length; i++) { const obs = obstacles[i]; if (!obs) continue; if (obs.kind === 'lane' && typeof obs.y === 'number') { - if (obs.x === px && obs.y === py) sameCell = true; - if (obs.x === px + 1 && obs.y === py) incomingCol = true; + if (obs.x === px && obs.y >= py && obs.y < py + gauntletCh) sameCell = true; + if (obs.x === px + 1 && obs.y >= py && obs.y < py + gauntletCh) incomingCol = true; } if (obs.kind === 'laser' && typeof obs.x === 'number') { if (obs.x === px && gauntletLaserOverlapsPlayerRow(obs, py, h)) sameCell = true; @@ -5105,12 +5106,13 @@ px = Math.max(0, Math.min(w - 1, px)); py = Math.max(0, Math.min(h - 1, py)); const air = (o.gauntletJumpTicks || 0) > 0; + const { ch: gauntletCh2 } = getCharacterFootprintWH(mapData); let advanceX = false; let hitBack = false; for (let i = 0; i < obstacles.length; i++) { const ob = obstacles[i]; if (!ob) continue; - if (ob.kind === 'lane' && typeof ob.y === 'number' && ob.x === px && ob.y === py) { + if (ob.kind === 'lane' && typeof ob.y === 'number' && ob.x === px && ob.y >= py && ob.y < py + gauntletCh2) { if (air) advanceX = true; else hitBack = true; } @@ -9725,20 +9727,22 @@ if (!o) continue; if (o.kind === 'lane' && typeof o.y === 'number') { if (o.drawX < stx - 2 || o.drawX > enx + 2 || o.y < sty || o.y > eny) continue; - const wx = o.drawX * tileSize, wy = o.y * tileSize; - const [sx, sy] = worldToScreen(wx, wy); const size = tileSize * zDraw; + const inner = Math.max(2, size - 4); + const [sxC, syCellBottom] = worldToScreen((o.drawX + 0.5) * tileSize, (o.y + 1) * tileSize); + const dx = sxC - inner / 2; + const dy = syCellBottom - inner; const laneRec = pickGauntletLaneImageRec(o.id); if (laneRec && laneRec.img.complete && laneRec.img.naturalWidth > 0) { try { - ctx.drawImage(laneRec.img, sx + 2, sy + 2, size - 4, size - 4); + ctx.drawImage(laneRec.img, dx, dy, inner, inner); } catch (e) { ctx.fillStyle = '#f7768e'; - ctx.fillRect(sx + 2, sy + 2, size - 4, size - 4); + ctx.fillRect(dx, dy, inner, inner); } } else { ctx.fillStyle = '#f7768e'; - ctx.fillRect(sx + 2, sy + 2, size - 4, size - 4); + ctx.fillRect(dx, dy, inner, inner); } } else if (o.kind === 'laser' && typeof o.drawX === 'number') { if (o.drawX < stx - 2 || o.drawX > enx + 2) continue; diff --git a/www/html/Game/server.js b/www/html/Game/server.js index ec6cfb2..3a61e6c 100644 --- a/www/html/Game/server.js +++ b/www/html/Game/server.js @@ -2655,6 +2655,19 @@ function gauntletLaserVerticalSpanFromMap(md) { return { y0, y1 }; } +/** + * แถว grid ของ lane obstacle: แถวเท้า (ขอบล่าง footprint; แถวบนของตัว = player y) + * — สูงกว่าแบบ top+ch หนึ่งช่อง (ไม่ให้ลงเกินพรม) + */ +function gauntletLaneGridRowFromPlayerTop(md, topY, h) { + const { ch } = getCharacterFootprintWHForMove(md); + const top = Math.floor(Number(topY)); + if (!Number.isFinite(top) || top < 0 || top >= h) return null; + const lr = top + ch - 1; + if (lr < 0 || lr >= h) return null; + return lr; +} + function runGauntletTick(sid, space) { const md = (space.mapId && maps.get(space.mapId)) || space.mapData; if (!md || md.gameType !== 'gauntlet') { @@ -2684,23 +2697,23 @@ function runGauntletTick(sid, space) { for (let i = 0; i < gr.obstacles.length; i++) gr.obstacles[i].x -= 1; gr.obstacles = gr.obstacles.filter((o) => o.x >= 0); - const occupiedY = new Set(); + const laneSpawnRows = new Set(); space.peers.forEach((peer) => { - const fy = Math.floor(Number(peer.y)); - if (Number.isFinite(fy) && fy >= 0 && fy < h) occupiedY.add(fy); + const lr = gauntletLaneGridRowFromPlayerTop(md, peer.y, h); + if (lr != null) laneSpawnRows.add(lr); }); - /* แถวที่ client แจ้ง (บอท preview อยู่บน client ไม่ใช่ peer) — ให้ spawn lane บนแถวบอทด้วย */ + /* แถวบนที่ client แจ้ง (บอท preview) — แปลงเป็นแถว lane เดียวกับ peer */ const previewRows = space.gauntletPreviewRowsBySocket; if (previewRows && previewRows.size) { previewRows.forEach((set) => { set.forEach((y) => { - const fy = Math.floor(Number(y)); - if (Number.isFinite(fy) && fy >= 0 && fy < h) occupiedY.add(fy); + const lr = gauntletLaneGridRowFromPlayerTop(md, y, h); + if (lr != null) laneSpawnRows.add(lr); }); }); } - /* lane obstacle เฉพาะแถวที่มีคน — ลบชิ้นที่ลอยในแถวว่าง */ - gr.obstacles = gr.obstacles.filter((o) => o.kind !== 'lane' || occupiedY.has(o.y)); + /* lane obstacle เฉพาะแถวที่ยังมีผู้เล่น/บอทในเลนนั้น — ลบชิ้นที่ไม่มีใครแล้ว */ + gr.obstacles = gr.obstacles.filter((o) => o.kind !== 'lane' || laneSpawnRows.has(o.y)); gr.spawnAcc = (gr.spawnAcc || 0) + 1; if (gr.spawnAcc >= (gr.nextSpawnIn || 3)) { @@ -2711,8 +2724,8 @@ function runGauntletTick(sid, space) { const span = gauntletLaserVerticalSpanFromMap(md); gr.obstacles.push({ id: ++gr.nextObsId, kind: 'laser', x: w - 1, y0: span.y0, y1: span.y1 }); } - /* ประเภท 1: แยกเลน — เฉพาะแถวที่มีผู้เล่นยืนอยู่ (สุ่มแยกแถว) */ - occupiedY.forEach((ly) => { + /* ประเภท 1: แยกเลน — แถวเท้า (ไม่ใช่แถวหัว; ไม่ใช่แถวใต้เท้าอีกช่อง) */ + laneSpawnRows.forEach((ly) => { if (Math.random() < GAUNTLET_LANE_ROW_SPAWN_CHANCE) { gr.obstacles.push({ id: ++gr.nextObsId, kind: 'lane', x: w - 1, y: ly }); } @@ -2720,6 +2733,7 @@ function runGauntletTick(sid, space) { } /* กระโดดข้ามสำเร็จ → เลื่อนผู้เล่นไปขวา แต่ไม่ลบ obstacle (ยังไหลต่อให้คนอื่น/รอบถัดไป) */ + const { ch: gauntletCh } = getCharacterFootprintWHForMove(md); space.peers.forEach((p) => { let px = Math.floor(Number(p.x)) || 0; let py = Math.floor(Number(p.y)) || 0; @@ -2729,7 +2743,7 @@ function runGauntletTick(sid, space) { let advanceX = false; let hitBack = false; for (const o of gr.obstacles) { - if (o.kind === 'lane' && o.x === px && o.y === py) { + if (o.kind === 'lane' && o.x === px && o.y >= py && o.y < py + gauntletCh) { if (air) advanceX = true; else hitBack = true; }