update minigame 2
This commit is contained in:
@@ -51,9 +51,9 @@
|
||||
"providerUserId": "p_1775109142385_wq7wfy1p32j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 3064,
|
||||
"coins": 3082,
|
||||
"createdAt": "2026-04-02T05:52:21+00:00",
|
||||
"updatedAt": "2026-07-03T04:30:33+00:00",
|
||||
"updatedAt": "2026-07-03T10:29:39+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781197200000,
|
||||
"claimedDays": [
|
||||
@@ -67,7 +67,7 @@
|
||||
],
|
||||
"lockUntilMs": 0
|
||||
},
|
||||
"score": 990,
|
||||
"score": 1008,
|
||||
"scoreByCase": {
|
||||
"1": 26,
|
||||
"10": 970,
|
||||
@@ -76,11 +76,11 @@
|
||||
"13": 368,
|
||||
"14": 30,
|
||||
"9": 40,
|
||||
"12": 466,
|
||||
"12": 476,
|
||||
"15": 20,
|
||||
"4": 20,
|
||||
"5": 32,
|
||||
"7": 68,
|
||||
"7": 76,
|
||||
"3": 170,
|
||||
"6": 44,
|
||||
"2": 284
|
||||
@@ -456,17 +456,19 @@
|
||||
"providerUserId": "p_1781850663247_l0g0d1r7pcm",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 14,
|
||||
"coins": 36,
|
||||
"createdAt": "2026-06-19T06:31:03+00:00",
|
||||
"updatedAt": "2026-07-03T04:30:33+00:00",
|
||||
"score": 14,
|
||||
"updatedAt": "2026-07-03T10:29:39+00:00",
|
||||
"score": 36,
|
||||
"scoreByCase": {
|
||||
"10": 12,
|
||||
"13": 2
|
||||
"13": 2,
|
||||
"7": 6,
|
||||
"12": 16
|
||||
},
|
||||
"lbName": "sdd",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 3
|
||||
"d1_minigame_solver": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1190,29 +1192,39 @@
|
||||
"providerUserId": "p_1783048961594_ret1ovutfmg",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 18,
|
||||
"coins": 48,
|
||||
"createdAt": "2026-07-03T03:22:43+00:00",
|
||||
"updatedAt": "2026-07-03T04:30:33+00:00",
|
||||
"score": 18,
|
||||
"updatedAt": "2026-07-03T10:29:39+00:00",
|
||||
"score": 48,
|
||||
"scoreByCase": {
|
||||
"10": 18
|
||||
"10": 18,
|
||||
"7": 10,
|
||||
"12": 20
|
||||
},
|
||||
"lbName": "MONE",
|
||||
"lbName": "ผู้เล่น",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 4
|
||||
"d1_minigame_solver": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dc5f68fed544857b2b8b7aa1",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"displayName": "MONE",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1783049887754_gy0fhp1tq7",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"coins": 10,
|
||||
"createdAt": "2026-07-03T03:38:25+00:00",
|
||||
"updatedAt": "2026-07-03T03:38:25+00:00"
|
||||
"updatedAt": "2026-07-03T10:29:39+00:00",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 2
|
||||
},
|
||||
"score": 10,
|
||||
"scoreByCase": {
|
||||
"12": 10
|
||||
},
|
||||
"lbName": "MONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2031,12 +2031,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** ทุกคนจริง (me + others ที่ไม่ใช่บอทเติม) ตกขอบหมด = จบเกมเป็น gameover ทันที (ไม่ต้องรอเดินเข้าเส้นชัย)
|
||||
บอทเติม (__lobby_bot_N) ไม่นับ — server ตัดสิน all_dead จาก space.peers (คนจริงเท่านั้น) เหมือนกัน
|
||||
เดิมนับบอทด้วยเมื่อ playBotsEnabled()=true (ห้องจริงเปิดเสมอ) → บอทไม่ตายเลย = เช็คนี้ไม่ทริกเกอร์เลย */
|
||||
function gauntletCrownAllEntitiesEliminatedPlay() {
|
||||
let total = 0, elim = 0;
|
||||
if (Number.isFinite(me.x) || Number.isFinite(me.y)) { total++; if (me.gauntletEliminated) elim++; }
|
||||
others.forEach((o, id) => {
|
||||
if (!o) return;
|
||||
if (isPreviewBotId(id)) return;
|
||||
total++; if (o.gauntletEliminated) elim++;
|
||||
});
|
||||
return total >= 1 && elim === total;
|
||||
}
|
||||
|
||||
function tickGauntletCrownRunwayBgPlay(dtSec) {
|
||||
if (!gauntletCrownRunwayBgDrawActivePlay()) return;
|
||||
/* หยุดเลื่อน BG ระหว่างคำถามพิเศษ (special-quiz freeze) — กัน BG/เส้นชัยเดินตอนค้างตอบ */
|
||||
if (specialQuizFreeze) return;
|
||||
/** พรีรัน (howto / countdown) — ห้ามเลื่อนก่อน GO · Mega Virus ใช้ shell เดียวกัน */
|
||||
if (usesCrownLobbyShellPlay() && gauntletCrownPregamePhase !== 'live') return;
|
||||
/* ทุกคนตกขอบหมด → เข้า flow gameover ทันที (ไม่รอ scroll เดินเข้าเส้นชัย) — server ก็จบเกม all_dead พร้อมกัน */
|
||||
if (playBotsEnabled() && gauntletCrownRunwayBgMapEligiblePlay()
|
||||
&& !gauntletCrownRunwayClientMissionShown && gauntletCrownAllEntitiesEliminatedPlay()) {
|
||||
gauntletCrownRunwayClientMissionShown = true;
|
||||
gauntletCrownRunwayWalkActive = false;
|
||||
const mg2AllDeadMission = gauntletCrownHeistBuildLocalCrownMissionPlay();
|
||||
beginSimpleResultFlashThenGcmPlay(mg2AllDeadMission, 'result-gameover.png');
|
||||
try { draw(); } catch (_d) { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
if (gauntletCrownRunwayBgFinishLatched) { gauntletSnapLivingToFinishPlay(); return; }
|
||||
|
||||
/* เฟส "เดินเข้าเส้นชัย" — ค่อยๆ เดินทุกคนมาที่เส้นแทน snap กระพริบ แล้วค่อยเข้า freeze→จบ */
|
||||
@@ -3926,8 +3950,8 @@
|
||||
function drawQuizWrongGhostOverlayPlay(sxCenter, syBottom, heightPx, entId, nameLabel) {
|
||||
const img = getQuizWrongGhostImg();
|
||||
if (!img || !img.complete || !img.naturalWidth) return;
|
||||
/* ผีสูงเท่า avatar ตามจริง — คงสัดส่วนภาพ */
|
||||
const hPx = Math.max(24, Number(heightPx) || 0);
|
||||
/* ผีเล็กลง 25% จากความสูง avatar (คงสัดส่วนภาพ · เท้ายังตรงพื้น) */
|
||||
const hPx = (Math.max(24, Number(heightPx) || 0)) * 0.75;
|
||||
const ratio = img.naturalWidth / Math.max(1, img.naturalHeight);
|
||||
const wPx = hPx * ratio;
|
||||
const sid = String(entId == null ? '' : entId);
|
||||
@@ -6535,6 +6559,31 @@
|
||||
}
|
||||
|
||||
function isFrogger() { return mapData && mapData.gameType === 'frogger'; }
|
||||
/* net-ready badge: emit client-ready ครั้งเดียวเมื่อฉากพร้อม */
|
||||
let playClientReadyEmitted = false;
|
||||
/* วาด badge "กำลังโหลด…" เหนือหัว peer ที่ยังโหลดไม่เสร็จ (netReady=false) — คนอื่นรู้ว่าไม่ได้ค้าง */
|
||||
function drawNetLoadingBadgePlay(axTile, ayTile, w2s, zDrawIn) {
|
||||
try {
|
||||
const fw = getCharacterFootprintWH(mapData);
|
||||
const cxw = (axTile + (fw.cw || 1) * 0.5) * tileSize;
|
||||
const topYw = ayTile * tileSize;
|
||||
const scr = w2s(cxw, topYw);
|
||||
const sx = scr[0], sy = scr[1];
|
||||
const now = Date.now();
|
||||
const spin = ['◐', '◓', '◑', '◒'][Math.floor(now / 180) % 4];
|
||||
const txt = spin + ' กำลังโหลด…';
|
||||
ctx.save();
|
||||
ctx.font = '700 13px Kanit, system-ui, sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
const tw = ctx.measureText(txt).width + 12;
|
||||
const by = sy - 46, bh = 20;
|
||||
ctx.fillStyle = 'rgba(10,16,30,0.82)';
|
||||
if (ctx.roundRect) { ctx.beginPath(); ctx.roundRect(sx - tw / 2, by, tw, bh, 6); ctx.fill(); } else ctx.fillRect(sx - tw / 2, by, tw, bh);
|
||||
ctx.fillStyle = '#ffd34d'; ctx.textBaseline = 'middle';
|
||||
ctx.fillText(txt, sx, by + bh / 2 + 1);
|
||||
ctx.restore();
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
/* ===== Sound (jdSound) — scene + end-sfx helpers ===== */
|
||||
var JD_SCENE_MAP = { quiz: 'mg1', gauntlet: 'mg2', stack: 'mg3', quiz_carry: 'mg4', jump_survive: 'mg5', space_shooter: 'mg6', balloon_boss: 'mg7' };
|
||||
function jdPlayScene() { var gt = mapData && mapData.gameType; return (gt && JD_SCENE_MAP[gt]) || 'mg1'; }
|
||||
@@ -20649,6 +20698,7 @@
|
||||
x: px, y: py, tx: px, ty: py,
|
||||
direction: data.direction || 'down', nickname: data.nickname,
|
||||
characterId: data.characterId ?? null,
|
||||
netReady: (data.nr !== false && data.netReady !== false), /* badge กำลังโหลด ตอนเพิ่ง join */
|
||||
playerKey: joinPkDedup || null, /* ใช้ dedup ผีตอน reconnect (เก็บไว้เทียบรอบหน้า) */
|
||||
lobbyThemeIndexSrv: parsePlayLobbyThemeIndex(data.lobbyColorThemeIndex) || null, /* สี server-authoritative → block MG3 ตรงทุกจอ */
|
||||
spawnJoinOrder: spawnOrdJ,
|
||||
@@ -20710,6 +20760,7 @@
|
||||
กัน host นับผู้ถูกแบนเป็นผู้เล่นที่ต้องรอ Ready → humansReady ไม่ครบ → เริ่มเกมไม่ได้ */
|
||||
const nb = !!p.bannedSpectator;
|
||||
if (existing.bannedSpectator !== nb) { existing.bannedSpectator = nb; changed = true; }
|
||||
if (p.nr !== undefined) existing.netReady = p.nr !== false;
|
||||
return;
|
||||
}
|
||||
const px = Number(p.x);
|
||||
@@ -20722,6 +20773,7 @@
|
||||
direction: p.direction || 'down',
|
||||
nickname: p.nickname,
|
||||
characterId: p.characterId ?? null,
|
||||
netReady: p.nr !== false,
|
||||
playerKey: pkSnap || null, /* dedup ผี reconnect */
|
||||
lobbyThemeIndexSrv: parsePlayLobbyThemeIndex(p.lobbyColorThemeIndex) || null, /* สี server-authoritative → block MG3 ตรงทุกจอ */
|
||||
spawnJoinOrder: Number.isFinite(joP) ? Math.max(0, Math.floor(joP)) : 0,
|
||||
@@ -21492,6 +21544,13 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
/* สถานะ net ของ peer (โหลดเสร็จ) → เอา badge "กำลังโหลด" ออก */
|
||||
socket.on('peer-net', (data) => {
|
||||
if (!data || data.id == null) return;
|
||||
const o = others.get(data.id) || others.get(String(data.id));
|
||||
if (o) o.netReady = data.nr !== false;
|
||||
});
|
||||
|
||||
socket.on('user-left', (data) => {
|
||||
if (!data || isPreviewBotId(data.id)) return;
|
||||
const leftWasCurrentStack = !!(isStack() && detectiveCaseFillBots && isMePlayHost()
|
||||
@@ -26265,6 +26324,7 @@
|
||||
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletCrownPenaltyFxUntil || 0) : 0,
|
||||
id);
|
||||
}
|
||||
if (o.netReady === false && !isPreviewBotId(id)) drawNetLoadingBadgePlay(axO, ayO, worldToScreen, zDraw);
|
||||
if (botOut) ctx.restore();
|
||||
} else {
|
||||
if (shouldGauntletCrownHeistSkipAvatarDrawPlay(me, true, 0, 0)) return;
|
||||
@@ -26586,6 +26646,8 @@
|
||||
if (!mapData) { playTickLastTs = 0; requestAnimationFrame(tick); return; }
|
||||
/* BGM ต่อมินิเกม (idempotent — playBgm return เร็วถ้า scene เดิม) */
|
||||
if (window.jdSound) { try { window.jdSound.playBgm(jdPlayScene()); } catch (e) { /* ignore */ } }
|
||||
/* โหลดฉากมินิเกมเสร็จ (ครั้งเดียว) → เอา badge "กำลังโหลด" ออกให้คนอื่นเห็น */
|
||||
if (!playClientReadyEmitted && socket && socket.connected) { playClientReadyEmitted = true; try { socket.emit('client-ready'); } catch (e) { /* ignore */ } }
|
||||
/* dt frame (วินาที) → playDtMul = dt*60 (clamp 0.05s = step ≤0.45 tile กัน tunneling) ใช้ทำ movement time-based */
|
||||
{
|
||||
const _pnow = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -316,7 +316,8 @@
|
||||
const y = Number(p.y);
|
||||
const nx = Number.isFinite(x) ? x : fx;
|
||||
const ny = Number.isFinite(y) ? y : fy;
|
||||
return { ...p, x: nx, y: ny, tx: nx, ty: ny };
|
||||
/* netReady: false = เพิ่ง join กำลังโหลด (snap ส่ง nr, user-joined ส่ง netReady) → โชว์ badge */
|
||||
return { ...p, x: nx, y: ny, tx: nx, ty: ny, netReady: (p.nr !== false && p.netReady !== false) };
|
||||
}
|
||||
const characterImages = {};
|
||||
|
||||
@@ -614,6 +615,8 @@
|
||||
try { lobbyLog('loading-hide', 'เริ่มเล่นได้'); } catch (e) { /* ignore */ }
|
||||
/* แจ้ง server ว่าโหลดฉากเสร็จ → นับเป็น present ใน LobbyB จริง (กัน host เริ่มก่อนคนโหลดเสร็จ) */
|
||||
try { if (isPostCaseLobbyRoom() && socket && socket.connected) socket.emit('lobbyb-loaded'); } catch (e) { /* ignore */ }
|
||||
/* โหลดฉากเสร็จ (ทุกล็อบบี้) → เอา badge "กำลังโหลด" ออกให้คนอื่นเห็น */
|
||||
try { if (socket && socket.connected) socket.emit('client-ready'); } catch (e) { /* ignore */ }
|
||||
var ov = document.getElementById('room-loading-overlay');
|
||||
if (ov) { ov.classList.add('is-hidden'); setTimeout(function () { if (ov.parentNode) ov.parentNode.removeChild(ov); }, 600); }
|
||||
}
|
||||
@@ -1802,6 +1805,22 @@
|
||||
const labelY = cellBottom - drawH - (tileSize * 0.2);
|
||||
const headTop = cellBottom - drawH - 4;
|
||||
const now = Date.now();
|
||||
/* badge "กำลังโหลด…" เหนือหัว — คนที่เพิ่ง join ยังโหลดไม่เสร็จ (netReady=false) → คนอื่นเห็นว่าไม่ได้ค้าง */
|
||||
if (p.netReady === false) {
|
||||
const spin = ['◐', '◓', '◑', '◒'][Math.floor(now / 180) % 4];
|
||||
const txt = spin + ' กำลังโหลด…';
|
||||
ctx.save();
|
||||
ctx.font = '700 ' + Math.max(9, Math.round(tileSize * 0.32)) + 'px Kanit, system-ui, sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
const tw = ctx.measureText(txt).width + 12;
|
||||
const bx = cx - tw / 2, by = headTop - Math.round(tileSize * 0.5), bh2 = Math.max(15, Math.round(tileSize * 0.42));
|
||||
ctx.fillStyle = 'rgba(10,16,30,0.82)';
|
||||
if (ctx.roundRect) { ctx.beginPath(); ctx.roundRect(bx, by, tw, bh2, 6); ctx.fill(); } else ctx.fillRect(bx, by, tw, bh2);
|
||||
ctx.fillStyle = '#ffd34d';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(txt, cx, by + bh2 / 2 + 1);
|
||||
ctx.restore();
|
||||
}
|
||||
if (p.speakingUntil > now) {
|
||||
var bubbleImg = getSpeakingBubbleFrame();
|
||||
if (bubbleImg) {
|
||||
@@ -4214,6 +4233,13 @@
|
||||
if (window.jdSound) window.jdSound.notifySpeaking(); /* หรี่ BGM ตอน peer อื่นพูด */
|
||||
});
|
||||
|
||||
/* สถานะ net ของ peer (โหลดเสร็จ/lag) → เอา/ใส่ badge "กำลังโหลด" */
|
||||
socket.on('peer-net', function (data) {
|
||||
if (!data || data.id == null) return;
|
||||
var p = peers.get(data.id);
|
||||
if (p) { p.netReady = data.nr !== false; redrawLobbyMap(); try { renderPeers(); } catch (e) { /* ignore */ } }
|
||||
});
|
||||
|
||||
const iceQueue = {};
|
||||
const defaultIceServers = [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }];
|
||||
let cachedIceServers = null;
|
||||
@@ -7543,6 +7569,7 @@
|
||||
if (row.nickname) p.nickname = row.nickname;
|
||||
p.ready = !!row.ready;
|
||||
if (row.characterId != null) p.characterId = row.characterId;
|
||||
if (row.nr !== undefined) p.netReady = row.nr !== false;
|
||||
});
|
||||
const meAfter = peers.get(socket.id);
|
||||
if (readyCheck && meAfter) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5462,7 +5462,7 @@
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0306"></script>
|
||||
<script src="js/sound.js?v=1"></script>
|
||||
<script src="js/play.js?v=0.006301070"></script>
|
||||
<script src="js/play.js?v=0.006301074"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
<style id="qc-howto-mg2-fix">
|
||||
/* HOW TO PLAY ของ quiz_carry (Minigame-4) -> ให้เหมือน Minigame-2 (gch-mg2-mock)
|
||||
|
||||
@@ -1649,7 +1649,7 @@
|
||||
<script src="js/customize-popup.js?v=40" data-customize-triggers="" data-customize-asset-base="img/03-5-Customize"></script>
|
||||
<script src="js/achievements.js?v=0.002" data-asset-base="img/03-6-Profile"></script>
|
||||
<script src="js/sound.js?v=1"></script>
|
||||
<script src="js/room-lobby.js?v=0.0344"></script>
|
||||
<script src="js/room-lobby.js?v=0.0345"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+67
-1
@@ -3448,6 +3448,7 @@ function buildPeersSnapForSpace(space, extraFields) {
|
||||
lobbyColorThemeIndex: p.lobbyColorThemeIndex || null,
|
||||
lobbySkinToneIndex: p.lobbySkinToneIndex || null,
|
||||
bannedSpectator: !!p.bannedSpectator,
|
||||
nr: p.netReady !== false, /* net-ready: false = เพิ่ง join กำลังโหลด (client โชว์ badge) */
|
||||
};
|
||||
if (extraFields && extraFields.gauntlet) {
|
||||
row.gauntletJumpTicks = p.gauntletJumpTicks || 0;
|
||||
@@ -7896,6 +7897,7 @@ function newGauntletRunState(space) {
|
||||
crownRunHeld: crown,
|
||||
finishPhase: false,
|
||||
finishEndAt: null,
|
||||
gameoverAt: null, /* != null = อยู่ในช่วง grace "ตายหมด" (โชว์ game over ก่อนกลับ LobbyB) */
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9542,7 +9544,8 @@ function endGauntletGame(sid, space, reason) {
|
||||
const md = (space.mapId && maps.get(space.mapId)) || space.mapData;
|
||||
if (!md || md.gameType !== 'gauntlet') return;
|
||||
if (space.detectiveMinigameActive) {
|
||||
const msg = reason === 'time' ? 'หมดเวลา — จบมินิเกม' : 'จบมินิเกม';
|
||||
const msg = reason === 'time' ? 'หมดเวลา — จบมินิเกม'
|
||||
: (reason === 'all_dead' ? 'ตกขอบหมดแล้ว — จบมินิเกม' : 'จบมินิเกม');
|
||||
returnDetectiveSpaceToLobbyB(sid, space, msg, { allPlayers: true });
|
||||
return;
|
||||
}
|
||||
@@ -9654,10 +9657,31 @@ function runGauntletTick(sid, space) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ตายหมดแล้ว (all_dead) — อยู่ในช่วง grace: หยุดเกม + ให้เวลา client วาดจอ result-gameover → GCM
|
||||
ก่อน server ดึงกลับ LobbyB. เดิม all_dead จบ+return ทันที (0 grace) = client ยังไม่ทันวาด game over
|
||||
ก็โดนดึงกลับ = "ตายหมดแต่ไม่ขึ้น game over". ใช้ grace เท่ากับทางชนะ (GAUNTLET_FINISH_WALK_MS) */
|
||||
if (gr.gameoverAt != null) {
|
||||
if (gr.obstacles && gr.obstacles.length) gr.obstacles = [];
|
||||
if (Date.now() >= gr.gameoverAt) {
|
||||
endGauntletGame(sid, space, 'all_dead');
|
||||
return;
|
||||
}
|
||||
/* ไม่ emit sync ระหว่าง grace — ปล่อย client คุม overlay result เอง (เหมือน finish grace) */
|
||||
return;
|
||||
}
|
||||
|
||||
/* เข้าโซนเส้นชัยแล้ว (client แจ้ง) — หยุด obstacle/collision ทันที แล้วรอ grace ให้ client
|
||||
เดินทุกคน "ค่อยๆ" เข้าเส้นชัยจนครบ ค่อยสรุปผล (ไม่ snap กระพริบ / ไม่จบทันที) */
|
||||
if (gr.finishPhase) {
|
||||
if (gr.obstacles.length) { gr.obstacles = []; }
|
||||
/* ตายหมดตั้งแต่ก่อน/ระหว่าง finish-walk grace → เข้า grace game over (แทน result-complete จากทางเส้นชัย)
|
||||
(race: client ตัวหนึ่ง scroll ถึงเส้นชัยก่อน → set finishPhase → early-return ข้ามเช็ค all_dead ท้าย tick) */
|
||||
if (gauntletAllHumansEliminated(space)) {
|
||||
gr.gameoverAt = Date.now() + GAUNTLET_FINISH_WALK_MS;
|
||||
gr.obstacles = [];
|
||||
try { glog(sid, space, 'gauntlet-end', 'ตายหมด (ระหว่าง finish-phase) → grace game over'); } catch (e) { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
if (gr.finishEndAt == null) gr.finishEndAt = Date.now() + GAUNTLET_FINISH_WALK_MS;
|
||||
if (Date.now() >= gr.finishEndAt) {
|
||||
endGauntletGame(sid, space, 'finish');
|
||||
@@ -9808,9 +9832,34 @@ function runGauntletTick(sid, space) {
|
||||
});
|
||||
}
|
||||
|
||||
/* ผู้เล่น (คนจริง) ตกขอบหมด → เข้า grace game over (ไม่รอหมดเวลา) แล้วค่อย return LobbyB
|
||||
ส่ง sync ครั้งสุดท้าย (ทุกคน eliminated) ให้ client ทริก overlay result-gameover ก่อนหมด grace */
|
||||
if (gauntletAllHumansEliminated(space)) {
|
||||
gr.gameoverAt = Date.now() + GAUNTLET_FINISH_WALK_MS;
|
||||
gr.obstacles = [];
|
||||
try { glog(sid, space, 'gauntlet-end', 'ผู้เล่นตกขอบหมด → grace game over แล้วกลับ LobbyB'); } catch (e) { /* ignore */ }
|
||||
emitGauntletSync(sid, space);
|
||||
return;
|
||||
}
|
||||
|
||||
emitGauntletSync(sid, space);
|
||||
}
|
||||
|
||||
/** MG2 crown/gauntlet: คนจริงที่ "ยังเล่นอยู่" (ไม่นับผู้ถูกแบน/ผู้ชม) ตกขอบหมด = จบเกม all_dead
|
||||
บอทอยู่ใน gr.bots ไม่ใช่ space.peers → ไม่ถูกนับอยู่แล้ว (server ตัดสินจากคนจริงเท่านั้น)
|
||||
- นับเฉพาะ crown map · ต้องมีคนเล่นจริง ≥1 คน (กันจบทันทีตอน 0 คน) */
|
||||
function gauntletAllHumansEliminated(space) {
|
||||
if (!isGauntletCrownHeistMapBySpace(space) || !space.peers || space.peers.size === 0) return false;
|
||||
let active = 0;
|
||||
let alive = 0;
|
||||
space.peers.forEach((p) => {
|
||||
if (!p || p.bannedSpectator) return; /* ผู้ถูกแบน = เข้ามาดู ไม่ได้เล่น → ไม่บล็อกการจบเกม */
|
||||
active++;
|
||||
if (!p.gauntletEliminated) alive++;
|
||||
});
|
||||
return active > 0 && alive === 0;
|
||||
}
|
||||
|
||||
function rescheduleAllGauntletTickers() {
|
||||
for (const [sid, space] of spaces) {
|
||||
const md = (space.mapId && maps.get(space.mapId)) || space.mapData;
|
||||
@@ -10738,6 +10787,7 @@ io.on('connection', (socket) => {
|
||||
playerKey: (typeof playerKey === 'string' && /^[a-zA-Z0-9_-]{8,128}$/.test(playerKey)) ? playerKey : '',
|
||||
reportedMiniScore: 0, reportedMiniSurvived: true,
|
||||
bannedSpectator: !!bannedSpectator,
|
||||
netReady: false, /* เพิ่ง join → กำลังโหลด จนกว่า client ส่ง client-ready (badge ให้คนอื่นเห็นว่า "กำลังโหลด" ไม่ใช่ค้าง) */
|
||||
};
|
||||
if (mdJoin.gameType === 'quiz_battle' && quizBattlePathModeActiveServer(mdJoin)) {
|
||||
const sn = quizBattleSpawnWorldFromJoinOrderServer(mdJoin, spawnJoinOrder);
|
||||
@@ -11848,6 +11898,9 @@ io.on('connection', (socket) => {
|
||||
try { glog(sid, space, 'story-gate', '[TC156] ทุกคนอ่าน story ครบ (' + st.total + ') → ปลดล็อกเลือกผู้ต้องสงสัย (pass)'); } catch (e) { /* ignore */ }
|
||||
}
|
||||
emitDetectiveStoryReadPresence(sid, space);
|
||||
/* piggyback เพิ่ม lobbyBLoadedKeys แล้ว → ต้อง broadcast lobbyb-presence ใหม่ด้วย
|
||||
ไม่งั้น client ค้างโชว์ "รอผู้เล่น (2/3)" (ค่าเก่า) → ปุ่ม host ถูก disable ทั้งที่ครบแล้ว */
|
||||
emitDetectiveLobbyBPresence(sid, space);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -11872,6 +11925,19 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
});
|
||||
|
||||
/* client โหลดฉากเสร็จ (ทุกฉาก lobby/minigame) → เอา badge "กำลังโหลด" ออก (คนอื่นเห็นว่าพร้อมแล้ว) */
|
||||
socket.on('client-ready', () => {
|
||||
for (const [sid, space] of spaces) {
|
||||
if (!space.peers.has(socket.id)) continue;
|
||||
const p = space.peers.get(socket.id);
|
||||
if (p && p.netReady !== true) {
|
||||
p.netReady = true;
|
||||
io.to(sid).emit('peer-net', { id: socket.id, nr: true });
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('start-game', (data, cb) => {
|
||||
const reply = typeof cb === 'function' ? cb : () => {};
|
||||
for (const [sid, space] of spaces) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user