minigame 2 just begin1.4
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 203 B |
|
After Width: | Height: | Size: 236 B |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 507 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 473 B |
|
After Width: | Height: | Size: 485 B |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 256 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@@ -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;
|
||||
|
||||
@@ -1462,7 +1462,7 @@
|
||||
</div>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0166"></script>
|
||||
<script src="js/play.js?v=0.189"></script>
|
||||
<script src="js/play.js?v=0.192"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||