update all game still have issue on mg 7 6 a bit

This commit is contained in:
2026-06-28 15:04:41 +00:00
parent 6bc2ccea19
commit 8efb7adedf
7 changed files with 1349 additions and 994 deletions
+11 -10
View File
@@ -51,9 +51,9 @@
"providerUserId": "p_1775109142385_wq7wfy1p32j",
"notes": "auto: player-coins",
"blocked": false,
"coins": 2382,
"coins": 2392,
"createdAt": "2026-04-02T05:52:21+00:00",
"updatedAt": "2026-06-27T18:23:36+00:00",
"updatedAt": "2026-06-28T14:47:24+00:00",
"daily": {
"anchorMs": 1781197200000,
"claimedDays": [
@@ -67,7 +67,7 @@
],
"lockUntilMs": 0
},
"score": 308,
"score": 318,
"scoreByCase": {
"1": 26,
"10": 676,
@@ -76,7 +76,7 @@
"13": 324,
"14": 30,
"9": 40,
"12": 408,
"12": 418,
"15": 20,
"4": 20,
"5": 32,
@@ -924,18 +924,19 @@
"providerUserId": "p_1782376007927_lpt9m4xge9",
"notes": "auto: player-coins",
"blocked": false,
"coins": 82,
"coins": 100,
"createdAt": "2026-06-25T08:26:49+00:00",
"updatedAt": "2026-06-26T04:11:43+00:00",
"score": 82,
"updatedAt": "2026-06-28T14:53:20+00:00",
"score": 100,
"scoreByCase": {
"12": 52,
"12": 60,
"8": 20,
"3": 10
"3": 10,
"2": 10
},
"lbName": "MONE",
"achievements": {
"d1_minigame_solver": 9
"d1_minigame_solver": 10
}
},
{
+2 -2
View File
@@ -95,9 +95,9 @@
"balloonBossPlayerBalloonFallbackUrl": "/Game/img/MegaVirus/Artboard%209.png",
"balloonBossBalloonsPerPlayer": 3,
"forcedMinigameKeys": [
"gauntlet",
"jump_survive",
"space_shooter",
"stack"
"balloon_boss"
],
"testSpecialCardByMap": {},
"troublesomeForceOffer": false,
+223 -34
View File
@@ -694,6 +694,9 @@
/* MG3 server-authoritative: stack-state srv:true client host-sim
(ลำดบตา/แกว/ดรอปบอท/จบเกม = server เปนเจาของ; เลนสงเจตนา stack-drop-req) */
let stackServerAuthPlay = false;
/* server-auth swing: extrapolate phase จาก sample ล่าสุดของ server (กัน drift → บล็อกวาปตอนปล่อย) */
let stackSrvPhaseAnchor = 0;
let stackSrvPhaseAnchorAt = 0;
/** MG3 = + id / (shared tower)
(เด [human=วเอง seat1 + bots] อเครอง แตละเครองมองตวเองเปนคนเดยว เลยแยกตกก) */
@@ -3596,6 +3599,14 @@
let meGauntletJumpTicks = 0;
/** ค่าที่ใช้วาดการยกตัว (เลอร์ปจาก meGauntletJumpTicks ให้โค้งกระโดดไม่กระตุก) */
let meGauntletJumpVis = 0;
/** local "" (-)
(server เซ jumpTicks=0 นทตอนขามสำเร = ดอาร + ญญาณลอยหายพรอมตอนด +1 เป "วาปข้าม")
ระหวางอารคน vis มเอง + glide แนวนอนช เหนเปนกระโดดขามจร ไมใชวาป */
let meGauntletJumpAnimStart = 0;
let meGauntletJumpAnimDur = 0;
function isMeGauntletJumpAnimActivePlay() {
return meGauntletJumpAnimStart > 0 && (Date.now() - meGauntletJumpAnimStart) < meGauntletJumpAnimDur;
}
/** ซิงก์จากเซิร์ฟเวอร์ (gauntlet-sync / GET /api/game-timing) */
let gauntletRuntimeTickMs = 220;
let gauntletRuntimeJumpTicks = 6;
@@ -3990,6 +4001,11 @@
/** quiz_carry: epoch ให้หยิบตัวเลือกได้ · epoch ปิดรอบตอบ (ตั้งที่ Admin แท็บหยิบมาวาง) */
let quizCarryOptionRevealAt = 0;
let quizCarryAnswerCloseAt = 0;
/** server-auth (เกมจริง): epoch สิ้นสุดนับ 3-2-1 บนโต๊ะ ช่วง read phase (>0 = กำลังโชว์ countdown) */
let quizCarrySrvReadCountdownEndAt = 0;
/** MG4 server-auth: ไอคอน ✓/✗ ลอยเหนือหัวตอนตอบ (key = entity id → { ok, until }) ทุกจอเห็นพร้อมกัน */
const quizCarryEntityFeedbackFx = Object.create(null);
const QUIZ_CARRY_FEEDBACK_FX_MS = 1300;
/** หมดเวลาตอบรอบนี้แล้ว — โชว์ TIME'S UP ~3s แล้วเรียก quizCarryAfterRoundResolved */
let quizCarryAnswerTimeupAwaitNext = false;
let quizCarryAnswerRoundTimeupAutoT = null;
@@ -3999,6 +4015,8 @@
let quizCarrySessionLength = 0;
let quizCarryRoundsCompleted = 0;
let quizCarrySessionEnded = false;
/** MG4 server-auth (detective): กัน schedule กลับ LobbyB ซ้ำเมื่อ quiz-carry-ended มาหลายครั้ง */
let quizCarryDetectiveReturnNavDone = false;
/** หลัง timeup บนโต๊ะ (embed): รอ 5s แล้วโชว์ result-complete / result-gameover */
let quizCarryResultEndTimer = null;
/** หลังโชว์ result-end (embed preview): รอ 5s แล้วกลับ room-lobby — ใช้ร่วม quiz_carry + crown */
@@ -9833,7 +9851,7 @@
if (o.balloonBossBossDmg != null) row.bbDmg = o.balloonBossBossDmg;
if (o.balloonBossBalloons != null) row.bbBalloons = o.balloonBossBalloons;
if (o.balloonBossEliminated != null) row.bbElim = !!o.balloonBossEliminated;
if (o.quizCarryHeld !== undefined) row.qcHeld = o.quizCarryHeld;
if (o.quizCarryHeld !== undefined && !(isQuizCarry() && quizCarryServerAuthActive())) row.qcHeld = o.quizCarryHeld;
/* MG1 quiz: (=) + host
(เดมไมงเลย non-host เหนบอทลอก/respawn/คะแนนคนละแบบ) */
if (isQuiz()) {
@@ -10897,6 +10915,27 @@
}
}
/** MG4 server-auth (detective): จบเซสชัน — flow เหมือน MG1 (result flash 5s → GCM สรุป → Done → LobbyB + การ์ด) */
function beginQuizCarryServerAuthMissionEndFlow() {
if (quizCarrySessionEnded) return;
quizCarrySessionEnded = true;
quizCarryPregameActive = false;
hideQuizCarryPregameOverlay();
quizCarryCurrent = null;
quizCarryEmbedPendingQuestion = null;
hideQuizCarryEmbedCountdownOverlay();
me.quizCarryHeld = null;
others.forEach((o) => {
if (!o) return;
o.quizCarryHeld = null;
o.botPath = [];
});
hidePlayQuizHud();
const mission = quizCarryBuildGcmMissionPayload({});
const anyScore = quizCarryAnyPlayerHasCorrectAnswer();
beginSimpleResultFlashThenGcmPlay(mission, anyScore ? 'result-complete.png' : 'result-gameover.png');
}
function showQuizCarrySessionCompleteOverlay() {
if (quizCarrySessionEnded) return;
quizCarrySessionEnded = true;
@@ -11026,7 +11065,7 @@
showQuizCarryToast(ok ? ('ถูกต้อง +' + QUIZ_CARRY_POINTS_PER_CORRECT + ' แต้ม') : 'ผิด — คืนป้ายที่โซนตัวเลือกแล้ว กด F หยิบใหม่ได้', ok);
}
} catch (e) { /* ignore */ }
socket.emit('quiz-carry-grab');
socket.emit('quiz-carry-grab', { x: me.x, y: me.y });
}
return false;
}
@@ -11198,8 +11237,15 @@
if (isGauntletCrownHeistMapPlay() && me.gauntletEliminated) return false;
const now = Date.now();
if (now - lastGauntletJumpKey < 200) return false;
/* (server arc local )
(server นดวย if jumpTicks>0 return แต gate client นการเร arc ใหม = ไมเหนกระโดดตอเนอง) */
if ((meGauntletJumpTicks || 0) > 0 || isMeGauntletJumpAnimActivePlay()) return false;
lastGauntletJumpKey = now;
socket.emit('gauntlet-jump');
/* local (instant feedback) server (jumpTicks×tickMs)
เพอใเหนโคงข-ลงเต แม server จะต jumpTicks=0 กลางคนตอนขามสำเร */
meGauntletJumpAnimDur = Math.max(360, (gauntletRuntimeJumpTicks || 6) * (gauntletRuntimeTickMs || 220));
meGauntletJumpAnimStart = now;
return true;
}
@@ -12047,6 +12093,8 @@
stackTowerPost50AnimStartMs = null;
stackTowerHeartMinusFx = null;
stackServerAuthPlay = false; /* re-arm: server จะเปิดใหม่ผ่าน stack-state(srv) เมื่อรอบใหม่เริ่ม */
stackSrvPhaseAnchor = 0;
stackSrvPhaseAnchorAt = 0;
if (!mapData || !isStack()) return;
const w = mapData.width || 20, h = mapData.height || 15;
const land = getStackAreaBoundsPlay(mapData.stackLandArea, w, h);
@@ -12145,7 +12193,11 @@
}
}
if (towerLive && stackMini.teamMissesLeft != null) {
stackMini.teamMissesLeft = Math.max(0, stackMini.teamMissesLeft - 1);
if (stackServerAuthPlay && hit.teamMissesLeft != null) {
stackMini.teamMissesLeft = Math.max(0, Math.floor(Number(hit.teamMissesLeft)));
} else {
stackMini.teamMissesLeft = Math.max(0, stackMini.teamMissesLeft - 1);
}
} else {
stackMini.lives = Math.max(0, stackMini.lives - 1);
if (stackMini.lives <= 0) {
@@ -12166,6 +12218,13 @@
triggerStackTowerHeartMinusFxPlay();
const remain = stackMini.teamMissesLeft != null ? stackMini.teamMissesLeft : Math.max(0, (stackMini.lives || 0));
mg3TermOnMiss(Number(remain) || 0);
if (stackServerAuthPlay && stackMini.teamMissesLeft != null && stackMini.teamMissesLeft <= 0 && !stackTowerMissionEndedOnce) {
setTimeout(() => {
if (!stackTowerMissionEndedOnce && stackTowerMissionPhase === 'live' && stackMini && stackMini.teamMissesLeft <= 0) {
finishStackTowerMissionLocalPlay('misses_exhausted');
}
}, 900);
}
}
} else {
const seatLay = layerVisualMeta && layerVisualMeta.seat != null ? layerVisualMeta.seat : getStackDropActorSeat();
@@ -12544,8 +12603,9 @@
stackMini.pendingDropHeavy = !!item.heavy;
stackMini.pendingDropTheme = item.theme || null; /* สีจาก host สำหรับบล็อกนี้ (remote drop) */
stackMini.widthTiles = stackBlockWidthTilesForPlay(stackMini.initialWidthTiles, !!item.heavy);
/* snap เฟสเชือกให้ตรงจุดปล่อยของ server → เชือก+บล็อกที่ร่วงตรงกัน (กันบล็อกไม่ตรงเชือก) */
if (Number.isFinite(item.phase)) stackMini.phase = item.phase;
/* server-auth: snap phase startStackFallAnimation landOffsetTiles server
(snap phase ทำใหบลอกเดงไปทศอนกอนรวง เพราะ phase client อาจคลาดจาก server) */
if (!stackServerAuthPlay && Number.isFinite(item.phase)) stackMini.phase = item.phase;
return startStackFallAnimation(item.hit, item.actor || { human: true });
}
@@ -12630,14 +12690,18 @@
}
} else if (stackMini && stackRemoteDropQueue.length && applyNextRemoteStackDropPlay()) {
/* ว่าง (ไม่มีบล็อกร่วง/settle) + มีดรอปในคิวจาก host/คนอื่น → apply ทีละอัน (ไม่แกว่งเฟรมนี้) */
} else if (stackMini) {
} else if (stackMini && (!isStackTowerMissionUiMapPlay() || stackTowerMissionPhase === 'live')) {
/* : ( host ) broadcast mirror;
editor preview / เลนเดยว: แกวงเองตลอดเหมอนเด */
let advancePhase = true;
let emitSwing = false;
if (stackServerAuthPlay) {
advancePhase = true; /* server-auth: เดินเฟส local ให้ลื่น → reconcile ตอนรับ stack-swing */
emitSwing = false; /* แต่ไม่ emit — server เป็นเจ้าของเฟส */
if (!stackFall && !stackMini.settling && stackSrvPhaseAnchorAt > 0) {
const elapsed = (Date.now() - stackSrvPhaseAnchorAt) / 1000;
stackMini.phase = stackSrvPhaseAnchor + elapsed * stackMini.phaseSpeed * Math.PI * 2;
}
advancePhase = false;
emitSwing = false;
} else if (detectiveCaseFillBots) {
advancePhase = isMyStackTurn() || (isMePlayHost() && isStackCurrentBotTurnPlay());
emitSwing = advancePhase;
@@ -13105,6 +13169,8 @@
/** เลอร์ปตำแหน่งตัวละครต่อเฟรม (ยิ่งสูงยิ่งตามเป้าเร็ว) */
const GAUNTLET_VIS_LERP = 0.42;
/** ระหว่างกระโดด (อยู่กลางอากาศ) glide แนวนอนช้าลง — ให้การเลื่อนไปข้างหน้า +1 ช่อง กระจายทั่วโค้งกระโดด ไม่วาปไปข้างหน้าทันที */
const GAUNTLET_AIR_X_LERP = 0.13;
function gauntletObsSmoothstep(t) {
t = Math.max(0, Math.min(1, t));
@@ -13274,11 +13340,12 @@
gauntletObsBlendT0 = now;
}
function lerpGauntletEntityPos(x, y, tx, ty) {
function lerpGauntletEntityPos(x, y, tx, ty, xLerp) {
let nx = x;
let ny = y;
const xr = Number.isFinite(xLerp) ? xLerp : GAUNTLET_VIS_LERP;
if (tx != null && Number.isFinite(tx)) {
nx += (tx - nx) * GAUNTLET_VIS_LERP;
nx += (tx - nx) * xr;
if (Math.abs(tx - nx) < 0.02) nx = tx;
}
if (ty != null && Number.isFinite(ty)) {
@@ -20495,6 +20562,38 @@
if (!Number.isFinite(n) || n <= 0) return 0;
return n - (serverNow() - Date.now());
}
/* server-auth: โชว์รูป TIME'S UP บนโต๊ะ (รูปเดียวกับ embed) — ซ่อนเองเมื่อ phase ถัดไปมา (applyQuizCarryPhaseFromServer) */
function showQuizCarrySrvTimeupOnDesk() {
if (!isQuizCarry()) return;
const el = document.getElementById('quiz-carry-timeup-desk-layer');
if (!el) return;
const img = document.getElementById('quiz-carry-timeup-txt-img');
if (img) { img.onerror = null; img.src = BASE + '/img/quiz-carry/timeup-txt.png?v=' + String(Date.now()); }
el.classList.remove('qc-timeup-desk--interactive');
el.classList.remove('is-hidden');
el.setAttribute('aria-hidden', 'false');
try { syncQuizCarryTimeupDeskLayerPosition(); } catch (e) { /* ignore */ }
}
/* server-auth: 3-2-1 read phase () overlay embed
บทกเฟรมจาก render loop; อนเองเมอถ optionRevealAt (เขาเฟส answer) */
function tickQuizCarrySrvReadCountdown() {
if (!quizCarryServerAuthActive()) { if (quizCarrySrvReadCountdownEndAt > 0) { quizCarrySrvReadCountdownEndAt = 0; try { hideQuizCarryEmbedCountdownOverlay(); } catch (e) { /* ignore */ } } return; }
const ov = document.getElementById('quiz-carry-embed-countdown');
const numEl = document.getElementById('quiz-carry-embed-countdown-num');
if (!ov || !numEl) return;
const now = Date.now();
if (quizCarrySrvReadCountdownEndAt > now) {
const n = Math.max(1, Math.min(3, Math.ceil((quizCarrySrvReadCountdownEndAt - now) / 1000)));
setCountdown321QuestionAssetGraphic(numEl, n);
ov.classList.remove('is-hidden');
ov.setAttribute('aria-hidden', 'false');
try { syncQuizCarryEmbedCountdownLayout(); } catch (e) { /* ignore */ }
try { showQuizCarryEmbedCountdownDigitsOnlyChrome(); } catch (e) { /* ignore */ }
} else if (quizCarrySrvReadCountdownEndAt > 0) {
quizCarrySrvReadCountdownEndAt = 0;
try { hideQuizCarryEmbedCountdownOverlay(); } catch (e) { /* ignore */ }
}
}
function applyQuizCarryPhaseFromServer(p) {
if (!p || !isQuizCarry()) return;
quizCarryServerAuthPlay = true;
@@ -20523,10 +20622,12 @@
if (p.phase === 'answer') {
quizCarryOptionRevealAt = quizCarryServerEpochToLocal(p.optionRevealAt) || Date.now();
quizCarryAnswerCloseAt = quizCarryServerEpochToLocal(p.endsAt);
quizCarrySrvReadCountdownEndAt = 0; /* เข้าเฟสตอบ → จบ countdown โต๊ะ */
} else {
/* read: optionRevealAt อยู่อนาคต → ยังหยิบไม่ได้ จนกว่าเฟส answer */
/* read: optionRevealAt อยู่อนาคต → ยังหยิบไม่ได้ จนกว่าเฟส answer; โชว์ 3-2-1 บนโต๊ะจนถึง optionRevealAt */
quizCarryOptionRevealAt = quizCarryServerEpochToLocal(p.optionRevealAt);
quizCarryAnswerCloseAt = 0;
quizCarrySrvReadCountdownEndAt = (quizCarryOptionRevealAt && quizCarryOptionRevealAt > Date.now()) ? quizCarryOptionRevealAt : 0;
}
me.quizCarryHeld = null;
others.forEach((o) => { if (o) { o.quizCarryHeld = null; o.botPath = []; } });
@@ -20539,6 +20640,17 @@
if (!p || !p.srv || !isQuizCarry()) return;
applyQuizCarryPhaseFromServer(p);
});
/* ผลถูก/ผิดของ "คนที่ส่งคำตอบ" → โชว์ไอคอน + (ถ้าถูก) score+10 บนแผงคำถาม ให้เจ้าตัวเห็น (server-auth) */
socket.on('quiz-carry-answer-feedback', (d) => {
if (!d || d.id == null || !isQuizCarry()) return;
const ok = !!d.correct;
/* ทุกคน (คนจริง+บอท) → ลอยไอคอน ✓/✗ เหนือหัว ให้ทุกจอเห็นว่าใครตอบถูก/ผิด */
quizCarryEntityFeedbackFx[String(d.id)] = { ok, until: Date.now() + QUIZ_CARRY_FEEDBACK_FX_MS };
/* ตัวเองเพิ่มแผง feedback กลางจอ (ตอบถูก/ผิดของเรา) เหมือนเดิม */
if (String(d.id) === String(myId)) {
try { showQuizMapQuestionCarryFeedback(ok); } catch (e) { /* ignore */ }
}
});
/* บอท carry: server เป็นเจ้าของการเดิน/หยิบ (BFS) → รับตำแหน่ง+held แล้ว lerp ใน tick (o.tx/o.ty) */
socket.on('quiz-carry-bot-move', (d) => {
if (!d || !Array.isArray(d.bots) || !isQuizCarry()) return;
@@ -20577,6 +20689,10 @@
if (d.winnerId != null && String(d.winnerId) === String(myId)) {
try { showQuizMapQuestionCarryFeedback(true); } catch (e) { /* ignore */ }
}
/* หมดเวลา (ไม่มีใครตอบถูก) → โชว์รูป TIME'S UP บนโต๊ะ ~จนข้อถัดไปมา (applyQuizCarryPhaseFrom... ซ่อนให้) */
if (d.winnerId == null) {
try { showQuizCarrySrvTimeupOnDesk(); } catch (e) { /* ignore */ }
}
try { renderPlayQuizScoreboard(playLiveQuizScores); } catch (e) { /* ignore */ }
try { syncQuizCarryGrabButton(); } catch (e) { /* ignore */ }
});
@@ -20584,10 +20700,19 @@
socket.on('quiz-carry-ended', (d) => {
if (!isQuizCarry()) return;
quizCarryServerAuthPlay = true;
quizCarrySessionEnded = true;
if (d && d.scores && typeof d.scores === 'object') {
playLiveQuizScores = {};
for (const id in d.scores) playLiveQuizScores[id] = Math.max(0, Number(d.scores[id]) || 0);
}
/* detective: flow MG1 result-complete/gameover GCM Done LobbyB +
(าม finishDetectiveMinigameAndReturnLobby นท จะขาม flash + สรปคะแนน) */
if (isDetectiveMinigamePlay() && !quizCarryDetectiveReturnNavDone) {
quizCarryDetectiveReturnNavDone = true;
try { beginQuizCarryServerAuthMissionEndFlow(); } catch (e) { /* ignore */ }
return;
}
quizCarrySessionEnded = true;
if (d && d.scores && typeof d.scores === 'object') {
try { renderPlayQuizScoreboard(playLiveQuizScores); } catch (e) { /* ignore */ }
}
});
@@ -20753,7 +20878,7 @@
if (rb.bbDmg != null) o.balloonBossBossDmg = rb.bbDmg;
if (rb.bbBalloons != null) o.balloonBossBalloons = rb.bbBalloons;
if (rb.bbElim != null) o.balloonBossEliminated = !!rb.bbElim;
if (rb.qcHeld !== undefined) o.quizCarryHeld = rb.qcHeld;
if (rb.qcHeld !== undefined && !(isQuizCarry() && quizCarryServerAuthActive())) o.quizCarryHeld = rb.qcHeld;
/* MG1 quiz: รับสถานะล็อก + คะแนนบอทจาก host (single source of truth) */
if (rb.qct != null) o.quizCannotTrue = !!rb.qct;
if (rb.qcf != null) o.quizCannotFalse = !!rb.qcf;
@@ -20780,7 +20905,11 @@
overlap: Number(d.overlap),
supportRatio: (d.supportRatio == null ? null : Number(d.supportRatio)),
delta: Number(d.delta) || 0,
teamMissesLeft: d.teamMissesLeft != null ? Math.max(0, Math.floor(Number(d.teamMissesLeft))) : null,
};
if (stackServerAuthPlay && d.teamMissesLeft != null && stackMini.teamMissesLeft != null) {
stackMini.teamMissesLeft = Math.max(0, Math.floor(Number(d.teamMissesLeft)));
}
stackRemoteDropQueue.push({
hit: hit, seat: Number(d.seat) || 1, heavy: !!d.heavy,
theme: parsePlayLobbyThemeIndex(d.theme) || null, /* สีจาก host → freeze บนบล็อกที่วาง (ตรงทุกจอ) */
@@ -20810,12 +20939,9 @@
const ph = Number(d.phase);
if (Number.isFinite(ph)) {
if (stackServerAuthPlay && !stackFall && !stackMini.settling) {
/* server-auth: local server ( 60fps) "" sample
เพราะ sample จาก server เปนคาของอด (network latency) local เดนนำหนาเสมอ การ chase กล = บลอกสะด/ถอยหลงท ~60ms (อาการกระต)
snap เฉพาะตอน drift ใหญจร (เปลยนตา/ดรอป/หล sync) นอกนนเด local วน */
let diff = ph - stackMini.phase;
diff = ((diff + Math.PI) % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2) - Math.PI;
if (Math.abs(diff) > Math.PI * 0.6) stackMini.phase = ph;
stackSrvPhaseAnchor = ph;
stackSrvPhaseAnchorAt = Date.now();
stackMini.phase = ph;
} else {
stackMini.phase = ph;
}
@@ -21054,10 +21180,10 @@
(MG6 งอกาบาต/MG7 กโป ไม sync tick เหมอน gauntlet เคยตกไป hash) */
const moveTint = playTintFromJoinPeer(data);
if (moveTint) o.playTint = moveTint;
if (mapData && isQuizCarry() && data.quizCarryHeld !== undefined) {
if (mapData && isQuizCarry() && data.quizCarryHeld !== undefined && !quizCarryServerAuthActive()) {
o.quizCarryHeld = (data.quizCarryHeld == null ? null : data.quizCarryHeld);
}
if (mapData && isQuizCarry() && data.quizCarryScore !== undefined && (myId == null || data.id !== myId)) {
if (mapData && isQuizCarry() && data.quizCarryScore !== undefined && !quizCarryServerAuthActive() && (myId == null || data.id !== myId)) {
/* คะแนน carry ของคนจริงคนอื่น → ใช้ค่าจากเจ้าตัว (authoritative) ให้ทุกจอตรงกัน (ของเราใช้ค่า local) */
const qcs = Math.max(0, Number(data.quizCarryScore) || 0);
playLiveQuizScores[data.id] = qcs;
@@ -21124,7 +21250,8 @@
[...others.keys()].forEach((id) => {
if (isPreviewBotId(id)) return;
const oo = others.get(id);
if (oo && (oo.isQuizTakeoverBot || oo.isGauntletTakeoverBot)) return; /* บอทที่แปลงจากคนหลุด — ไม่อยู่ใน peers แล้ว แต่ห้าม prune */
/* บอทที่แปลงจากคนหลุด (ทุก MG) — ไม่อยู่ใน peers แล้ว แต่ห้าม prune ไม่งั้น avatar บอท takeover หาย ~1.5s หลังคนออก */
if (oo && (oo.isQuizTakeoverBot || oo.isGauntletTakeoverBot || oo.isStackTakeoverBot || oo.isQuizCarryTakeoverBot || oo.isJumpSurviveTakeoverBot || oo.isSpaceShooterTakeoverBot || oo.isBalloonBossTakeoverBot)) return;
if (myId != null && String(id) === String(myId)) return;
if (!live.has(String(id))) { others.delete(id); removed++; }
});
@@ -21833,7 +21960,8 @@
if (p.direction) me.direction = p.direction;
const jt = Number(p.gauntletJumpTicks);
meGauntletJumpTicks = Number.isFinite(jt) ? jt : 0;
meGauntletJumpVis = meGauntletJumpTicks;
/* อย่าทับ vis ระหว่างอาร์ค local — ไม่งั้น server ที่เซ็ต jumpTicks=0 ตอนข้ามจะตัดโค้งกระโดด (วาป) */
if (!isMeGauntletJumpAnimActivePlay()) meGauntletJumpVis = meGauntletJumpTicks;
const sc = Number(p.gauntletScore);
const newSc = Number.isFinite(sc) ? Math.max(0, sc) : 0;
me.gauntletScore = newSc;
@@ -22118,7 +22246,8 @@
me.direction = p.direction || me.direction;
const jt = Number(p.gauntletJumpTicks);
meGauntletJumpTicks = Number.isFinite(jt) ? jt : 0;
meGauntletJumpVis = meGauntletJumpTicks;
/* อย่าทับ vis ระหว่างอาร์ค local — กันการตัดโค้งกระโดดตอน server ดัน +1 พร้อมเซ็ต jumpTicks=0 */
if (!isMeGauntletJumpAnimActivePlay()) meGauntletJumpVis = meGauntletJumpTicks;
const sc = Number(p.gauntletScore);
me.gauntletScore = Number.isFinite(sc) ? Math.max(0, sc) : 0;
me.gauntletEliminated = !!p.gauntletEliminated;
@@ -25491,6 +25620,41 @@
ctx.restore();
}
/** MG4: ไอคอน ✓ (เขียว) / ✗ (แดง) ลอยเหนือหัวตอนตอบ — float ขึ้น + จางหายช่วงท้าย */
function drawQuizCarryAnswerBadge(cx, topY, ok, ageMs) {
const remain = QUIZ_CARRY_FEEDBACK_FX_MS - ageMs;
const rise = Math.min(1, ageMs / QUIZ_CARRY_FEEDBACK_FX_MS) * 14;
const rr = Math.max(11, tileSize * zDraw * 0.42);
const cy = topY - rr - 8 - rise;
const alpha = remain < 300 ? Math.max(0, remain / 300) : 1;
ctx.save();
ctx.globalAlpha = alpha;
ctx.beginPath();
ctx.arc(cx, cy, rr, 0, Math.PI * 2);
ctx.fillStyle = ok ? 'rgba(34,197,94,0.96)' : 'rgba(239,68,68,0.96)';
ctx.fill();
ctx.lineWidth = Math.max(2, rr * 0.14);
ctx.strokeStyle = 'rgba(255,255,255,0.95)';
ctx.stroke();
ctx.lineWidth = Math.max(2.2, rr * 0.18);
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = '#ffffff';
ctx.beginPath();
if (ok) {
ctx.moveTo(cx - rr * 0.42, cy + rr * 0.04);
ctx.lineTo(cx - rr * 0.10, cy + rr * 0.36);
ctx.lineTo(cx + rr * 0.46, cy - rr * 0.34);
} else {
ctx.moveTo(cx - rr * 0.32, cy - rr * 0.32);
ctx.lineTo(cx + rr * 0.32, cy + rr * 0.32);
ctx.moveTo(cx + rr * 0.32, cy - rr * 0.32);
ctx.lineTo(cx - rr * 0.32, cy + rr * 0.32);
}
ctx.stroke();
ctx.restore();
}
/** ป้ายติดตัว — กึ่งกลางลำตัว (ทับอก/เอว) แบบเดียวกับป้ายบนพื้น ไม่มีเส้นเชื่อม */
function drawQuizCarryHeldSignBoard(sxFeet, feetSy, halfSpriteW, spriteTopY, signText, _faceDir, choiceIndex, imageUrl, imageElementOpt) {
const imgUrlSan = sanitizeQuizCarryImageUrlClient(imageUrl);
@@ -25562,7 +25726,7 @@
ctx.restore();
}
function drawAvatar(ax, ay, isMe, name, characterId, direction, isWalking, playTint, gauntletAirTicks, gauntletScoreLabel, quizCarrySignPayload, crownPenaltyFxUntil) {
function drawAvatar(ax, ay, isMe, name, characterId, direction, isWalking, playTint, gauntletAirTicks, gauntletScoreLabel, quizCarrySignPayload, crownPenaltyFxUntil, entIdForFx) {
const { cw, ch } = getCharacterFootprintWH(mapData);
const air = Number(gauntletAirTicks) || 0;
let liftWorldY = 0;
@@ -25664,6 +25828,14 @@
quizCarrySignPayload.imageElement,
);
}
if (isQuizCarry() && entIdForFx != null) {
const fx = quizCarryEntityFeedbackFx[String(entIdForFx)];
if (fx) {
const age = Date.now() - (fx.until - QUIZ_CARRY_FEEDBACK_FX_MS);
if (age >= 0 && age < QUIZ_CARRY_FEEDBACK_FX_MS) drawQuizCarryAnswerBadge(sx, spriteTopY, fx.ok, age);
else delete quizCarryEntityFeedbackFx[String(entIdForFx)];
}
}
}
function quizCarrySignForEntity(ent) {
if (!isQuizCarry() || !quizCarryCurrent || !ent || ent.quizCarryHeld == null) return null;
@@ -25782,7 +25954,8 @@
drawAvatar(axO, ayO, false, peerName, o.characterId, faceDirOther, otherWalk, ot, (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks) || 0,
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletScore || 0) : undefined,
quizCarrySignForEntity(o),
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletCrownPenaltyFxUntil || 0) : 0);
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (o.gauntletCrownPenaltyFxUntil || 0) : 0,
id);
}
if (botOut) ctx.restore();
} else {
@@ -25800,7 +25973,8 @@
drawAvatar(axMe, ayMe, true, me.nickname + meTag, me.characterId, faceDirMe, meWalking, mt, meGauntletJumpVis,
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (me.gauntletScore || 0) : undefined,
quizCarrySignForEntity(me),
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (me.gauntletCrownPenaltyFxUntil || 0) : 0);
(isGauntletCrownHeistMapPlay() && gauntletCrownPregamePhase === 'live') ? (me.gauntletCrownPenaltyFxUntil || 0) : 0,
myId);
}
}
});
@@ -26089,9 +26263,12 @@
function buildPlayMovePayload() {
const mv = { x: me.x, y: me.y, direction: me.direction };
if (isQuizCarry()) {
mv.quizCarryHeld = (me.quizCarryHeld == null ? null : me.quizCarryHeld);
/* แนบคะแนนของเรา → คนอื่นเห็นคะแนนคนจริงตรงกัน (เดิมคนอื่นเห็นคะแนนเรา = 0/ค้าง) */
mv.quizCarryScore = Math.max(0, Number(playLiveQuizScores[myId]) || 0);
/* server-auth: held/ quiz-carry-held / quiz-carry-result move
(เดมแนบ quizCarryHeld local user-move state server broadcast แล = คนอนเหนหยบแลวเดงกล) */
if (!quizCarryServerAuthActive()) {
mv.quizCarryHeld = (me.quizCarryHeld == null ? null : me.quizCarryHeld);
mv.quizCarryScore = Math.max(0, Number(playLiveQuizScores[myId]) || 0);
}
}
return mv;
}
@@ -26207,13 +26384,24 @@
};
/* เฟสเดินเข้าเส้นชัย/freeze → บังคับไม่มีท่ากระโดด (ทุกคนเดินเข้าเส้น ไม่ค้างลอย) */
const gauntletFinishWalkPose = gauntletCrownRunwayWalkActive || gauntletCrownRunwayBgFinishLatched;
if (gauntletFinishWalkPose) { meGauntletJumpTicks = 0; meGauntletJumpVis = 0; }
else meGauntletJumpVis = stepGauntletJumpVis(meGauntletJumpVis, meGauntletJumpTicks);
const mp = lerpGauntletEntityPos(me.x, me.y, me.tx, me.ty);
if (gauntletFinishWalkPose) { meGauntletJumpTicks = 0; meGauntletJumpVis = 0; meGauntletJumpAnimStart = 0; }
else if (isMeGauntletJumpAnimActivePlay()) {
/* อาร์ค local: prog 0→1 ; vis = max·(1prog) → lift = sin(π·prog) ขึ้น-ลงเต็มโค้งเสมอ (ไม่ถูกตัดตอนข้ามสำเร็จ) */
const prog = Math.max(0, Math.min(1, (Date.now() - meGauntletJumpAnimStart) / meGauntletJumpAnimDur));
meGauntletJumpVis = (gauntletRuntimeJumpTicks || 6) * (1 - prog);
} else {
if (meGauntletJumpAnimStart > 0) meGauntletJumpAnimStart = 0;
meGauntletJumpVis = stepGauntletJumpVis(meGauntletJumpVis, meGauntletJumpTicks);
}
/* : server +1 lerp x (0.42) ~80ms
แลวลอยคางตออกตลอด arc = "วาปข้าม" ไมเหมอนกระโดด ตอนลอยอย glide x าลง ใหอยๆ ไปขางหนาพรอมโคงกระโดด */
const meAir = (meGauntletJumpVis || 0) > 0.08;
const mp = lerpGauntletEntityPos(me.x, me.y, me.tx, me.ty, meAir ? GAUNTLET_AIR_X_LERP : GAUNTLET_VIS_LERP);
me.x = mp.nx;
me.y = mp.ny;
others.forEach((o) => {
const op = lerpGauntletEntityPos(o.x, o.y, o.tx, o.ty);
const oAir = (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks || 0) > 0.08;
const op = lerpGauntletEntityPos(o.x, o.y, o.tx, o.ty, oAir ? GAUNTLET_AIR_X_LERP : GAUNTLET_VIS_LERP);
o.x = op.nx;
o.y = op.ny;
if (gauntletFinishWalkPose) { o.gauntletJumpTicks = 0; o.gauntletJumpVis = 0; return; }
@@ -26271,6 +26459,7 @@
}
}
tickQuizCarryEmbedCountdown();
tickQuizCarrySrvReadCountdown();
tickQuizCarryRoundTimers();
updateQuizCarryCarryPhaseHud();
stepPreviewBots();
+1 -1
View File
@@ -5429,7 +5429,7 @@
<script src="/app-base.js?v=2"></script>
<script src="/Game/socket.io/socket.io.js"></script>
<script src="js/version.js?v=0.0306"></script>
<script src="js/play.js?v=0.006280301"></script>
<script src="js/play.js?v=0.006280707"></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)
+255 -17
View File
@@ -3928,6 +3928,7 @@ function returnDetectiveSpaceToLobbyB(sid, space, message, awardOptions) {
/* MG4 quiz_carry (server-auth): ล้าง session (คะแนนถูกอ่านไปแจกเหรียญใน awardMinigameRankCoins ด้านบนแล้ว) */
if (space.quizCarrySession) space.quizCarrySession.active = false;
space.quizCarrySession = null;
if (space.quizCarryWatchdog) { clearInterval(space.quizCarryWatchdog); space.quizCarryWatchdog = null; }
/* MG5 jump_survive (server-auth): ล้าง session */
if (space.jumpSurviveSession) space.jumpSurviveSession.active = false;
space.jumpSurviveSession = null;
@@ -5026,8 +5027,8 @@ function quizCarryPathfindServer(md, sx, sy, gx, gy) {
return path;
}
/** หยิบ/ส่ง (server เป็นเจ้าของ) — คืน true ถ้ามีการเปลี่ยนสถานะ */
function quizCarryServerTryInteract(sid, space, actorId, isBot) {
/** หยิบ/ส่ง (server เป็นเจ้าของ) — คืน true ถ้ามีการเปลี่ยนสถานะ · clientPos = ตำแหน่งที่ client รายงานตอนกด grab (ใช้ถ้าใกล้ server ≤1.25 tile) */
function quizCarryServerTryInteract(sid, space, actorId, isBot, clientPos) {
const s = space.quizCarrySession;
if (!s || !s.active || s.ended) return false;
if (s.phase !== 'answer') return false;
@@ -5035,7 +5036,18 @@ function quizCarryServerTryInteract(sid, space, actorId, isBot) {
if (!md || !s.current) return false;
let px, py;
if (isBot) { const b = s.bots.get(actorId); if (!b) return false; px = b.x; py = b.y; }
else { const p = space.peers.get(actorId); if (!p) return false; px = Number(p.x); py = Number(p.y); }
else {
const p = space.peers.get(actorId);
if (!p) return false;
px = Number(p.x); py = Number(p.y);
/* client รายงานตำแหน่งตอนกด F — ถ้าใกล้ server พอ ใช้ client เพื่อลด desync หยิบไม่ติด/เด้ง */
if (clientPos && typeof clientPos === 'object') {
const cx = Number(clientPos.x), cy = Number(clientPos.y);
if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(px) && Number.isFinite(py)) {
if (Math.hypot(cx - px, cy - py) <= 1.25) { px = cx; py = cy; }
}
}
}
if (!Number.isFinite(px) || !Number.isFinite(py)) return false;
const holder = isBot ? s.bots.get(actorId) : s.players[actorId];
if (!holder) return false;
@@ -5049,6 +5061,8 @@ function quizCarryServerTryInteract(sid, space, actorId, isBot) {
holder.held = null;
const ok = held === s.current.correctIndex;
io.to(sid).emit('quiz-carry-held', { id: actorId, held: null });
/* แจ้งผลถูก/ผิด "บรอดแคสต์ให้ทุกคน" → โชว์ไอคอน ✓/✗ เหนือหัวทั้งคนจริงและบอท (ทุกจอเห็นพร้อมกัน) */
io.to(sid).emit('quiz-carry-answer-feedback', { id: actorId, correct: ok });
if (ok) {
holder.score = (Number(holder.score) || 0) + QUIZ_CARRY_POINTS_PER_CORRECT;
resolveQuizCarryRound(sid, space, actorId);
@@ -5087,6 +5101,7 @@ function resolveQuizCarryRound(sid, space, winnerId) {
s.bots.forEach((b) => { b.held = null; b.botPath = []; b.wantIdx = null; });
s.roundsCompleted++;
const willEnd = s.sessionLength > 0 && s.roundsCompleted >= s.sessionLength;
try { glog(sid, space, 'mg4-round', 'resolve ' + s.roundsCompleted + '/' + s.sessionLength + ' q=' + s.qIndex + ' winner=' + (winnerId || '-') + ' willEnd=' + willEnd); } catch (e) { /* ignore */ }
io.to(sid).emit('quiz-carry-result', {
questionIndex: s.qIndex + 1,
correctIndex: s.current ? s.current.correctIndex : null,
@@ -5118,6 +5133,8 @@ function scheduleQuizCarryReadPhase(sid, space) {
if (s.deck == null || !s.deck.length) s.deck = quizCarryShuffleServer(s.pool.slice());
s.current = s.deck.shift();
s.qIndex++;
s.lastAdvanceAt = Date.now();
try { glog(sid, space, 'mg4-read', 'q=' + s.qIndex + ' deck=' + (s.deck ? s.deck.length : 0) + ' rounds=' + s.roundsCompleted + '/' + s.sessionLength); } catch (e) { /* ignore */ }
s.roundResolved = false;
s.phase = 'read';
const readMs = Math.max(0, s.carryReadMs | 0);
@@ -5144,6 +5161,7 @@ function scheduleQuizCarryAnswerPhase(sid, space) {
const s = space.quizCarrySession;
if (!s || !s.active || s.ended) return;
s.phase = 'answer';
s.lastAdvanceAt = Date.now();
const ansMs = Math.max(1000, s.carryAnswerMs | 0);
s.optionRevealAt = Date.now();
s.phaseEndsAt = Date.now() + ansMs;
@@ -5174,7 +5192,9 @@ function startQuizCarryGame(sid, space) {
const players = {};
space.peers.forEach((_, peerId) => { players[peerId] = { held: null, score: 0 }; });
let sessionLength = clampCarrySessionLength(settings.carrySessionLength, 0);
if (!(sessionLength > 0)) sessionLength = 10; /* detective ต้องจบเมื่อครบ (เหมือน client default 10) */
/* 0 = "" ()
"พอจบทุกคำถาม เกมต้องจบ"; pool างคอย fallback 10 (นหารดวยศนย/าง) */
if (!(sessionLength > 0)) sessionLength = Math.max(1, pool.length || 10);
space.quizCarrySession = {
active: true, ended: false,
pool, deck: null, current: null, qIndex: 0,
@@ -5184,13 +5204,52 @@ function startQuizCarryGame(sid, space) {
sessionLength, roundsCompleted: 0, roundResolved: false,
players, bots: createQuizCarryBots(space, md), mapMd: md,
};
/* คนหลุดช่วง how-to → seed เป็นบอท takeover (BFS เล่นต่อ) — แทนคนที่ออก ใช้ socket.id เดิม ไม่ชน id บอท AI */
{
const sC = space.quizCarrySession;
drainPendingMissionTakeovers(space, 'quiz_carry', (rec) => {
sC.bots.set(rec.id, {
tier: 'avg',
x: Number.isFinite(rec.x) ? rec.x : 1, y: Number.isFinite(rec.y) ? rec.y : 1,
dir: rec.direction || 'down', isWalking: false,
held: null, score: 0,
botPath: [], pathfindAfter: 0, wantIdx: null,
wanderTX: null, wanderTY: null, retargetAt: 0,
takeover: true, takeoverPlayerKey: rec.playerKey || '',
});
});
}
if (!pool.length) { endQuizCarryGame(sid, space); return; }
/* watchdog : space.quizTimers ( resolveQuizCarryRound array )
าไมความคบหน (lastAdvanceAt) นานเก force resolve/จบ องก "เล่นจนครบแต่เกมไม่ยอมจบ" */
{
const sC = space.quizCarrySession;
sC.lastAdvanceAt = Date.now();
const perRound = Math.max(0, sC.carryReadMs) + Math.max(1000, sC.carryAnswerMs) + QUIZ_CARRY_BETWEEN_MS + 1500;
const stallMs = perRound + 20000; /* ช่วงเวลาที่ "ยอมรับได้" ต่อข้อ ก่อนถือว่าค้าง */
if (space.quizCarryWatchdog) clearInterval(space.quizCarryWatchdog);
space.quizCarryWatchdog = setInterval(() => {
const sp = spaces.get(sid);
const ss = sp && sp.quizCarrySession;
if (!sp || !ss || !ss.active || ss.ended) { clearInterval(space.quizCarryWatchdog); space.quizCarryWatchdog = null; return; }
const idle = Date.now() - (ss.lastAdvanceAt || 0);
if (idle > stallMs) {
try { glog(sid, sp, 'mg4-watchdog', 'STALL idle=' + idle + 'ms phase=' + ss.phase + ' round=' + ss.roundsCompleted + '/' + ss.sessionLength); } catch (e) { /* ignore */ }
ss.lastAdvanceAt = Date.now();
/* ดันให้ไปต่อ: ถ้ายังตอบ/อ่านค้าง → จบรอบนี้ (ไม่ให้คะแนน) แล้วระบบจะเลื่อนข้อหรือจบเองตาม willEnd */
if (!ss.roundResolved) { try { resolveQuizCarryRound(sid, sp, null); } catch (e) { /* ignore */ } }
else { try { scheduleQuizCarryReadPhase(sid, sp); } catch (e) { /* ignore */ } }
}
}, 5000);
}
scheduleQuizCarryReadPhase(sid, space);
}
function endQuizCarryGame(sid, space) {
const s = space.quizCarrySession;
if (s) { s.active = false; s.ended = true; }
if (space.quizCarryWatchdog) { clearInterval(space.quizCarryWatchdog); space.quizCarryWatchdog = null; }
try { glog(sid, space, 'mg4-end', 'END rounds=' + (s ? s.roundsCompleted : '?') + '/' + (s ? s.sessionLength : '?')); } catch (e) { /* ignore */ }
clearSpaceQuizTimers(space);
io.to(sid).emit('quiz-carry-ended', { scores: quizCarryScoresSnapshot(space) });
if (space.detectiveMinigameActive) {
@@ -7240,6 +7299,57 @@ function isCrownLobbyShellSpace(space) {
return isGauntletCrownHeistMapBySpace(space) || isBalloonBossMissionShellSpace(space) || isQuizQuestionMissionShellSpace(space) || isStackTowerMissionShellSpace(space) || isJumpSurviveMissionShellSpace(space) || isSpaceShooterMissionShellSpace(space);
}
/* ── How-to / pre-live disconnect takeover (MG3MG7)
เกมเหลานสราง session (bots/turnOrder) ตอน 3-2-1 จบเทาน าคนหลดชวง how-to /
บถอยหล งไม session ใหแปลงเปนบอท เด avatar เลยหายไปเลย (ตกไป user-left).
แก: เก "pending takeover" ไวบน space + แจ client เก avatar (*-player-to-bot) แล
seed เปนบอท server ตอน startXxxGame บอทรบชวงเลนตอทนทเมอเกมเร เหมอน MG1/MG2 */
function missionShellGameTypeForSpace(space) {
if (!space) return null;
const md = (space.mapId && maps.get(space.mapId)) || space.mapData;
const gt = md && md.gameType;
if (!gt) return null;
if (gt === 'space_shooter' && isSpaceShooterMissionShellSpace(space)) return 'space_shooter';
if (gt === 'jump_survive' && isJumpSurviveMissionShellSpace(space)) return 'jump_survive';
if (gt === 'balloon_boss' && isBalloonBossMissionShellSpace(space)) return 'balloon_boss';
if (gt === 'stack' && isStackTowerMissionShellSpace(space)) return 'stack';
if (gt === 'quiz_carry') return 'quiz_carry';
return null;
}
function recordPendingMissionTakeover(space, gameType, socketId, info, playerKey, nick, wasElim) {
if (!space || !gameType || !socketId || !info) return;
if (!Array.isArray(space.pendingMissionTakeovers)) space.pendingMissionTakeovers = [];
space.pendingMissionTakeovers = space.pendingMissionTakeovers.filter((r) => r.id !== socketId);
const themeIdx = parseInt(info.lobbyColorThemeIndex, 10);
const skinIdx = parseInt(info.lobbySkinToneIndex, 10);
space.pendingMissionTakeovers.push({
gameType, id: socketId, playerKey: playerKey || '', nick: (nick || 'ผู้เล่น'), wasEliminated: !!wasElim,
x: Number(info.x), y: Number(info.y), direction: info.direction || 'down',
characterId: info.characterId != null ? info.characterId : null,
themeIdx: Number.isFinite(themeIdx) ? themeIdx : null,
skinIdx: Number.isFinite(skinIdx) ? skinIdx : null,
spaceShooterScore: Math.max(0, Number(info.spaceShooterScore) || 0),
spaceShooterEliminated: !!info.spaceShooterEliminated,
balloonBossScore: Math.max(0, Number(info.balloonBossScore) || 0),
balloonBossBossDmg: Math.max(0, Number(info.balloonBossBossDmg) || 0),
balloonBossBalloons: Math.max(0, Number(info.balloonBossBalloons) || 0),
balloonBossEliminated: !!info.balloonBossEliminated,
jumpSurviveEliminated: !!info.jumpSurviveEliminated,
});
}
/** seed บอท takeover จาก pending (เฉพาะ gameType นี้) — makeBot(rec) สร้างบอทใน session แล้วคืน true ถ้าสำเร็จ */
function drainPendingMissionTakeovers(space, gameType, makeBot) {
if (!space || !Array.isArray(space.pendingMissionTakeovers) || typeof makeBot !== 'function') return 0;
const keep = [];
let n = 0;
for (const rec of space.pendingMissionTakeovers) {
if (rec.gameType !== gameType) { keep.push(rec); continue; }
try { if (makeBot(rec) !== false) n++; } catch (e) { /* ignore */ }
}
space.pendingMissionTakeovers = keep;
return n;
}
function newBalloonBossShellGauntletRunState() {
return {
obstacles: [],
@@ -7864,6 +7974,13 @@ function buildStackTurnOrderServer(space, s) {
order.push({ kind: 'human', seat, id, botId: null, theme: (p.lobbyThemeIndex != null ? (Number(p.lobbyThemeIndex) % 8) + 1 : null) });
seat++;
}
/* บอท takeover (คนหลุดช่วง how-to) ได้ seat ต่อจากคน ก่อนบอท AI — คง avatar/ตำแหน่งลำดับเดิมไว้ */
for (const [botId, b] of s.bots) {
if (!b || !b.takeover) continue;
if (seat > STACK_SEATS) break;
order.push({ kind: 'bot', seat, id: null, botId, theme: (b.theme != null ? b.theme : ((seat % 8) + 1)) });
seat++;
}
const botCount = Math.max(0, Math.min(STACK_SEATS - order.length, effectiveBotSlotCount(space)));
let bannedBotSlot = -1;
const bannedId = space.bannedThisRunPlayerId;
@@ -7978,9 +8095,14 @@ function runStackTick(sid, space) {
overlap: hit.overlap, supportRatio: hit.supportRatio, delta: hit.delta,
seat: entry.seat, heavy: false, theme: entry.theme || null,
actorId: entry.botId, isBot: true,
phase: s.phase, /* เฟสจริง ณ ปล่อย → client snap เชือกให้ตรงบล็อกที่ร่วง (กันบล็อกไม่ตรงเชือก) */
phase: s.phase,
teamMissesLeft: s.teamMissesLeft,
});
s.busyUntil = now + STACK_DROP_BUSY_MS;
if (s.teamMissesLeft != null && s.teamMissesLeft <= 0) {
stackSessionEnd(sid, space, 'misses_exhausted');
return;
}
stackAdvanceTurnServer(s);
emitStackStateServer(sid, space);
}
@@ -7999,6 +8121,16 @@ function startStackTicker(sid, space) {
if (!space.detectiveMinigameActive) return; /* เกมจริงเท่านั้น — editor preview (เล่นเดี่ยว) คง client-sim เดิม */
if (space.stackSession && space.stackSession.timerId) return;
space.stackSession = newStackSessionState(space, md);
/* คนหลุดช่วง how-to → seed เป็นบอท takeover ก่อนจัดลำดับตา (buildStackTurnOrderServer จะใส่เป็น seat ให้) */
{
const s3 = space.stackSession;
drainPendingMissionTakeovers(space, 'stack', (rec) => {
s3.bots.set(rec.id, {
tier: 'avg', score: 0, takeover: true, takeoverPlayerKey: rec.playerKey || '',
theme: Number.isFinite(rec.themeIdx) ? (rec.themeIdx % 8) + 1 : null,
});
});
}
buildStackTurnOrderServer(space, space.stackSession);
emitStackStateServer(sid, space);
space.stackSession.timerId = setInterval(() => { runStackTick(sid, space); }, STACK_TICK_MS);
@@ -8248,9 +8380,11 @@ function createJumpSurviveBots(space, md) {
? cells.map((c) => jsGridPosStandingOnPlatform(md, ts, c.x, c.y, sc))
: [jsGridPosStandingOnPlatform(md, ts, Number(md.spawn && md.spawn.x) || 1, Number(md.spawn && md.spawn.y) || 1, sc)];
const humanCount = [...space.peers.keys()].filter((id) => !isBannedPeerForRun(space, id)).length;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount, 6), effectiveBotSlotCount(space)));
/* บอท takeover (จาก how-to) ที่ seed ไว้แล้ว = นับเป็น "คน" กันรวมเกิน 6 + เลื่อนช่อง spawn บอท AI */
const existing = s.bots.size;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount + existing, 6), effectiveBotSlotCount(space)));
const bannedSlot = jsBannedBotSlot(space);
let poolIdx = 1 + humanCount;
let poolIdx = 1 + humanCount + existing;
let created = 0, slot = 0;
while (created < botCount && slot < 64) {
if (slot === bannedSlot) { slot++; continue; }
@@ -8284,8 +8418,25 @@ function startJumpSurviveGame(sid, space) {
players: {}, /* peerId -> { eliminated } */
lastEmit: 0,
};
/* คนหลุดช่วง how-to → seed บอท takeover ก่อนสร้างบอท AI */
seedJumpSurviveTakeoverBots(space, md);
createJumpSurviveBots(space, md);
}
function seedJumpSurviveTakeoverBots(space, md) {
const s = space.jumpSurviveSession;
if (!s) return;
const now = Date.now();
drainPendingMissionTakeovers(space, 'jump_survive', (rec) => {
s.bots.set(rec.id, {
tier: 'avg',
x: Number.isFinite(rec.x) ? rec.x : 1, y: Number.isFinite(rec.y) ? rec.y : 1,
vy: 0, onGround: false, dir: rec.direction || 'down', walking: false,
eliminated: !!rec.jumpSurviveEliminated,
wishX: 0, wishNext: now + 200, nextJumpAi: now + 200,
takeover: true, takeoverPlayerKey: rec.playerKey || '',
});
});
}
function endJumpSurviveGame(sid, space, reason) {
const s = space.jumpSurviveSession;
if (!s || s.ended) return;
@@ -8407,13 +8558,15 @@ function createSpaceShooterBots(space, md) {
const ts = jsTileSize(md);
const mw = (md.width || 20) * ts, mh = (md.height || 15) * ts;
const humanCount = [...space.peers.keys()].filter((id) => !isBannedPeerForRun(space, id)).length;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount, 6), effectiveBotSlotCount(space)));
/* บอท takeover (จาก how-to) ที่ seed ไว้แล้ว = นับเป็น "คน" ในการจำกัด 6 ช่อง กันรวมเกิน 6 */
const existing = s.bots.size;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount + existing, 6), effectiveBotSlotCount(space)));
const bannedSlot = jsBannedBotSlot(space);
let created = 0, slot = 0;
while (created < botCount && slot < 64) {
if (slot === bannedSlot) { slot++; continue; }
const id = JS_BOT_PREFIX + slot;
const slotNum = ((humanCount + created) % 6) + 1;
const slotNum = ((humanCount + existing + created) % 6) + 1;
const cen = ssShooterSpawnCenter(md, slotNum, ts);
let cx = cen ? cen.cx : ((created + 1) / (botCount + 1)) * mw;
let cy = cen ? cen.cy : (mh - ts * 1.5);
@@ -8441,8 +8594,27 @@ function startSpaceShooterGame(sid, space) {
endsAt: lim > 0 ? liveBeginsAt + lim * 1000 + 2000 : 0, /* +grace: client จบ time_up ก่อน; server backstop */
bots: new Map(), players: {}, lastEmit: 0,
};
/* คนหลุดช่วง how-to → seed เป็นบอท takeover ก่อนสร้างบอท AI (createSpaceShooterBots นับ s.bots.size เป็น human-like) */
seedSpaceShooterTakeoverBots(space, md);
createSpaceShooterBots(space, md);
}
function seedSpaceShooterTakeoverBots(space, md) {
const s = space.spaceShooterSession;
if (!s) return;
const ts = jsTileSize(md);
const mw = (md.width || 20) * ts, mh = (md.height || 15) * ts;
drainPendingMissionTakeovers(space, 'space_shooter', (rec) => {
const cx = Number.isFinite(rec.x) ? Math.max(ts * 0.5, Math.min(mw - ts * 0.5, (rec.x + 0.5) * ts)) : mw * 0.5;
const cy = ssClampCy(Number.isFinite(rec.y) ? (rec.y + 0.5) * ts : mh - ts * 1.5, mh);
s.bots.set(rec.id, {
tier: 'avg', cx, cy, dir: 'up',
score: rec.spaceShooterScore || 0, eliminated: !!rec.spaceShooterEliminated, ssHits: 0,
fireCd: 0.3 + Math.random() * 0.4,
wanderVx: Math.random() < 0.5 ? -1 : 1, wanderVy: Math.random() < 0.5 ? -1 : 1, wanderNext: Date.now() + 500,
takeover: true, takeoverPlayerKey: rec.playerKey || '',
});
});
}
function endSpaceShooterGame(sid, space, reason) {
const s = space.spaceShooterSession;
if (!s || s.ended) return;
@@ -8539,13 +8711,15 @@ function createBalloonBossBots(space, md) {
const mw = (md.width || 20) * ts, mh = (md.height || 15) * ts;
const startBalloons = balloonBossBalloonsForMap(md);
const humanCount = [...space.peers.keys()].filter((id) => !isBannedPeerForRun(space, id)).length;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount, 6), effectiveBotSlotCount(space)));
/* บอท takeover (จาก how-to) ที่ seed ไว้แล้ว = นับเป็น "คน" กันรวมเกิน 6 + เลื่อนช่อง spawn บอท AI */
const existing = s.bots.size;
const botCount = Math.max(0, Math.min(6 - Math.min(humanCount + existing, 6), effectiveBotSlotCount(space)));
const bannedSlot = jsBannedBotSlot(space);
let created = 0, slot = 0;
while (created < botCount && slot < 64) {
if (slot === bannedSlot) { slot++; continue; }
const id = JS_BOT_PREFIX + slot;
const slotNum = ((humanCount + created) % 6) + 1;
const slotNum = ((humanCount + existing + created) % 6) + 1;
const cen = bbPlayerSpawnCenter(md, slotNum, ts);
let cx = cen ? cen.cx : ((created + 1) / (botCount + 1)) * mw;
let cy = cen ? cen.cy : (mh - ts * 1.4);
@@ -8572,8 +8746,29 @@ function startBalloonBossGame(sid, space) {
endsAt: lim > 0 ? liveBeginsAt + lim * 1000 + 2000 : 0,
maxHp: bbMaxHp(md), bots: new Map(), lastEmit: 0,
};
/* คนหลุดช่วง how-to → seed บอท takeover ก่อนสร้างบอท AI */
seedBalloonBossTakeoverBots(space, md);
createBalloonBossBots(space, md);
}
function seedBalloonBossTakeoverBots(space, md) {
const s = space.balloonBossSession;
if (!s) return;
const ts = jsTileSize(md);
const mw = (md.width || 20) * ts, mh = (md.height || 15) * ts;
const startBalloons = balloonBossBalloonsForMap(md);
drainPendingMissionTakeovers(space, 'balloon_boss', (rec) => {
const cx = Number.isFinite(rec.x) ? (rec.x + 0.5) * ts : mw * 0.5;
const cy = Number.isFinite(rec.y) ? (rec.y + 0.5) * ts : mh - ts * 1.4;
const cl = bbClampWorld(cx, cy, mw, mh);
s.bots.set(rec.id, {
tier: 'avg', homeCx: cl.cx, homeCy: cl.cy, cx: cl.cx, cy: cl.cy,
score: rec.balloonBossScore || 0, dmg: rec.balloonBossBossDmg || 0,
balloons: rec.balloonBossBalloons || startBalloons, eliminated: !!rec.balloonBossEliminated,
fireCd: 0.6 + Math.random() * 0.8, phase: Math.random() * Math.PI * 2,
takeover: true, takeoverPlayerKey: rec.playerKey || '',
});
});
}
function endBalloonBossGame(sid, space, reason) {
const s = space.balloonBossSession;
if (!s || s.ended) return;
@@ -10624,9 +10819,14 @@ io.on('connection', (socket) => {
overlap: hit.overlap, supportRatio: hit.supportRatio, delta: hit.delta,
seat: entry.seat, heavy: false, theme,
actorId: entry.id, isBot: false,
phase: s.phase, /* เฟสจริง ณ ปล่อย → client snap เชือกให้ตรงบล็อกที่ร่วง */
phase: s.phase,
teamMissesLeft: s.teamMissesLeft,
});
s.busyUntil = Date.now() + STACK_DROP_BUSY_MS;
if (s.teamMissesLeft != null && s.teamMissesLeft <= 0) {
stackSessionEnd(sid, space, 'misses_exhausted');
return;
}
stackAdvanceTurnServer(s);
emitStackStateServer(sid, space);
});
@@ -11397,9 +11597,10 @@ io.on('connection', (socket) => {
try {
const md9b = (space.mapId && maps.get(space.mapId)) || space.mapData;
const grT = space.gauntletRun;
/* takeover run () MG1 takeover session active
ยกเวนชวง pre-run hold (งไมเรมว) ลบ avatar ตรงๆ ได */
if (grT && md9b && md9b.gameType === 'gauntlet' && !grT.crownRunHeld) {
/* takeover ตราบที่ run ยังมีอยู่ — รวมช่วง how-to / pre-run hold (crownRunHeld) วย เหมอน MG1
(เดมขาม crownRunHeld host หลดตอน how-to = avatar หายไปเลย ไมบอทรบช; ตอนนแปลงเปนบอททนท
บอทจะคางในเลนตอน how-to แลววงเองเมอเกมเร emitGauntletSync งบอท takeover ระหวาง hold อยแล) */
if (grT && md9b && md9b.gameType === 'gauntlet') {
if (!grT.bots) grT.bots = new Map();
const px = Number(leftPeerInfo.x), py = Number(leftPeerInfo.y);
const themeIdx = parseInt(leftPeerInfo.lobbyColorThemeIndex, 10);
@@ -11533,6 +11734,31 @@ io.on('connection', (socket) => {
mg7Takeover = true;
}
} catch (e) { /* ignore */ }
/* MG3MG7 หลุดช่วง how-to / บถอยหล (งไม session live) เก pending takeover ไว
แล seed เปนบอทตอนเกมเร + แจ client เก avatar (*-player-to-bot) เหมอน MG1/MG2
(เด: ไม session = ไม takeover = avatar หายเลยตอน how-to) */
let shellTakeoverEvent = null;
try {
if (!mg1Takeover && !mg2Takeover && !mg3Takeover && !mg4Takeover && !mg5Takeover && !mg6Takeover && !mg7Takeover && space.detectiveMinigameActive) {
const gtShell = missionShellGameTypeForSpace(space);
const liveActive = !!(
(gtShell === 'space_shooter' && space.spaceShooterSession && space.spaceShooterSession.active) ||
(gtShell === 'jump_survive' && space.jumpSurviveSession && space.jumpSurviveSession.active) ||
(gtShell === 'balloon_boss' && space.balloonBossSession && space.balloonBossSession.active) ||
(gtShell === 'quiz_carry' && space.quizCarrySession && space.quizCarrySession.active) ||
(gtShell === 'stack' && space.stackSession && space.stackSession.active)
);
if (gtShell && !liveActive) {
recordPendingMissionTakeover(space, gtShell, socket.id, leftPeerInfo, leftPlayerKey, leftNick, leftWasEliminated);
shellTakeoverEvent =
gtShell === 'space_shooter' ? 'space-shooter-player-to-bot' :
gtShell === 'jump_survive' ? 'jump-survive-player-to-bot' :
gtShell === 'balloon_boss' ? 'balloon-boss-player-to-bot' :
gtShell === 'quiz_carry' ? 'quiz-carry-player-to-bot' :
gtShell === 'stack' ? 'stack-player-to-bot' : null;
}
}
} catch (e) { /* ignore */ }
if (space.voiceInits) delete space.voiceInits[socket.id];
if (space.troublesomeEligible) space.troublesomeEligible.delete(socket.id);
if (space.quizSession && space.quizSession.players) delete space.quizSession.players[socket.id];
@@ -11559,12 +11785,14 @@ io.on('connection', (socket) => {
space.quizSession = null;
if (space.quizCarrySession) space.quizCarrySession.active = false;
space.quizCarrySession = null;
if (space.quizCarryWatchdog) { clearInterval(space.quizCarryWatchdog); space.quizCarryWatchdog = null; }
if (space.jumpSurviveSession) space.jumpSurviveSession.active = false;
space.jumpSurviveSession = null;
if (space.spaceShooterSession) space.spaceShooterSession.active = false;
space.spaceShooterSession = null;
if (space.balloonBossSession) space.balloonBossSession.active = false;
space.balloonBossSession = null;
space.pendingMissionTakeovers = []; /* ห้องว่าง → ล้าง pending takeover ค้าง (กัน seed บอทผีรอบหน้า) */
space.emptySince = Date.now();
/* ระหว่างมินิเกมสืบสวน / คดีล็อก — อย่าลบห้องเมื่อเปลี่ยนหน้า room-lobby → play.html (รอ join กลับ) */
const keepCaseSpace = !!(space.detectiveMinigameActive
@@ -11619,6 +11847,10 @@ io.on('connection', (socket) => {
/* MG7: ไม่ลบ avatar — แปลงเป็นบอท server (ตำแหน่ง/คะแนน/ดาเมจ/ลูกโป่งเดิม) ยิงบอสต่อ */
io.to(sid).emit('balloon-boss-player-to-bot', { id: socket.id, nickname: leftNick });
glog(sid, space, 'leave', 'หลุด MG7 → บอทแทนในเกม (สี/ตำแหน่งเดิม) · เหลือคน ' + space.peers.size, leftNick);
} else if (shellTakeoverEvent) {
/* how-to/นับถอยหลัง: client เก็บ avatar + ติดป้าย (บอท) — บอท server จะ seed ตอนเกมเริ่ม (pendingMissionTakeovers) */
io.to(sid).emit(shellTakeoverEvent, { id: socket.id, nickname: leftNick });
glog(sid, space, 'leave', 'หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน ' + space.peers.size, leftNick);
} else {
io.to(sid).emit('user-left', { id: socket.id });
glog(sid, space, 'leave', 'ออกจากห้อง · เหลือคน ' + space.peers.size, leftNick);
@@ -11856,13 +12088,19 @@ io.on('connection', (socket) => {
});
/* MG4 server-auth: client ส่ง intent "หยิบ/ส่ง" (กด F / ปุ่ม GRAB) → server ตัดสินจากตำแหน่งล่าสุด */
socket.on('quiz-carry-grab', () => {
socket.on('quiz-carry-grab', (data) => {
const sid = socket.data.spaceId;
const space = sid ? spaces.get(sid) : null;
if (!space) return;
const s = space.quizCarrySession;
if (!s || !s.active || s.ended) return;
quizCarryServerTryInteract(sid, space, socket.id, false);
const changed = quizCarryServerTryInteract(sid, space, socket.id, false, data);
/* (/) held "" optimistic prediction
อาจคางผ (เช ทำนายวาหยบไดแต server ปฏเสธเพราะคนอนถออย) */
if (!changed) {
const hp = s.players[socket.id];
io.to(socket.id).emit('quiz-carry-held', { id: socket.id, held: hp ? (hp.held == null ? null : hp.held) : null });
}
});
/* ตอบเวลา server ปัจจุบัน → client ใช้คำนวณ clock offset (ชดเชย latency) ให้เวลาในเกมตรงกันทุกเครื่อง */
+257
View File
@@ -1835,3 +1835,260 @@
{"t":1782623870157,"cat":"LobbyB","room":"aaa-1782623736802","who":"MONE","ev":"join-ok","detail":"peers=1 host=me map=mn8nx46h bots=5 [+3336ms]","src":"client"}
{"t":1782623884340,"cat":"MG6","room":"aaa","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-6 ยิงอุกาบาต Violent Crime (การ์ด 2)","src":"server"}
{"t":1782623903616,"cat":"MG6","room":"aaa","who":"MONE","ev":"join","detail":"สี#5 · คน 1 + บอท 5","src":"server"}
{"t":1782657861376,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"lobby-open","detail":"space=aaa-1782657858314 [+0ms]","src":"client"}
{"t":1782657861416,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"preload-start","detail":" [+22ms]","src":"client"}
{"t":1782657861504,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"manifest-done","detail":" [+123ms]","src":"client"}
{"t":1782657861507,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"colors-resolved","detail":" [+124ms]","src":"client"}
{"t":1782657861571,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"socket-connect","detail":" [+196ms]","src":"client"}
{"t":1782657861585,"cat":"LobbyA","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 4","src":"server"}
{"t":1782657863169,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"join-ok","detail":"peers=1 host=me map=mlsbbxfe bots=4 [+1793ms]","src":"client"}
{"t":1782657865497,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+4127ms]","src":"client"}
{"t":1782657871827,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"lobby-open","detail":"space=aaa-1782657858314 [+0ms]","src":"client"}
{"t":1782657871876,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"colors-resolved","detail":" [+31ms]","src":"client"}
{"t":1782657871877,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"manifest-done","detail":" [+30ms]","src":"client"}
{"t":1782657871877,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-start","detail":" [+30ms]","src":"client"}
{"t":1782657871884,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+31ms]","src":"client"}
{"t":1782657871915,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-occupants-done","detail":" [+31ms]","src":"client"}
{"t":1782657871924,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-ready(occupants)","detail":" [+31ms]","src":"client"}
{"t":1782657871975,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"socket-connect","detail":" [+150ms]","src":"client"}
{"t":1782657871979,"cat":"LobbyA","room":"aaa","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782657872893,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mlsbbxfe bots=4 [+1032ms]","src":"client"}
{"t":1782657872896,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+1032ms]","src":"client"}
{"t":1782657880050,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"preload-occupants-done","detail":" [+18679ms]","src":"client"}
{"t":1782657880050,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+18680ms]","src":"client"}
{"t":1782657880053,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"preload-ready(occupants)","detail":" [+18679ms]","src":"client"}
{"t":1782657920324,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+48490ms]","src":"client"}
{"t":1782657920329,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+58952ms]","src":"client"}
{"t":1782657924571,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+63201ms]","src":"client"}
{"t":1782657925602,"cat":"LobbyB","room":"aaa-1782657858314","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+53776ms]","src":"client"}
{"t":1782657932595,"cat":"MG3","room":"aaa","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-3 Tower block (การ์ด 2)","src":"server"}
{"t":1782657932722,"cat":"MG3","room":"aaa","who":"MONE","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782657933087,"cat":"MG3","room":"aaa","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 4","src":"server"}
{"t":1782657933603,"cat":"MG3","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 4","src":"server"}
{"t":1782657938540,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=5seat othHumans=[MONE(self)] :: s1:MONE blk=3 av=3 | s2:บอท 1 blk=1 av=1 | s3:บอท 2 blk=2 av=2 | s4:บอท 3 blk=4 av=4 | s5:บอท 4 blk=5 av=5","src":"client"}
{"t":1782657941476,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=4 av=4 | s6:บอท 4 blk=5 av=5","src":"client"}
{"t":1782657946674,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=4 av=4 | s6:บอท 4 blk=5 av=5 || layers(0)=[]","src":"client"}
{"t":1782657946716,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=4 av=4 | s6:บอท 4 blk=5 av=5 || layers(0)=[]","src":"client"}
{"t":1782657948958,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-mismatch","detail":"บอท 3 (ฉลาด) seat5 avatar#4 แต่ block#3 (srvnull\/bc3) | บอท 4 (กลาง) seat6 avatar#5 แต่ block#4 (srvnull\/bc4)","src":"client"}
{"t":1782657948962,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-mismatch","detail":"บอท 3 (ฉลาด) seat5 avatar#4 แต่ block#3 (srvnull\/bc3) | บอท 4 (กลาง) seat6 avatar#5 แต่ block#4 (srvnull\/bc4)","src":"client"}
{"t":1782657953538,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(1)=[1:7]","src":"client"}
{"t":1782657953540,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(1)=[1:7]","src":"client"}
{"t":1782657960349,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(1)=[1:7]","src":"client"}
{"t":1782657960351,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(1)=[1:7]","src":"client"}
{"t":1782657965350,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(2)=[1:7,3:1]","src":"client"}
{"t":1782657965354,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(2)=[1:7,3:1]","src":"client"}
{"t":1782657971380,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(2)=[1:7,3:1]","src":"client"}
{"t":1782657971382,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(2)=[1:7,3:1]","src":"client"}
{"t":1782657978551,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(3)=[1:7,3:1,5:3]","src":"client"}
{"t":1782657978552,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(3)=[1:7,3:1,5:3]","src":"client"}
{"t":1782657985047,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(3)=[1:7,3:1,5:3]","src":"client"}
{"t":1782657985050,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(3)=[1:7,3:1,5:3]","src":"client"}
{"t":1782657991609,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(4)=[1:7,3:1,5:3,1:7]","src":"client"}
{"t":1782657991611,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(4)=[1:7,3:1,5:3,1:7]","src":"client"}
{"t":1782657996671,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(5)=[1:7,3:1,5:3,1:7,2:3]","src":"client"}
{"t":1782657996675,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(5)=[1:7,3:1,5:3,1:7,2:3]","src":"client"}
{"t":1782658002152,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(5)=[1:7,3:1,5:3,1:7,2:3]","src":"client"}
{"t":1782658002152,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(5)=[1:7,3:1,5:3,1:7,2:3]","src":"client"}
{"t":1782658009144,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(6)=[1:7,3:1,5:3,1:7,2:3,4:2]","src":"client"}
{"t":1782658009146,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(6)=[1:7,3:1,5:3,1:7,2:3,4:2]","src":"client"}
{"t":1782658015729,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(7)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4]","src":"client"}
{"t":1782658015731,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(7)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4]","src":"client"}
{"t":1782658022111,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(8)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4,1:7]","src":"client"}
{"t":1782658022116,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(8)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4,1:7]","src":"client"}
{"t":1782658029838,"cat":"MG3","room":"aaa-1782657858314","who":"client","ev":"color-snap","detail":"order=6seat othHumans=[MONE(self),Q] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(9)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4,1:7,2:3]","src":"client"}
{"t":1782658029841,"cat":"MG3","room":"aaa-1782657858314","who":"host","ev":"color-snap","detail":"order=6seat othHumans=[Q(self),MONE] :: s1:Q blk=7 av=7 | s2:MONE blk=3 av=3 | s3:บอท 1 blk=1 av=1 | s4:บอท 2 blk=2 av=2 | s5:บอท 3 blk=3 av=4 | s6:บอท 4 blk=4 av=5 || layers(9)=[1:7,3:1,5:3,1:7,2:3,4:2,6:4,1:7,2:3]","src":"client"}
{"t":1782658046379,"cat":"LobbyB","room":"aaa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658046380,"cat":"LobbyB","room":"aaa","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
{"t":1782658046642,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"lobby-open","detail":"space=aaa-1782657858314 [+0ms]","src":"client"}
{"t":1782658046710,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"preload-start","detail":" [+51ms]","src":"client"}
{"t":1782658046761,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"colors-resolved","detail":" [+133ms]","src":"client"}
{"t":1782658046765,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"manifest-done","detail":" [+132ms]","src":"client"}
{"t":1782658046801,"cat":"LobbyA","room":"aaa-1782657858314","who":"Q","ev":"socket-connect","detail":" [+156ms]","src":"client"}
{"t":1782658046848,"cat":"LobbyB","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 4","src":"server"}
{"t":1782658047595,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"join-ok","detail":"peers=2 host=me map=mn8nx46h bots=4 [+927ms]","src":"client"}
{"t":1782658047619,"cat":"LobbyB","room":"aaa","who":"MONE","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
{"t":1782658047996,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"lobby-open","detail":"space=aaa-1782657858314 [+0ms]","src":"client"}
{"t":1782658048039,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-start","detail":" [+18ms]","src":"client"}
{"t":1782658048043,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"manifest-done","detail":" [+18ms]","src":"client"}
{"t":1782658048070,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"colors-resolved","detail":" [+19ms]","src":"client"}
{"t":1782658048078,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+19ms]","src":"client"}
{"t":1782658048081,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-occupants-done","detail":" [+19ms]","src":"client"}
{"t":1782658048082,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"preload-ready(occupants)","detail":" [+19ms]","src":"client"}
{"t":1782658048170,"cat":"LobbyA","room":"aaa-1782657858314","who":"MONE","ev":"socket-connect","detail":" [+155ms]","src":"client"}
{"t":1782658048212,"cat":"LobbyB","room":"aaa","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782658049195,"cat":"LobbyB","room":"aaa-1782657858314","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mn8nx46h bots=4 [+1168ms]","src":"client"}
{"t":1782658049196,"cat":"LobbyB","room":"aaa-1782657858314","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+1168ms]","src":"client"}
{"t":1782658051977,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+5348ms]","src":"client"}
{"t":1782658066283,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"preload-occupants-done","detail":" [+19652ms]","src":"client"}
{"t":1782658066285,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+19653ms]","src":"client"}
{"t":1782658066287,"cat":"LobbyB","room":"aaa-1782657858314","who":"Q","ev":"preload-ready(occupants)","detail":" [+19653ms]","src":"client"}
{"t":1782658078981,"cat":"MG4","room":"aaa","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-4 คำศัพท์ในกระบวนการยุติธรรม (การ์ด 1)","src":"server"}
{"t":1782658079091,"cat":"MG4","room":"aaa","who":"MONE","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658079354,"cat":"MG4","room":"aaa","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782658087176,"cat":"MG4","room":"aaa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658087176,"cat":"MG4","room":"aaa","who":"Q","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658097177,"cat":"MG4","room":"aaa","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
{"t":1782658104419,"cat":"MG4","room":"aaa","who":"","ev":"mg4-read","detail":"q=1 deck=29 rounds=0/10","src":"server"}
{"t":1782658112557,"cat":"MG4","room":"aaa","who":"","ev":"mg4-round","detail":"resolve 1/10 q=1 winner=IO69d3h9f8s-WOSFAAAN willEnd=false","src":"server"}
{"t":1782658115058,"cat":"MG4","room":"aaa","who":"","ev":"mg4-read","detail":"q=2 deck=28 rounds=1/10","src":"server"}
{"t":1782658141049,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"lobby-open","detail":"space=aa-1782658138076 [+0ms]","src":"client"}
{"t":1782658141124,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"preload-start","detail":" [+24ms]","src":"client"}
{"t":1782658141187,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"manifest-done","detail":" [+131ms]","src":"client"}
{"t":1782658141197,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"colors-resolved","detail":" [+131ms]","src":"client"}
{"t":1782658141241,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"socket-connect","detail":" [+178ms]","src":"client"}
{"t":1782658141266,"cat":"LobbyA","room":"aa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 4","src":"server"}
{"t":1782658142114,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"join-ok","detail":"peers=1 host=me map=mlsbbxfe bots=4 [+1041ms]","src":"client"}
{"t":1782658145353,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+4304ms]","src":"client"}
{"t":1782658146308,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"lobby-open","detail":"space=aa-1782658138076 [+0ms]","src":"client"}
{"t":1782658146348,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"preload-start","detail":" [+33ms]","src":"client"}
{"t":1782658146351,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"manifest-done","detail":" [+33ms]","src":"client"}
{"t":1782658146355,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"colors-resolved","detail":" [+33ms]","src":"client"}
{"t":1782658146361,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+33ms]","src":"client"}
{"t":1782658146367,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"preload-occupants-done","detail":" [+34ms]","src":"client"}
{"t":1782658146373,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"preload-ready(occupants)","detail":" [+34ms]","src":"client"}
{"t":1782658146453,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"socket-connect","detail":" [+171ms]","src":"client"}
{"t":1782658146477,"cat":"LobbyA","room":"aa","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782658147168,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mlsbbxfe bots=4 [+851ms]","src":"client"}
{"t":1782658147169,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+851ms]","src":"client"}
{"t":1782658154014,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"preload-occupants-done","detail":" [+12959ms]","src":"client"}
{"t":1782658154014,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+12960ms]","src":"client"}
{"t":1782658154014,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"preload-ready(occupants)","detail":" [+12960ms]","src":"client"}
{"t":1782658159560,"cat":"LobbyA","room":"aa-1782658138076","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+13282ms]","src":"client"}
{"t":1782658163350,"cat":"LobbyB","room":"aa-1782658138076","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+17072ms]","src":"client"}
{"t":1782658175488,"cat":"LobbyA","room":"aa-1782658138076","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+34436ms]","src":"client"}
{"t":1782658178997,"cat":"LobbyB","room":"aa-1782658138076","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+37941ms]","src":"client"}
{"t":1782658198372,"cat":"LobbyB","room":"aa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658198372,"cat":"LobbyB","room":"aa","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
{"t":1782658204974,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"lobby-open","detail":"space=aasd-1782658201995 [+0ms]","src":"client"}
{"t":1782658205019,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"preload-start","detail":" [+32ms]","src":"client"}
{"t":1782658205107,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"manifest-done","detail":" [+128ms]","src":"client"}
{"t":1782658205112,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"colors-resolved","detail":" [+128ms]","src":"client"}
{"t":1782658205154,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"socket-connect","detail":" [+155ms]","src":"client"}
{"t":1782658205156,"cat":"LobbyA","room":"aasd","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 3","src":"server"}
{"t":1782658205996,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"join-ok","detail":"peers=1 host=me map=mlsbbxfe bots=3 [+992ms]","src":"client"}
{"t":1782658208373,"cat":"LobbyB","room":"aa","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
{"t":1782658209525,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+4548ms]","src":"client"}
{"t":1782658212177,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"lobby-open","detail":"space=aasd-1782658201995 [+0ms]","src":"client"}
{"t":1782658212223,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-start","detail":" [+26ms]","src":"client"}
{"t":1782658212246,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"manifest-done","detail":" [+26ms]","src":"client"}
{"t":1782658212259,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"colors-resolved","detail":" [+27ms]","src":"client"}
{"t":1782658212262,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+27ms]","src":"client"}
{"t":1782658212268,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-ready(occupants)","detail":" [+27ms]","src":"client"}
{"t":1782658212269,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-occupants-done","detail":" [+27ms]","src":"client"}
{"t":1782658212352,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"socket-connect","detail":" [+161ms]","src":"client"}
{"t":1782658212355,"cat":"LobbyA","room":"aasd","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 3","src":"server"}
{"t":1782658213078,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mlsbbxfe bots=3 [+851ms]","src":"client"}
{"t":1782658213079,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+851ms]","src":"client"}
{"t":1782658219985,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"preload-occupants-done","detail":" [+14994ms]","src":"client"}
{"t":1782658219986,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"preload-ready(occupants)","detail":" [+14994ms]","src":"client"}
{"t":1782658219986,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+14994ms]","src":"client"}
{"t":1782658228528,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+16350ms]","src":"client"}
{"t":1782658228781,"cat":"LobbyA","room":"aasd-1782658201995","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+23784ms]","src":"client"}
{"t":1782658230628,"cat":"LobbyB","room":"aasd-1782658201995","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+25651ms]","src":"client"}
{"t":1782658232284,"cat":"LobbyB","room":"aasd-1782658201995","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+20104ms]","src":"client"}
{"t":1782658235835,"cat":"MG5","room":"aasd","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-5 กระโดดขึ้นแท่น (การ์ด 1)","src":"server"}
{"t":1782658235957,"cat":"MG5","room":"aasd","who":"MONE","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658236215,"cat":"MG5","room":"aasd","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 3","src":"server"}
{"t":1782658236632,"cat":"MG5","room":"aasd","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 3","src":"server"}
{"t":1782658241827,"cat":"MG5","room":"aasd","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658241827,"cat":"MG5","room":"aasd","who":"Q","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658251827,"cat":"MG5","room":"aasd","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
{"t":1782658270550,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"lobby-open","detail":"space=aasd-1782658201995 [+0ms]","src":"client"}
{"t":1782658270591,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-start","detail":" [+10ms]","src":"client"}
{"t":1782658270594,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"manifest-done","detail":" [+10ms]","src":"client"}
{"t":1782658270603,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"colors-resolved","detail":" [+10ms]","src":"client"}
{"t":1782658270631,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+11ms]","src":"client"}
{"t":1782658270632,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-occupants-done","detail":" [+11ms]","src":"client"}
{"t":1782658270638,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"preload-ready(occupants)","detail":" [+11ms]","src":"client"}
{"t":1782658270724,"cat":"LobbyA","room":"aasd-1782658201995","who":"MONE","ev":"socket-connect","detail":" [+142ms]","src":"client"}
{"t":1782658270774,"cat":"LobbyB","room":"aasd","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 4","src":"server"}
{"t":1782658271540,"cat":"LobbyB","room":"aasd-1782658201995","who":"MONE","ev":"join-ok","detail":"peers=1 host=me map=mn8nx46h bots=4 [+989ms]","src":"client"}
{"t":1782658271553,"cat":"LobbyB","room":"aasd-1782658201995","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+989ms]","src":"client"}
{"t":1782658281400,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"lobby-open","detail":"space=aa-1782658278396 [+0ms]","src":"client"}
{"t":1782658281442,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"preload-start","detail":" [+42ms]","src":"client"}
{"t":1782658281503,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"manifest-done","detail":" [+105ms]","src":"client"}
{"t":1782658281513,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"colors-resolved","detail":" [+106ms]","src":"client"}
{"t":1782658281548,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"socket-connect","detail":" [+134ms]","src":"client"}
{"t":1782658281533,"cat":"LobbyA","room":"aa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 4","src":"server"}
{"t":1782658282173,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"join-ok","detail":"peers=1 host=me map=mlsbbxfe bots=4 [+740ms]","src":"client"}
{"t":1782658285285,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"lobby-open","detail":"space=aa-1782658278396 [+0ms]","src":"client"}
{"t":1782658285330,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"manifest-done","detail":" [+24ms]","src":"client"}
{"t":1782658285330,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-start","detail":" [+23ms]","src":"client"}
{"t":1782658285364,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+24ms]","src":"client"}
{"t":1782658285365,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"colors-resolved","detail":" [+24ms]","src":"client"}
{"t":1782658285370,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-occupants-done","detail":" [+24ms]","src":"client"}
{"t":1782658285372,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-ready(occupants)","detail":" [+25ms]","src":"client"}
{"t":1782658285381,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+3994ms]","src":"client"}
{"t":1782658285507,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"socket-connect","detail":" [+185ms]","src":"client"}
{"t":1782658285513,"cat":"LobbyA","room":"aa","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782658286239,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mlsbbxfe bots=4 [+957ms]","src":"client"}
{"t":1782658286263,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+957ms]","src":"client"}
{"t":1782658296799,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"preload-occupants-done","detail":" [+15409ms]","src":"client"}
{"t":1782658296803,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"preload-ready(occupants)","detail":" [+15409ms]","src":"client"}
{"t":1782658296804,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+15409ms]","src":"client"}
{"t":1782658301668,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+16382ms]","src":"client"}
{"t":1782658306771,"cat":"LobbyB","room":"aa-1782658278396","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+21487ms]","src":"client"}
{"t":1782658308819,"cat":"LobbyA","room":"aa-1782658278396","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+27423ms]","src":"client"}
{"t":1782658310781,"cat":"LobbyB","room":"aa-1782658278396","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+29390ms]","src":"client"}
{"t":1782658314395,"cat":"MG6","room":"aa","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-6 ยิงอุกาบาต Violent Crime (การ์ด 2)","src":"server"}
{"t":1782658314503,"cat":"MG6","room":"aa","who":"MONE","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658314759,"cat":"MG6","room":"aa","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 4","src":"server"}
{"t":1782658315156,"cat":"MG6","room":"aa","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 4","src":"server"}
{"t":1782658342569,"cat":"MG6","room":"aa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658342569,"cat":"MG6","room":"aa","who":"Q","ev":"leave","detail":"หลุด MG6 → บอทแทนในเกม (สี/ตำแหน่งเดิม) · เหลือคน 1","src":"server"}
{"t":1782658352570,"cat":"MG6","room":"aa","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
{"t":1782658388065,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"lobby-open","detail":"space=ss-1782658384317 [+0ms]","src":"client"}
{"t":1782658388110,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"preload-start","detail":" [+21ms]","src":"client"}
{"t":1782658388169,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"manifest-done","detail":" [+113ms]","src":"client"}
{"t":1782658388173,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"colors-resolved","detail":" [+114ms]","src":"client"}
{"t":1782658388222,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"socket-connect","detail":" [+138ms]","src":"client"}
{"t":1782658388219,"cat":"LobbyA","room":"ss","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 4","src":"server"}
{"t":1782658388985,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"join-ok","detail":"peers=1 host=me map=mlsbbxfe bots=4 [+913ms]","src":"client"}
{"t":1782658391935,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"mychar-tinted","detail":"roomJoinReady=true [+3875ms]","src":"client"}
{"t":1782658402185,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"lobby-open","detail":"space=aa-1782658278396 [+0ms]","src":"client"}
{"t":1782658402205,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-start","detail":" [+10ms]","src":"client"}
{"t":1782658402227,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"manifest-done","detail":" [+10ms]","src":"client"}
{"t":1782658402229,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+10ms]","src":"client"}
{"t":1782658402230,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"colors-resolved","detail":" [+10ms]","src":"client"}
{"t":1782658402231,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-occupants-done","detail":" [+11ms]","src":"client"}
{"t":1782658402244,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"preload-ready(occupants)","detail":" [+11ms]","src":"client"}
{"t":1782658402328,"cat":"LobbyA","room":"aa-1782658278396","who":"MONE","ev":"socket-connect","detail":" [+151ms]","src":"client"}
{"t":1782658402379,"cat":"LobbyB","room":"aa","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 5","src":"server"}
{"t":1782658403071,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+14969ms]","src":"client"}
{"t":1782658403075,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"preload-ready(occupants)","detail":" [+14968ms]","src":"client"}
{"t":1782658403076,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"preload-occupants-done","detail":" [+14968ms]","src":"client"}
{"t":1782658403171,"cat":"LobbyB","room":"aa-1782658278396","who":"MONE","ev":"join-ok","detail":"peers=1 host=me map=mn8nx46h bots=5 [+983ms]","src":"client"}
{"t":1782658403173,"cat":"LobbyB","room":"aa-1782658278396","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+983ms]","src":"client"}
{"t":1782658414876,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"lobby-open","detail":"space=ss-1782658384317 [+1ms]","src":"client"}
{"t":1782658414901,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-start","detail":" [+15ms]","src":"client"}
{"t":1782658414921,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"manifest-done","detail":" [+15ms]","src":"client"}
{"t":1782658414921,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"colors-resolved","detail":" [+15ms]","src":"client"}
{"t":1782658414924,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+15ms]","src":"client"}
{"t":1782658414926,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-occupants-done","detail":" [+16ms]","src":"client"}
{"t":1782658414945,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-ready(occupants)","detail":" [+16ms]","src":"client"}
{"t":1782658415009,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"socket-connect","detail":" [+153ms]","src":"client"}
{"t":1782658415033,"cat":"LobbyA","room":"ss","who":"MONE","ev":"join","detail":"สี#3 · คน 2 + บอท 4","src":"server"}
{"t":1782658415905,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"join-ok","detail":"peers=2 host=other map=mlsbbxfe bots=4 [+1041ms]","src":"client"}
{"t":1782658415925,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+1042ms]","src":"client"}
{"t":1782658424421,"cat":"LobbyA","room":"ss-1782658384317","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+36359ms]","src":"client"}
{"t":1782658427084,"cat":"LobbyB","room":"ss-1782658384317","who":"Q","ev":"loading-hide","detail":"เริ่มเล่นได้ [+39019ms]","src":"client"}
{"t":1782658429248,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+14352ms]","src":"client"}
{"t":1782658432442,"cat":"LobbyB","room":"ss-1782658384317","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+17584ms]","src":"client"}
{"t":1782658433928,"cat":"MG7","room":"ss","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-7 ยิง Mega Virus (การ์ด 3)","src":"server"}
{"t":1782658434049,"cat":"MG7","room":"ss","who":"MONE","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658434423,"cat":"MG7","room":"ss","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 4","src":"server"}
{"t":1782658434517,"cat":"MG7","room":"ss","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 4","src":"server"}
{"t":1782658451112,"cat":"MG7","room":"ss","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
{"t":1782658451113,"cat":"MG7","room":"ss","who":"Q","ev":"leave","detail":"หลุดช่วง how-to → บอทรับช่วงตอนเกมเริ่ม · เหลือคน 1","src":"server"}
{"t":1782658461113,"cat":"MG7","room":"ss","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
{"t":1782658523471,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"lobby-open","detail":"space=ss-1782658384317 [+0ms]","src":"client"}
{"t":1782658523512,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"manifest-done","detail":" [+30ms]","src":"client"}
{"t":1782658523517,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-start","detail":" [+30ms]","src":"client"}
{"t":1782658523517,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"colors-resolved","detail":" [+30ms]","src":"client"}
{"t":1782658523539,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"mychar-tinted","detail":"roomJoinReady=false [+30ms]","src":"client"}
{"t":1782658523542,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-occupants-done","detail":" [+31ms]","src":"client"}
{"t":1782658523553,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"preload-ready(occupants)","detail":" [+31ms]","src":"client"}
{"t":1782658523629,"cat":"LobbyA","room":"ss-1782658384317","who":"MONE","ev":"socket-connect","detail":" [+149ms]","src":"client"}
{"t":1782658523677,"cat":"LobbyB","room":"ss","who":"MONE","ev":"join","detail":"สี#3 · คน 1 + บอท 5","src":"server"}
{"t":1782658524557,"cat":"LobbyB","room":"ss-1782658384317","who":"MONE","ev":"join-ok","detail":"peers=1 host=me map=mn8nx46h bots=5 [+1076ms]","src":"client"}
{"t":1782658524579,"cat":"LobbyB","room":"ss-1782658384317","who":"MONE","ev":"loading-hide","detail":"เริ่มเล่นได้ [+1076ms]","src":"client"}
File diff suppressed because it is too large Load Diff