update all game still have issue on mg 7 6 a bit
This commit is contained in:
@@ -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
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
@@ -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·(1−prog) → 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();
|
||||
|
||||
@@ -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
@@ -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 (MG3–MG7) ───────────────────────────
|
||||
มินิเกมเหล่านี้สร้าง 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 */ }
|
||||
/* MG3–MG7 หลุดช่วง 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) ให้เวลาในเกมตรงกันทุกเครื่อง */
|
||||
|
||||
@@ -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"}
|
||||
|
||||
+600
-930
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user