fix on testcase 1.3
This commit is contained in:
@@ -51,6 +51,8 @@
|
||||
</div>
|
||||
<div class="admin-top-actions" id="top-actions" hidden>
|
||||
<a href="/Admin/justice-git.html" class="btn btn-ghost" style="text-decoration:none;margin-right:0.35rem;">อัป Git (justice)</a>
|
||||
<a href="/gamelog/" target="_blank" rel="noopener noreferrer" class="btn btn-ghost" style="text-decoration:none;margin-right:0.35rem;">📊 Game Log</a>
|
||||
<a href="/testcase/" target="_blank" rel="noopener noreferrer" class="btn btn-ghost" style="text-decoration:none;margin-right:0.35rem;">🐞 Test Case</a>
|
||||
<span class="admin-user" id="admin-user-label"></span>
|
||||
<button type="button" class="btn btn-ghost btn-logout" id="btn-logout">ออกจากระบบ</button>
|
||||
</div>
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
"providerUserId": "p_1775109142385_wq7wfy1p32j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 1918,
|
||||
"coins": 2072,
|
||||
"createdAt": "2026-04-02T05:52:21+00:00",
|
||||
"updatedAt": "2026-06-25T16:09:47+00:00",
|
||||
"updatedAt": "2026-06-26T07:58:33+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781197200000,
|
||||
"claimedDays": [
|
||||
@@ -67,23 +67,23 @@
|
||||
],
|
||||
"lockUntilMs": 0
|
||||
},
|
||||
"score": 1738,
|
||||
"score": 18,
|
||||
"scoreByCase": {
|
||||
"1": 16,
|
||||
"10": 446,
|
||||
"10": 514,
|
||||
"8": 296,
|
||||
"11": 20,
|
||||
"13": 186,
|
||||
"13": 224,
|
||||
"14": 30,
|
||||
"9": 40,
|
||||
"12": 380,
|
||||
"12": 408,
|
||||
"15": 20,
|
||||
"4": 20,
|
||||
"5": 32,
|
||||
"7": 6,
|
||||
"3": 132,
|
||||
"3": 142,
|
||||
"6": 44,
|
||||
"2": 70
|
||||
"2": 80
|
||||
},
|
||||
"lbName": "Q",
|
||||
"achievements": {
|
||||
@@ -91,7 +91,7 @@
|
||||
"a1_first_deduction": 1,
|
||||
"a5_truth_hunter": 3
|
||||
},
|
||||
"lobbyColorThemeIndex": 8,
|
||||
"lobbyColorThemeIndex": 7,
|
||||
"lobbySkinToneIndex": 1
|
||||
},
|
||||
{
|
||||
@@ -651,21 +651,23 @@
|
||||
"providerUserId": "p_1782016990238_0u37bv7pxlxs",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 150,
|
||||
"coins": 288,
|
||||
"createdAt": "2026-06-21T04:43:11+00:00",
|
||||
"updatedAt": "2026-06-25T15:49:43+00:00",
|
||||
"updatedAt": "2026-06-26T08:07:51+00:00",
|
||||
"lobbyColorThemeIndex": 8,
|
||||
"lobbySkinToneIndex": 1,
|
||||
"score": 120,
|
||||
"score": 258,
|
||||
"scoreByCase": {
|
||||
"13": 56,
|
||||
"10": 46,
|
||||
"13": 82,
|
||||
"10": 120,
|
||||
"6": 8,
|
||||
"8": 10
|
||||
"8": 20,
|
||||
"2": 18,
|
||||
"3": 10
|
||||
},
|
||||
"lbName": "MONE",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 12
|
||||
"d1_minigame_solver": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -893,17 +895,18 @@
|
||||
"providerUserId": "p_1782376007927_lpt9m4xge9",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 62,
|
||||
"coins": 82,
|
||||
"createdAt": "2026-06-25T08:26:49+00:00",
|
||||
"updatedAt": "2026-06-25T14:34:19+00:00",
|
||||
"score": 62,
|
||||
"updatedAt": "2026-06-26T04:11:43+00:00",
|
||||
"score": 82,
|
||||
"scoreByCase": {
|
||||
"12": 42,
|
||||
"8": 20
|
||||
"12": 52,
|
||||
"8": 20,
|
||||
"3": 10
|
||||
},
|
||||
"lbName": "MONE",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 7
|
||||
"d1_minigame_solver": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
""
|
||||
],
|
||||
"stackHeavyBlockPercent": 35,
|
||||
"jumpSurviveJumpHeightMult": 1.5,
|
||||
"jumpSurviveJumpHeightMult": 1.7,
|
||||
"jumpSurviveMissionTimeSec": 0,
|
||||
"jumpSurvivePlatformTiles": [
|
||||
{
|
||||
@@ -51,7 +51,7 @@
|
||||
{
|
||||
"url": "/Game/img/Jumper/platfrom-2.png",
|
||||
"w": 389,
|
||||
"h": 33
|
||||
"h": 36
|
||||
},
|
||||
{
|
||||
"url": "/Game/img/Jumper/platfrom-3.png",
|
||||
@@ -74,7 +74,7 @@
|
||||
"/Game/img/ViolentCrime/Meteo-3.png"
|
||||
],
|
||||
"spaceShooterAsteroidExplodeFrameMs": 60,
|
||||
"spaceShooterAsteroidIntervalMs": 200,
|
||||
"spaceShooterAsteroidIntervalMs": 300,
|
||||
"spaceShooterShipDamageOverlayUrls": [
|
||||
"/Game/img/ViolentCrime/Rocket-broke-1.png",
|
||||
"/Game/img/ViolentCrime/Rocket-broke-2.png",
|
||||
@@ -95,9 +95,9 @@
|
||||
"balloonBossPlayerBalloonFallbackUrl": "/Game/img/MegaVirus/Artboard%209.png",
|
||||
"balloonBossBalloonsPerPlayer": 3,
|
||||
"forcedMinigameKeys": [
|
||||
"gauntlet",
|
||||
"stack",
|
||||
"balloon_boss"
|
||||
"jump_survive",
|
||||
"space_shooter"
|
||||
],
|
||||
"testSpecialCardByMap": {},
|
||||
"troublesomeForceOffer": false,
|
||||
|
||||
File diff suppressed because one or more lines are too long
+157
-34
@@ -691,11 +691,23 @@
|
||||
/* host: ผู้เล่นจริงทุกคน (ตัด banned spectator กัน stall) + บอท เรียง id → ที่นั่งคงที่; preserve คนที่กำลังถึงตา */
|
||||
const prevActorId = (stackCurrentTurnEntryPlay() && stackCurrentTurnEntryPlay().id) || null;
|
||||
const parts = [];
|
||||
if (myId != null && !playBannedSpectator) parts.push({ kind: 'human', id: String(myId) });
|
||||
/* dedup คนซ้ำ (ghost reconnect) ก่อนสร้างคิวตา — กัน seat ผี (ตาที่ไม่มีใครวาง = เกมค้าง)
|
||||
⚠️ เทียบด้วย playerKey เท่านั้น — **ห้าม dedup ด้วยชื่อ**: 2 คนตั้งชื่อเหมือนกัน (เช่น "MONE") = คนละ key = คนละคน
|
||||
ต้องได้ seat ทั้งคู่ (เดิม fallback ชื่อ → client ที่ชื่อซ้ำ host ถูกตัด = "ไม่ได้เล่นเลย") */
|
||||
const seenHumanKey = new Set();
|
||||
const tryAddHuman = (id, ent) => {
|
||||
const pk = (ent && ent.playerKey) ? String(ent.playerKey) : (String(id) === String(myId) ? (typeof getJdPlayerKey === 'function' ? getJdPlayerKey() : '') : '');
|
||||
if (pk) {
|
||||
if (seenHumanKey.has(pk)) return; /* playerKey เดียวกัน = ghost reconnect → ข้าม */
|
||||
seenHumanKey.add(pk);
|
||||
}
|
||||
parts.push({ kind: 'human', id: String(id) });
|
||||
};
|
||||
if (myId != null && !playBannedSpectator) tryAddHuman(myId, me);
|
||||
others.forEach((o, id) => {
|
||||
if (isPreviewBotId(id)) { parts.push({ kind: 'bot', id: String(id), botId: String(id) }); return; }
|
||||
if (o && o.bannedSpectator) return; /* คนถูกแบน = ดูเฉยๆ ไม่เข้าคิววาง */
|
||||
parts.push({ kind: 'human', id: String(id) });
|
||||
tryAddHuman(id, o);
|
||||
});
|
||||
parts.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
||||
const capped = parts.slice(0, STACK_PREVIEW_TURN_COUNT);
|
||||
@@ -720,6 +732,7 @@
|
||||
/* theme ต่อที่นั่ง (host เป็นเจ้าของ) → ทุกจอลงสี block ตรงกัน (เดิม non-host คำนวณสีเองจาก playTint → ไม่ตรง 2 จอ) */
|
||||
order: stackPreviewTurnOrder.map((e) => ({ kind: e.kind, seat: e.seat, id: e.id, botId: e.botId || null, theme: stackSeatColorThemePlay(e.seat) || null })),
|
||||
});
|
||||
logStackSeatColors('host-bcast');
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
@@ -753,6 +766,8 @@
|
||||
pts: hit.pts || 0, progressDelta: hit.progressDelta || 0, landOffsetTiles: hit.landOffsetTiles || 0,
|
||||
overlap: hit.overlap, supportRatio: hit.supportRatio, delta: hit.delta || 0,
|
||||
seat: seat || 1, heavy: !!heavy, actorId: actorId != null ? String(actorId) : '', isBot: !!isBot,
|
||||
/* สี (theme) ที่ host/เจ้าของตา resolve ตอนวาง → ส่งไปด้วย ให้บล็อกที่วางบนทุกจอใช้สีเดียวกัน (กัน host/client คนละสี) */
|
||||
theme: stackSeatColorThemePlay(seat) || null,
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
@@ -802,18 +817,76 @@
|
||||
const s = Math.floor(Number(seat)) || 0;
|
||||
const entry = stackPreviewTurnOrder.find(function (e) { return e && e.seat === s; });
|
||||
if (!entry) return null;
|
||||
/* theme ที่ host broadcast มา (stack-state) — ตรงกันทุกจอ → ใช้ก่อน (เดิมคำนวณ local จาก playTint = ไม่ตรง 2 จอ) */
|
||||
/* (1) สีที่ host broadcast มาทาง stack-state = SINGLE SOURCE OF TRUTH → client ยึดค่า host เสมอ (สีตรง host ทุกจอ)
|
||||
เกม MG3 host เป็นเจ้าของลำดับตา/สี; host เองไม่มี entry.theme (คำนวณสดตอน broadcast) → ตกไป (2) authoritative
|
||||
เหตุที่ต้อง broadcast-first: ถ้า client คำนวณเอง (lobbyThemeIndexSrv/playLobbyBotThemes ของ client) แล้วข้อมูลมาช้า/ไม่ตรง
|
||||
→ สีหลุดจาก host เพี้ยนเรื่อย ๆ. ใช้ค่า host = ไม่มีทางหลุด */
|
||||
const bcTheme = parsePlayLobbyThemeIndex(entry.theme);
|
||||
if (bcTheme) return bcTheme;
|
||||
/* (2) authoritative ต่อ entity (สำหรับ "host เอง" + ตอน client ยังไม่ได้รับ broadcast):
|
||||
bot → playLobbyBotThemes[slot] (server) ; human → lobbyThemeIndexSrv (= server lobbyColorThemeIndex) */
|
||||
const botId = entry.botId || (entry.kind === 'bot' ? entry.id : null);
|
||||
if (botId && isPreviewBotId(botId)) {
|
||||
const slot = parseInt(String(botId).slice(PREVIEW_BOT_PREFIX.length), 10);
|
||||
const bt = (slot >= 0 && Array.isArray(playLobbyBotThemes)) ? playLobbyBotThemes[slot] : null;
|
||||
const botIdx = bt ? parsePlayLobbyThemeIndex(bt.themeIndex) : null;
|
||||
if (botIdx) return botIdx;
|
||||
} else if (entry.id != null) {
|
||||
const entH = (myId != null && String(entry.id) === String(myId)) ? me : (others.get(entry.id) || others.get(String(entry.id)));
|
||||
const humanIdx = entH ? parsePlayLobbyThemeIndex(entH.lobbyThemeIndexSrv) : null;
|
||||
if (humanIdx) return humanIdx;
|
||||
}
|
||||
/* (3) ทางเลือกสุดท้าย: เดาจาก playTint (nearest hex — อาจไม่ตรงข้ามจอ) */
|
||||
let ent = null;
|
||||
if (entry.id != null) ent = (myId != null && String(entry.id) === String(myId)) ? me : (others.get(entry.id) || others.get(String(entry.id)));
|
||||
if (!ent && entry.botId) ent = others.get(entry.botId) || others.get(String(entry.botId));
|
||||
if (!ent) return null;
|
||||
/* ใช้ server theme index ก่อน (ตรงกันทุกจอ) — fallback ไป playTint ถ้าไม่มี (เช่น บอท) */
|
||||
const srvIdx = parsePlayLobbyThemeIndex(ent.lobbyThemeIndexSrv);
|
||||
return srvIdx || playLobbyThemeIndexForEntityPlay(ent);
|
||||
return ent ? playLobbyThemeIndexForEntityPlay(ent) : null;
|
||||
}
|
||||
|
||||
/** ส่ง log ไป /gamelog (ดูรวมที่หน้าเว็บ) */
|
||||
function gamelogPost(cat, ev, detail, who) {
|
||||
try {
|
||||
fetch('/gamelog/log.php', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, keepalive: true,
|
||||
body: JSON.stringify({ cat: cat || 'อื่นๆ', room: (typeof spaceId === 'string' ? spaceId : ''), who: who || '', ev: ev || '', detail: detail || '', src: 'client' }),
|
||||
}).catch(function () {});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
/** วินิจฉัยสี block MG3 ต่อ seat — เทียบ "สี avatar (playTint)" vs "สี block (resolved)"; ถ้าไม่ตรง → ส่งเข้า /gamelog
|
||||
เรียกเอง: window.__mg3ColorLog() หรือ auto throttle */
|
||||
let _mg3ColorLogAt = 0;
|
||||
let _mg3LastMismatchKey = '';
|
||||
function logStackSeatColors(tag, force) {
|
||||
try {
|
||||
if (!isStack() || !stackTurnOrderReadyPlay()) return;
|
||||
const now = Date.now();
|
||||
if (!force && now - _mg3ColorLogAt < 2000) return;
|
||||
_mg3ColorLogAt = now;
|
||||
const rows = stackPreviewTurnOrder.map(function (e) {
|
||||
let ent = null;
|
||||
if (e.id != null) ent = (myId != null && String(e.id) === String(myId)) ? me : (others.get(e.id) || others.get(String(e.id)));
|
||||
if (!ent && e.botId) ent = others.get(e.botId) || others.get(String(e.botId));
|
||||
const tint = ent && ent.playTint && ent.playTint.body ? ent.playTint.body : '';
|
||||
const avatarIdx = tint ? nearestIndexFromHexPlay(tint, PLAY_LOBBY_THEME_HEX) : null; /* สีจริงของ avatar (จาก playTint) */
|
||||
const blockIdx = stackSeatColorThemePlay(e.seat); /* สี block ที่จะวาด */
|
||||
return { seat: e.seat, kind: e.kind, nick: (ent && ent.nickname) || String(e.id || e.botId || '').slice(0, 6), avatarIdx: avatarIdx, blockIdx: blockIdx, srv: (ent && ent.lobbyThemeIndexSrv) || null, bc: e.theme || null, ok: (avatarIdx && blockIdx) ? (avatarIdx === blockIdx) : null };
|
||||
});
|
||||
console.log('[mg3-color/' + (tag || '') + '] host=' + isMePlayHost(), JSON.stringify(rows));
|
||||
/* ส่งเฉพาะกรณีพบ mismatch (avatar≠block) → เห็นใน /gamelog (MG3) ทันที ไม่ต้องเปิด console */
|
||||
const bad = rows.filter(function (r) { return r.ok === false; });
|
||||
if (bad.length) {
|
||||
const key = (isMePlayHost() ? 'H' : 'C') + '|' + bad.map(function (r) { return r.seat + ':' + r.avatarIdx + '!=' + r.blockIdx; }).join(',');
|
||||
if (key !== _mg3LastMismatchKey) {
|
||||
_mg3LastMismatchKey = key;
|
||||
const role = isMePlayHost() ? 'host' : 'client';
|
||||
const detail = bad.map(function (r) { return r.nick + ' seat' + r.seat + ' avatar#' + r.avatarIdx + ' แต่ block#' + r.blockIdx + ' (srv' + r.srv + '/bc' + r.bc + ')'; }).join(' | ');
|
||||
gamelogPost('MG3', 'color-mismatch', detail, role);
|
||||
}
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
window.__mg3ColorLog = function () { logStackSeatColors('manual', true); };
|
||||
|
||||
function pickStackBlockHeavyVisual(seat) {
|
||||
const slot = Math.max(0, Math.min(7, (Math.floor(Number(seat)) || 1) - 1));
|
||||
let hU = normalizeGauntletAssetUrlForPlay(playStackBlockHeavyUrls[slot] || '');
|
||||
@@ -838,6 +911,7 @@
|
||||
if (!stackMini || !stackMini.nextDropVisualDirty) return;
|
||||
stackMini.pendingDropSeat = getStackDropActorSeat();
|
||||
stackMini.pendingDropHeavy = pickStackBlockHeavyVisual(stackMini.pendingDropSeat);
|
||||
stackMini.pendingDropTheme = null; /* ดรอป local/host → ใช้ stackSeatColorThemePlay เอง (remote ตั้งทับทีหลัง) */
|
||||
stackMini.widthTiles = stackBlockWidthTilesForPlay(stackMini.initialWidthTiles, !!stackMini.pendingDropHeavy);
|
||||
stackMini.nextDropVisualDirty = false;
|
||||
}
|
||||
@@ -2768,12 +2842,13 @@
|
||||
const tiNum = parsePlayLobbyThemeIndex(themeIndex);
|
||||
if (myId != null && sid === String(myId)) {
|
||||
if (tiNum) me.lobbyThemeIndexSrv = tiNum;
|
||||
/* ตัวเราเอง: ยึด "สีที่เลือกไว้" (localStorage) เป็นหลักเสมอ — ไม่ให้ theme-index จาก server มาทับ
|
||||
(สาเหตุสีกระพริบ/สลับ: server sync ส่ง index ที่ไม่ตรงสีที่เลือก แล้วทับ me.playTint ทุกครั้ง) */
|
||||
/* ตัวเราเอง: ใช้ "สีจาก server index" (authoritative) เป็นหลัก — ให้สีเรา "ตรงกันทุกจอ" + block MG3 ตรงกับ avatar
|
||||
เดิม override ด้วย localStorage → จอเราเห็นสีที่เลือก แต่จออื่น+block MG3 ใช้ server index = ไม่ตรง
|
||||
(เคส server reassign สีตอนเลือกชนกัน → server index คือสีจริง ต้องยึดอันนี้) */
|
||||
const tintMe = (tiNum) ? playTintFromLobbyThemeIndices(themeIndex, skinIndex) : null;
|
||||
if (tintMe) { me.playTint = tintMe; return; }
|
||||
const meChosen = loadPlayTintFromLobbyPlayerStorage();
|
||||
if (meChosen) { me.playTint = meChosen; return; }
|
||||
const tintMe = playTintFromLobbyThemeIndices(themeIndex, skinIndex);
|
||||
if (tintMe) me.playTint = tintMe;
|
||||
if (meChosen) me.playTint = meChosen;
|
||||
return;
|
||||
}
|
||||
const oEarly = others.get(peerId) || others.get(sid);
|
||||
@@ -7310,6 +7385,7 @@
|
||||
howto.classList.remove(GCHOWTO_STACK_TOWER_CLASS);
|
||||
}
|
||||
gauntletCrownHowtoVisible = false;
|
||||
hideReadyAvatarsFloat(); /* ซ่อนแถวหน้าตัวละคร ready ตอนเริ่มเกม (กันค้างบนจอระหว่างเล่น) */
|
||||
hideStackTowerHowtoRulesDom();
|
||||
const st = document.getElementById('gauntlet-crown-howto-status');
|
||||
if (st) {
|
||||
@@ -7645,6 +7721,7 @@
|
||||
const howto = document.getElementById('gauntlet-crown-howto-overlay');
|
||||
if (howto) howto.classList.add('is-hidden');
|
||||
gauntletCrownHowtoVisible = false;
|
||||
hideReadyAvatarsFloat(); /* ซ่อนแถวหน้าตัวละคร ready ตอนเริ่มเกม (กันค้างบนจอระหว่างเล่น) */
|
||||
const st = document.getElementById('gauntlet-crown-howto-status');
|
||||
if (st) {
|
||||
st.classList.add('is-hidden');
|
||||
@@ -8025,6 +8102,7 @@
|
||||
const howto = document.getElementById('gauntlet-crown-howto-overlay');
|
||||
if (howto) howto.classList.add('is-hidden');
|
||||
gauntletCrownHowtoVisible = false;
|
||||
hideReadyAvatarsFloat(); /* ซ่อนแถวหน้าตัวละคร ready ตอนเริ่มเกม (กันค้างบนจอระหว่างเล่น) */
|
||||
const st = document.getElementById('gauntlet-crown-howto-status');
|
||||
if (st) {
|
||||
st.classList.add('is-hidden');
|
||||
@@ -8601,6 +8679,7 @@
|
||||
const howto = document.getElementById('gauntlet-crown-howto-overlay');
|
||||
if (howto) howto.classList.add('is-hidden');
|
||||
gauntletCrownHowtoVisible = false;
|
||||
hideReadyAvatarsFloat(); /* ซ่อนแถวหน้าตัวละคร ready ตอนเริ่มเกม (กันค้างบนจอระหว่างเล่น) */
|
||||
const st = document.getElementById('gauntlet-crown-howto-status');
|
||||
if (st) {
|
||||
st.classList.add('is-hidden');
|
||||
@@ -11878,8 +11957,10 @@
|
||||
const placedW = hit.placedW != null && Number.isFinite(hit.placedW)
|
||||
? Math.max(0.85, Math.min(3.2, hit.placedW))
|
||||
: stackMini.initialWidthTiles;
|
||||
/* freeze สี (theme) ตอนวาง → block ที่วางแล้วไม่เปลี่ยนสีแม้ turn order/theme resolve เปลี่ยนทีหลัง (ตรงทุกจอ) */
|
||||
stackMini.layers.push({ w: placedW, cx: hit.placedCx, seat: seatLay, heavy: heavyLay, theme: stackSeatColorThemePlay(seatLay) || null });
|
||||
/* freeze สี (theme) ตอนวาง → block ที่วางแล้วไม่เปลี่ยนสีแม้ turn order/theme resolve เปลี่ยนทีหลัง (ตรงทุกจอ)
|
||||
remote drop: ใช้สีจาก host (layerVisualMeta.theme) ก่อน → host/client สีบล็อกตรงกัน */
|
||||
const _layerTheme = (layerVisualMeta && parsePlayLobbyThemeIndex(layerVisualMeta.theme)) || stackSeatColorThemePlay(seatLay) || null;
|
||||
stackMini.layers.push({ w: placedW, cx: hit.placedCx, seat: seatLay, heavy: heavyLay, theme: _layerTheme });
|
||||
const nAfter = stackMini.layers.length;
|
||||
maybeQueueStackTowerBgScrollStepPlay(nAfter);
|
||||
const perfectFull =
|
||||
@@ -12097,6 +12178,7 @@
|
||||
const m = stackMini;
|
||||
const actorSeat = m.pendingDropSeat != null ? m.pendingDropSeat : getStackDropActorSeat();
|
||||
const actorHeavy = !!m.pendingDropHeavy;
|
||||
const actorTheme = m.pendingDropTheme || null; /* remote drop = สีจาก host; local = null (resolve เอง) */
|
||||
const tw = m.widthTiles * tileSize * getStackTowerBlockWorldScalePlay();
|
||||
const swingXW = (m.topCenterX + m.swingAmp * Math.sin(m.phase)) * tileSize;
|
||||
const lh = m.layerWorldH || Math.max(14, tileSize * 0.3);
|
||||
@@ -12140,6 +12222,7 @@
|
||||
releaseAttachWorldY: swingAttachY,
|
||||
actorSeat,
|
||||
actorHeavy,
|
||||
actorTheme,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
@@ -12230,6 +12313,7 @@
|
||||
ensureStackNextDropVisual();
|
||||
stackMini.pendingDropSeat = item.seat || 1;
|
||||
stackMini.pendingDropHeavy = !!item.heavy;
|
||||
stackMini.pendingDropTheme = item.theme || null; /* สีจาก host สำหรับบล็อกนี้ (remote drop) */
|
||||
stackMini.widthTiles = stackBlockWidthTilesForPlay(stackMini.initialWidthTiles, !!item.heavy);
|
||||
return startStackFallAnimation(item.hit, item.actor || { human: true });
|
||||
}
|
||||
@@ -12302,7 +12386,7 @@
|
||||
if (wasHuman) previewAttr = { human: true, id: fallHumanId };
|
||||
else if (fallBotId) previewAttr = { botId: fallBotId };
|
||||
}
|
||||
applyStackHitFromDrop(hit, previewAttr, { seat: stackFall.actorSeat, heavy: stackFall.actorHeavy });
|
||||
applyStackHitFromDrop(hit, previewAttr, { seat: stackFall.actorSeat, heavy: stackFall.actorHeavy, theme: stackFall.actorTheme });
|
||||
if (playBotsEnabled() && (wasHuman || fallBotId)) {
|
||||
stackPreviewLogStackDrop(hit, { human: wasHuman, botId: fallBotId });
|
||||
}
|
||||
@@ -18675,8 +18759,17 @@
|
||||
feetY = lo;
|
||||
headTop = feetY - bh;
|
||||
}
|
||||
vy = 0;
|
||||
onGround = true;
|
||||
/* ตกลงบน "แท่น" ที่วนมาอยู่ในโซนเพดาน (ยืนแล้วหัวมุดเพดาน แถว 0-1) → ห้ามยืนค้างรอแท่น scroll ลงมา
|
||||
ตามคำขอ "ตกลงทันที": ทะลุแท่นนี้ลงไปหาแท่นล่างเลย (ตอนตก = เจอเฉพาะแท่น ไม่ใช่ object เพดานจริง) */
|
||||
if (feetY - bh < ts) {
|
||||
feetY = ts + bh + ts; /* ดันให้พ้นโซนเพดาน (หัว ≈ แถว 2) แล้วตกต่อ */
|
||||
vy = Math.max(vy, ts * 6);
|
||||
onGround = false;
|
||||
headTop = feetY - bh;
|
||||
} else {
|
||||
vy = 0;
|
||||
onGround = true;
|
||||
}
|
||||
} else if (vy < 0 && jumpSurviveOverlapsObjectSolids(md, pxc - bw, headTop, pxc + bw, feetY)) {
|
||||
/* กระโดดขึ้น: แพลตฟอร์มทะลุได้ (one-way) — หยุดเฉพาะกำแพง/ขอบ/เพดานจริง (objects) ไม่ให้หัวติดแท่นบน */
|
||||
const stepUp = ts * 0.035;
|
||||
@@ -18709,7 +18802,8 @@
|
||||
headTop = feetY - bh;
|
||||
if (headTop < ts) {
|
||||
feetY = ts + bh;
|
||||
if (vy < 0) vy = 0;
|
||||
/* ชนเพดานบนสุด → ดันลงทันที (เดิม vy=0 = ลอยนิ่งใต้เพดาน → ตัวละครค้างบนสุด) ให้ตกกลับลงแท่นแล้วไหลตามสายพานต่อ */
|
||||
if (vy < 0) vy = ts * 6;
|
||||
headTop = feetY - bh;
|
||||
}
|
||||
|
||||
@@ -19159,7 +19253,9 @@
|
||||
|
||||
if (Date.now() - jumpSurviveLastEmitT > 80) {
|
||||
jumpSurviveLastEmitT = Date.now();
|
||||
socket.emit('move', { x: me.x, y: me.y, direction: me.direction });
|
||||
/* ส่ง isWalking มาด้วย — ยืนบนแท่นที่เลื่อนลง world Y เปลี่ยนทุกเฟรม → ถ้าให้ปลายทางเดา isWalking จาก delta ตำแหน่ง
|
||||
จะเห็นเรา"ซอยเท้า"ตลอด ทั้งที่ยืนนิ่ง (#42). ส่งสถานะจริง (กดซ้าย/ขวา หรือลอยอยู่) ให้คนอื่น render ตรง */
|
||||
socket.emit('move', { x: me.x, y: me.y, direction: me.direction, jsWalking: !!me.isWalking });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19468,6 +19564,8 @@
|
||||
nick = me.nickname;
|
||||
}
|
||||
me.characterId = myPeer?.characterId || null;
|
||||
/* สี server-authoritative ของเรา → block MG3 ของเราตรงกับ avatar + ตรงทุกจอ (ตั้งแต่เข้าเกม ไม่ต้องรอ lobby-tint-sync) */
|
||||
{ const meTi = parsePlayLobbyThemeIndex(myPeer && myPeer.lobbyColorThemeIndex); if (meTi) me.lobbyThemeIndexSrv = meTi; }
|
||||
meGauntletJumpTicks = typeof myPeer?.gauntletJumpTicks === 'number' ? myPeer.gauntletJumpTicks : 0;
|
||||
meGauntletJumpVis = meGauntletJumpTicks;
|
||||
{
|
||||
@@ -19539,6 +19637,8 @@
|
||||
const spawnOrd = Number.isFinite(joP) ? Math.max(0, Math.floor(joP)) : Math.max(0, plist.findIndex((q) => q && q.id === p.id));
|
||||
others.set(p.id, {
|
||||
x: px, y: py, tx: px, ty: py, direction: p.direction, nickname: p.nickname, characterId: p.characterId,
|
||||
playerKey: (p.playerKey != null ? String(p.playerKey) : null), /* dedup ผี reconnect */
|
||||
lobbyThemeIndexSrv: parsePlayLobbyThemeIndex(p.lobbyColorThemeIndex) || null, /* สี server-authoritative → block MG3 ตรงทุกจอ */
|
||||
spawnJoinOrder: spawnOrd,
|
||||
playTint: playTintFromJoinPeer(p) || playTintForPreviewBotId(p.id) || resolvePlayTintForPeer(p.id, p),
|
||||
gauntletJumpTicks: typeof p.gauntletJumpTicks === 'number' ? p.gauntletJumpTicks : 0,
|
||||
@@ -19959,6 +20059,16 @@
|
||||
ลบ peer เดิม (คนจริง) ที่ "ชื่อ+ตัวละครเดียวกัน" แต่ id ต่าง ก่อนเพิ่มตัวใหม่ (สัญญาณว่าเป็นคนเดิม) */
|
||||
const joinNickDedup = (data.nickname != null ? String(data.nickname).trim() : '');
|
||||
const joinCidDedup = (data.characterId != null ? String(data.characterId) : '');
|
||||
/* dedup ที่เชื่อถือได้สุด: playerKey เดียวกัน (คงที่ต่อ browser) แต่ socket.id ต่าง = คนเดิม reconnect/เปลี่ยนหน้า
|
||||
→ ลบ peer เก่าทิ้งก่อนเพิ่มตัวใหม่ (กัน "เงาซ้ำ" แม้ชื่อ default — nick dedup ข้าม default จึงเคยค้างเป็นผี) */
|
||||
const joinPkDedup = (data.playerKey != null ? String(data.playerKey).trim() : '');
|
||||
if (joinPkDedup) {
|
||||
[...others.keys()].forEach((oid) => {
|
||||
if (isPreviewBotId(oid) || String(oid) === String(data.id)) return;
|
||||
const ex = others.get(oid);
|
||||
if (ex && ex.playerKey && String(ex.playerKey) === joinPkDedup) others.delete(oid);
|
||||
});
|
||||
}
|
||||
/* dedup เฉพาะชื่อ "เฉพาะตัว" (custom) — ข้ามชื่อ default ที่ซ้ำกันได้ ('ผู้เล่น', 'บอท', ว่าง)
|
||||
ไม่งั้นผู้เล่นจริง 2 คนที่ไม่ตั้งชื่อ (default 'ผู้เล่น') จะถูกลบผิด → จำนวนคนหาย/ready ค้าง 4/5 */
|
||||
const isGenericNick = !joinNickDedup || /^ผู้เล่น/.test(joinNickDedup) || joinNickDedup === 'บอท';
|
||||
@@ -19982,6 +20092,8 @@
|
||||
x: px, y: py, tx: px, ty: py,
|
||||
direction: data.direction || 'down', nickname: data.nickname,
|
||||
characterId: data.characterId ?? null,
|
||||
playerKey: joinPkDedup || null, /* ใช้ dedup ผีตอน reconnect (เก็บไว้เทียบรอบหน้า) */
|
||||
lobbyThemeIndexSrv: parsePlayLobbyThemeIndex(data.lobbyColorThemeIndex) || null, /* สี server-authoritative → block MG3 ตรงทุกจอ */
|
||||
spawnJoinOrder: spawnOrdJ,
|
||||
playTint: playTintFromJoinPeer(data) || playTintForPreviewBotId(data.id) || resolvePlayTintForPeer(data.id, data),
|
||||
gauntletJumpTicks: typeof data.gauntletJumpTicks === 'number' ? data.gauntletJumpTicks : 0,
|
||||
@@ -20025,8 +20137,18 @@
|
||||
peers.forEach((p) => {
|
||||
if (!p || p.id == null || isPreviewBotId(p.id)) return;
|
||||
if (myId != null && String(p.id) === String(myId)) return;
|
||||
/* dedup ผี: ลบ peer เก่าที่ playerKey เดียวกันแต่ id ต่าง (reconnect/เปลี่ยนหน้า ทิ้ง id เก่าไว้ = เงาซ้ำ) */
|
||||
const pkSnap = (p.playerKey != null ? String(p.playerKey) : '');
|
||||
if (pkSnap) {
|
||||
[...others.keys()].forEach((oid) => {
|
||||
if (isPreviewBotId(oid) || String(oid) === String(p.id)) return;
|
||||
const ex = others.get(oid);
|
||||
if (ex && ex.playerKey && String(ex.playerKey) === pkSnap) { others.delete(oid); changed = true; }
|
||||
});
|
||||
}
|
||||
const existing = others.get(p.id) || others.get(String(p.id));
|
||||
if (existing) {
|
||||
if (pkSnap && !existing.playerKey) existing.playerKey = pkSnap;
|
||||
/* entry มีอยู่แล้ว — อัปเดต flag สำคัญ (โดยเฉพาะ bannedSpectator) ให้ตรง server
|
||||
กัน host นับผู้ถูกแบนเป็นผู้เล่นที่ต้องรอ Ready → humansReady ไม่ครบ → เริ่มเกมไม่ได้ */
|
||||
const nb = !!p.bannedSpectator;
|
||||
@@ -20043,6 +20165,8 @@
|
||||
direction: p.direction || 'down',
|
||||
nickname: p.nickname,
|
||||
characterId: p.characterId ?? null,
|
||||
playerKey: pkSnap || null, /* dedup ผี reconnect */
|
||||
lobbyThemeIndexSrv: parsePlayLobbyThemeIndex(p.lobbyColorThemeIndex) || null, /* สี server-authoritative → block MG3 ตรงทุกจอ */
|
||||
spawnJoinOrder: Number.isFinite(joP) ? Math.max(0, Math.floor(joP)) : 0,
|
||||
playTint: playTintFromJoinPeer(p) || playTintForPreviewBotId(p.id) || resolvePlayTintForPeer(p.id, p),
|
||||
gauntletJumpTicks: 0, gauntletJumpVis: 0, gauntletScore: 0,
|
||||
@@ -20258,6 +20382,7 @@
|
||||
};
|
||||
stackRemoteDropQueue.push({
|
||||
hit: hit, seat: Number(d.seat) || 1, heavy: !!d.heavy,
|
||||
theme: parsePlayLobbyThemeIndex(d.theme) || null, /* สีจาก host → freeze บนบล็อกที่วาง (ตรงทุกจอ) */
|
||||
/* เก็บ id ของคนวาง (ไม่ใช่แค่ human: true) → คะแนนเข้าคนที่วางจริง ไม่เข้า me ทุกครั้ง */
|
||||
actor: d.isBot ? { botId: String(d.actorId || '') } : { human: true, id: d.actorId != null ? String(d.actorId) : null },
|
||||
});
|
||||
@@ -20287,6 +20412,7 @@
|
||||
const n = stackPreviewTurnOrder.length;
|
||||
stackPreviewTurnIndex = ((Math.floor(Number(d.idx)) % n) + n) % n;
|
||||
}
|
||||
logStackSeatColors('client-recv');
|
||||
});
|
||||
|
||||
/* MG3 stack — host ตัดสินจบรอบ → non-host จบพร้อมกันด้วย outcome เดียวกัน (กัน "จบไม่พร้อมกัน") */
|
||||
@@ -20498,8 +20624,10 @@
|
||||
if (data.spaceShooterEliminated != null) o.spaceShooterEliminated = !!data.spaceShooterEliminated;
|
||||
}
|
||||
/* MG5: การตายของผู้เล่นมนุษย์มาจากเจ้าตัว (broadcast ผ่าน move) → ทุกเครื่องเห็นตรงกัน (เดิมไม่ส่ง = เพื่อน "ไม่ยอมตาย") */
|
||||
if (mapData && mapData.gameType === 'jump_survive' && data.jumpSurviveEliminated != null) {
|
||||
o.jumpSurviveEliminated = !!data.jumpSurviveEliminated;
|
||||
if (mapData && mapData.gameType === 'jump_survive') {
|
||||
if (data.jumpSurviveEliminated != null) o.jumpSurviveEliminated = !!data.jumpSurviveEliminated;
|
||||
/* isWalking จริงจากเจ้าตัว — กัน "ยืนซอยเท้า" ตอนยืนนิ่งบนแท่นเลื่อน (#42) */
|
||||
if (data.jsWalking != null) o.jumpSurviveRemoteWalking = !!data.jsWalking;
|
||||
}
|
||||
if (mapData && mapData.gameType === 'balloon_boss') {
|
||||
if (data.balloonBossScore != null) {
|
||||
@@ -20565,19 +20693,11 @@
|
||||
}
|
||||
socket.on('peer-disconnect-grace', (d) => { if (!d) return; showDisconnectGraceBanner(d.nickname || 'ผู้เล่น', d.seconds || 10); });
|
||||
socket.on('peer-grace-cancel', () => { hideDisconnectGraceBanner(); });
|
||||
socket.on('peer-bot-takeover', (d) => {
|
||||
socket.on('peer-bot-takeover', () => {
|
||||
/* ผู้เล่นที่หลุดให้ "หายไปเลย" จากมินิเกมปัจจุบัน — ถูกลบไปแล้วผ่าน user-left ทันทีที่หลุด
|
||||
ไม่เติมบอทเล่นแทนกลางเกม (เดิม bump detectiveBotSlotCount = บอทโผล่กลางเกม)
|
||||
บอทจะกลับมาแทนตอน LobbyB เอง: server เพิ่ม botSlotCount +1 (สีตามคนออก) → ห้องถัดไปบอทเล่นแทน */
|
||||
hideDisconnectGraceBanner();
|
||||
/* คนที่หลุด "ตาย/ตกรอบไปแล้ว" → ไม่ต้องเติมบอทมาเล่นแทน (เดิมเติมบอท alive = คนตายฟื้นกลับมา) */
|
||||
if (d && d.eliminated) return;
|
||||
/* host เติมบอท 1 ตัวเล่นแทนคนที่หลุด (ถ้ายังไม่เต็ม 6) — ใช้ rebalancePreviewBots เดิม */
|
||||
try {
|
||||
if (isMePlayHost()) {
|
||||
detectiveCaseFillBots = true;
|
||||
if (countPlayHumans() + detectiveBotSlotCount < 6) detectiveBotSlotCount += 1;
|
||||
rebalancePreviewBots();
|
||||
if (typeof broadcastFillBotState === 'function') broadcastFillBotState();
|
||||
}
|
||||
} catch (_e) { /* ignore */ }
|
||||
});
|
||||
|
||||
socket.on('chat', (data) => {
|
||||
@@ -24929,7 +25049,10 @@
|
||||
? ((crownRunStripLive && !o.gauntletEliminated) || (o.gauntletJumpTicks || 0) > 0 || (o.gauntletJumpVis || 0) > 0.08)
|
||||
: (isPreviewBotId(id)
|
||||
? !!o.botIsWalking
|
||||
: !!((o.tx != null && Math.abs((o.tx || o.x) - o.x) > 0.02) || (o.ty != null && Math.abs((o.ty || o.y) - o.y) > 0.02)));
|
||||
: (isJumpSurvive()
|
||||
/* MG5 ผู้เล่นจริงคนอื่น: ใช้ isWalking จริงจากเจ้าตัว (#42) ไม่เดาจาก delta — world Y เลื่อนลงตามแท่นทุกเฟรมแม้ยืนนิ่ง */
|
||||
? !!o.jumpSurviveRemoteWalking
|
||||
: !!((o.tx != null && Math.abs((o.tx || o.x) - o.x) > 0.02) || (o.ty != null && Math.abs((o.ty || o.y) - o.y) > 0.02))));
|
||||
const ot = o.playTint || resolvePlayTintForPeer(id);
|
||||
const faceDirOther = isGauntletFaceRightMapMno9kb07() ? 'right' : o.direction;
|
||||
const botOut = isJumpSurvive() && isPreviewBotId(id) && o.jumpSurviveEliminated;
|
||||
|
||||
@@ -606,6 +606,69 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ===== Loading overlay สีตัวละคร (peer เข้าใหม่ / เปลี่ยนสี) — ดำ 90% + % แล้วหายเมื่อ tint โหลดเสร็จ ===== */
|
||||
var _lcOverlayEl = null;
|
||||
var _lcShowTimer = null;
|
||||
var _lcActive = false;
|
||||
function lcEnsureEl() {
|
||||
if (_lcOverlayEl) return _lcOverlayEl;
|
||||
var el = document.createElement('div');
|
||||
el.id = 'lobby-color-loading';
|
||||
el.style.cssText = 'position:fixed;inset:0;z-index:99999;display:none;flex-direction:column;align-items:center;'
|
||||
+ 'justify-content:center;gap:16px;background:rgba(0,0,0,.9);opacity:0;transition:opacity .16s ease;'
|
||||
+ 'pointer-events:all;font-family:Kanit,system-ui,sans-serif';
|
||||
el.innerHTML =
|
||||
'<div style="color:#9fe8ff;font-size:17px;letter-spacing:.4px">กำลังโหลดสีตัวละคร...</div>'
|
||||
+ '<div id="lcl-pct" style="color:#fff;font-size:38px;font-weight:800;text-shadow:0 0 14px rgba(80,200,255,.6)">0%</div>'
|
||||
+ '<div style="width:260px;height:9px;border-radius:7px;background:rgba(255,255,255,.13);overflow:hidden">'
|
||||
+ '<div id="lcl-bar" style="height:100%;width:0%;background:linear-gradient(90deg,#36e0ff,#b15bff);transition:width .12s ease"></div>'
|
||||
+ '</div>';
|
||||
document.body.appendChild(el);
|
||||
_lcOverlayEl = el;
|
||||
return el;
|
||||
}
|
||||
function lcProgress(done, total) {
|
||||
var pct = total > 0 ? Math.round((done / total) * 100) : 100;
|
||||
pct = Math.max(0, Math.min(100, pct));
|
||||
var p = document.getElementById('lcl-pct');
|
||||
var b = document.getElementById('lcl-bar');
|
||||
if (p) p.textContent = pct + '%';
|
||||
if (b) b.style.width = pct + '%';
|
||||
}
|
||||
function lcShowDeferred() {
|
||||
if (_lcActive) return;
|
||||
_lcActive = true;
|
||||
lcEnsureEl();
|
||||
lcProgress(0, 1);
|
||||
/* เลื่อนแสดง ~140ms — ถ้าโหลดเสร็จเร็ว (cache อยู่แล้ว) จะไม่แฟลชจอ */
|
||||
if (_lcShowTimer) clearTimeout(_lcShowTimer);
|
||||
_lcShowTimer = setTimeout(function () {
|
||||
if (!_lcActive) return;
|
||||
var el = lcEnsureEl();
|
||||
el.style.display = 'flex';
|
||||
void el.offsetWidth;
|
||||
el.style.opacity = '1';
|
||||
}, 140);
|
||||
}
|
||||
function lcHide() {
|
||||
_lcActive = false;
|
||||
if (_lcShowTimer) { clearTimeout(_lcShowTimer); _lcShowTimer = null; }
|
||||
if (!_lcOverlayEl) return;
|
||||
var el = _lcOverlayEl;
|
||||
el.style.opacity = '0';
|
||||
setTimeout(function () { if (!_lcActive) el.style.display = 'none'; }, 180);
|
||||
}
|
||||
/** warm tint ของทุก occupant (me+bot+peer) + overlay % — เรียกตอน peer เข้าใหม่/เปลี่ยนสี กันกระตุกตอน render ครั้งแรก */
|
||||
function lobbyWarmTintsWithOverlay(cb) {
|
||||
lcShowDeferred();
|
||||
var safety = setTimeout(lcHide, 8000); /* กันค้าง */
|
||||
preloadAllLobbyTintedOccupants(function () {
|
||||
clearTimeout(safety);
|
||||
lcHide();
|
||||
if (cb) cb();
|
||||
}, function (d, t) { lcProgress(d, t); });
|
||||
}
|
||||
|
||||
function readLobbyCharImgSize(img) {
|
||||
if (!img) return { iw: 0, ih: 0 };
|
||||
if (img.tagName === 'CANVAS' && img.width > 0 && img.height > 0) {
|
||||
@@ -794,8 +857,11 @@
|
||||
}
|
||||
syncLobbyCaseBots();
|
||||
reapplyLobbyBotThemesFromServer();
|
||||
if (mapData && canvas) drawLobbyMap();
|
||||
if (typeof window.refreshCustomizeColorRow === 'function') window.refreshCustomizeColorRow();
|
||||
/* มีการเปลี่ยนสี → warm tint ทุกคน + overlay % (กันกระตุกตอน render สีใหม่) */
|
||||
lobbyWarmTintsWithOverlay(function () {
|
||||
if (mapData && canvas) drawLobbyMap();
|
||||
if (typeof window.refreshCustomizeColorRow === 'function') window.refreshCustomizeColorRow();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3317,10 +3383,13 @@
|
||||
socket.on('user-joined', (data) => {
|
||||
peers.set(data.id, normalizeLobbyPeerFromServer(data, mapData));
|
||||
applyPeerThemeFromServer(peers.get(data.id), data.lobbyColorThemeIndex, data.lobbySkinToneIndex, function () {
|
||||
updatePlayersHud();
|
||||
renderPeers();
|
||||
redrawLobbyMap();
|
||||
if (typeof window.refreshCustomizeColorRow === 'function') window.refreshCustomizeColorRow();
|
||||
/* คนเข้าใหม่ → warm tint ทุกคน + overlay % (กันกระตุกตอน render ตัวใหม่ครั้งแรก) */
|
||||
lobbyWarmTintsWithOverlay(function () {
|
||||
updatePlayersHud();
|
||||
renderPeers();
|
||||
redrawLobbyMap();
|
||||
if (typeof window.refreshCustomizeColorRow === 'function') window.refreshCustomizeColorRow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5412,7 +5412,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.006250415"></script>
|
||||
<script src="js/play.js?v=0.006260111"></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)
|
||||
|
||||
@@ -1648,7 +1648,7 @@
|
||||
<script src="js/version.js?v=0.0122"></script>
|
||||
<script src="js/customize-popup.js?v=40" data-customize-triggers="" data-customize-asset-base="img/03-5-Customize"></script>
|
||||
<script src="js/achievements.js?v=0.002" data-asset-base="img/03-6-Profile"></script>
|
||||
<script src="js/room-lobby.js?v=0.0323"></script>
|
||||
<script src="js/room-lobby.js?v=0.0324"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+125
-10
@@ -80,22 +80,34 @@ function pickLobbySkinIndexForSlot(slotIndex) {
|
||||
return (Math.abs(Math.floor(s)) % LOBBY_SKIN_COUNT) + 1;
|
||||
}
|
||||
|
||||
/** จัดสี bot ตามลำดับช่อง — ไม่ซ้ำกับผู้เล่น */
|
||||
/** จัดสี bot ตามลำดับช่อง — ไม่ซ้ำกับผู้เล่น
|
||||
taken = สีของ "คนจริง" เท่านั้น แล้วสะสมสีบอทที่จัดไปแล้ว (ไม่รวม pin ของ slot ตัวเอง)
|
||||
→ pin เดิมที่ valid + ไม่ชนคน/บอทอื่น = คงเดิม (ไม่เปลี่ยนสีมั่วทุก sync); ชนเมื่อไหร่ค่อยเลือกใหม่
|
||||
แก้ 2 อย่าง: (ก) เดิม getTakenLobbyThemeIndices รวม pin ของ slot ตัวเอง → re-pick เสมอ = สีบอท churn ทุก sync
|
||||
(ข) "เงาสะท้อนของ user" = บอทสีตรงคน (เช่น bot ทดแทนคนที่ออก pin สีคนนั้นไว้) พอคนนั้นกลับมา → บอท re-pick พ้นสีคน */
|
||||
function syncLobbyBotThemeSlots(space) {
|
||||
if (!space) return [];
|
||||
const botCount = effectiveBotSlotCount(space);
|
||||
if (!space.lobbyBotThemeBySlot || typeof space.lobbyBotThemeBySlot !== 'object') {
|
||||
space.lobbyBotThemeBySlot = {};
|
||||
}
|
||||
const taken = getTakenLobbyThemeIndices(space);
|
||||
const assigned = new Set();
|
||||
space.peers.forEach((p) => {
|
||||
const ti = parseInt(p.lobbyColorThemeIndex, 10);
|
||||
if (ti >= 1 && ti <= LOBBY_THEME_COUNT) assigned.add(ti);
|
||||
});
|
||||
const pickFree = () => {
|
||||
for (let t = 1; t <= LOBBY_THEME_COUNT; t++) { if (!assigned.has(t)) return t; }
|
||||
return (assigned.size % LOBBY_THEME_COUNT) + 1;
|
||||
};
|
||||
const out = [];
|
||||
for (let i = 0; i < botCount; i++) {
|
||||
let slot = space.lobbyBotThemeBySlot[i];
|
||||
const slot = space.lobbyBotThemeBySlot[i];
|
||||
let themeIndex = parseInt(slot && slot.themeIndex, 10);
|
||||
if (!(themeIndex >= 1 && themeIndex <= LOBBY_THEME_COUNT) || taken.has(themeIndex)) {
|
||||
themeIndex = pickFreeLobbyThemeIndex(space);
|
||||
if (!(themeIndex >= 1 && themeIndex <= LOBBY_THEME_COUNT) || assigned.has(themeIndex)) {
|
||||
themeIndex = pickFree();
|
||||
}
|
||||
taken.add(themeIndex);
|
||||
assigned.add(themeIndex);
|
||||
const skinToneIndex = (() => {
|
||||
const si = parseInt(slot && slot.skinToneIndex, 10);
|
||||
return (si >= 1 && si <= LOBBY_SKIN_COUNT) ? si : pickLobbySkinIndexForSlot(i);
|
||||
@@ -126,6 +138,78 @@ function emitLobbyTintSync(spaceId, space) {
|
||||
});
|
||||
}
|
||||
|
||||
/* ===== Game Log (รวม 7 มินิเกม + LobbyA/B) → /var/www/html/gamelog/data/log.jsonl อ่านที่ /gamelog ===== */
|
||||
const GAMELOG_DIR = path.join(__dirname, '..', 'gamelog', 'data');
|
||||
const GAMELOG_FILE = path.join(GAMELOG_DIR, 'log.jsonl');
|
||||
try { fs.mkdirSync(GAMELOG_DIR, { recursive: true }); } catch (eMk) { /* ignore */ }
|
||||
function gameLogCategoryOf(space) {
|
||||
if (!space) return 'อื่นๆ';
|
||||
try {
|
||||
const md = (space.mapId && maps.get(space.mapId)) || space.mapData;
|
||||
const gt = md && md.gameType;
|
||||
const m = { quiz: 'MG1', gauntlet: 'MG2', stack: 'MG3', quiz_carry: 'MG4', jump_survive: 'MG5', space_shooter: 'MG6', balloon_boss: 'MG7' };
|
||||
if (gt && m[gt]) return m[gt];
|
||||
if (serverMapIsPostCaseLobbyB(space)) return 'LobbyB';
|
||||
return 'LobbyA';
|
||||
} catch (e) { return 'อื่นๆ'; }
|
||||
}
|
||||
function glog(spaceId, space, ev, detail, who) {
|
||||
try {
|
||||
const rec = {
|
||||
t: Date.now(),
|
||||
cat: gameLogCategoryOf(space),
|
||||
room: String((space && space.spaceName) || spaceId || '').slice(0, 64),
|
||||
who: String(who || '').slice(0, 48),
|
||||
ev: String(ev || '').slice(0, 48),
|
||||
detail: detail == null ? '' : String(detail).slice(0, 500),
|
||||
src: 'server',
|
||||
};
|
||||
fs.appendFile(GAMELOG_FILE, JSON.stringify(rec) + '\n', () => {});
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
/** เก็บกวาด peer ซ้ำ (ghost / "เงาสะท้อนของ user") ออกจาก space.peers ทั้งก้อน — กันกรณี dedup ต่อ-join พลาด/race
|
||||
จน peer คนเดิมค้าง 2 ตัว → snapshot ส่งไปทุกจอ = เห็นตัวซ้ำ + MG3 คิวตามี seat ผี = เกมค้าง.
|
||||
เทียบ playerKey ก่อน (เชื่อถือสุด ข้าม reconnect) แล้วชื่อ custom; เก็บตัวที่อยู่ท้าย Map (เพิ่ง join/active ล่าสุด) */
|
||||
function pruneDuplicateSpacePeers(spaceId, space) {
|
||||
if (!space || !space.peers || space.peers.size < 2) return false;
|
||||
const validKey = (p) => (p && typeof p.playerKey === 'string' && /^[a-zA-Z0-9_-]{8,128}$/.test(p.playerKey)) ? p.playerKey : '';
|
||||
const isCustom = (nn) => nn && nn !== 'ผู้เล่น' && nn.indexOf('ผู้เล่น') !== 0 && nn.indexOf('บอท') !== 0;
|
||||
const lastByKey = new Map();
|
||||
const lastByNickNoKey = new Map(); /* ชื่อ → pid เฉพาะ peer ที่ "ไม่มี playerKey" (ก้ำกึ่งว่าเป็น ghost) */
|
||||
space.peers.forEach((p, pid) => {
|
||||
if (!p) return;
|
||||
const pk = validKey(p);
|
||||
if (pk) { lastByKey.set(pk, pid); return; } /* มี key → ไม่เข้า nickname dedup */
|
||||
const nn = normalizeLobbyNickname(p.nickname);
|
||||
if (isCustom(nn)) lastByNickNoKey.set(nn, pid);
|
||||
});
|
||||
const remove = [];
|
||||
space.peers.forEach((p, pid) => {
|
||||
if (!p) return;
|
||||
const pk = validKey(p);
|
||||
if (pk) {
|
||||
/* มี playerKey → ลบเฉพาะตัวที่ key เดียวกันแต่ไม่ใช่ตัวล่าสุด (= ghost reconnect จริง)
|
||||
⚠️ ไม่ลบด้วยชื่อ: 2 คนตั้งชื่อเหมือนกัน (เช่น "MONE") = คนละ playerKey = คนละคน ต้องเก็บทั้งคู่ */
|
||||
if (lastByKey.get(pk) !== pid) remove.push(pid);
|
||||
return;
|
||||
}
|
||||
/* ไม่มี playerKey → dedup ชื่อ custom ได้ (เทียบเฉพาะกลุ่มไม่มี key ด้วยกัน) */
|
||||
const nn = normalizeLobbyNickname(p.nickname);
|
||||
if (isCustom(nn) && lastByNickNoKey.get(nn) !== pid) remove.push(pid);
|
||||
});
|
||||
if (!remove.length) return false;
|
||||
remove.forEach((pid) => {
|
||||
const gp = space.peers.get(pid);
|
||||
space.peers.delete(pid);
|
||||
if (space.quizCarryLobbyReady) delete space.quizCarryLobbyReady[pid];
|
||||
if (space.gauntletCrownLobbyReady) delete space.gauntletCrownLobbyReady[pid];
|
||||
io.to(spaceId).emit('user-left', { id: pid });
|
||||
glog(spaceId, space, 'ghost-prune', 'ลบตัวซ้ำ id=' + String(pid).slice(0, 6), (gp && gp.nickname) || '');
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/** จำนวน Bot จาก Create Room — ถ้าไม่เคยบันทึก ใช้ 6 − maxPlayers (เช่น 3 คน → 3 Bot) */
|
||||
function effectiveBotSlotCount(space) {
|
||||
let n = parseInt(space && space.botSlotCount, 10);
|
||||
@@ -3601,6 +3685,7 @@ function beginDetectiveSuspectMinigame(sid, space, cardEntry, selectedIndex) {
|
||||
space.quizSession = null;
|
||||
space.mapId = mapId;
|
||||
space.mapData = md;
|
||||
glog(sid, space, 'game-start', 'เริ่มมินิเกม: ' + ((md && md.name) || mapId) + ' (การ์ด ' + ((selectedIndex != null ? selectedIndex + 1 : '?')) + ')');
|
||||
/* สุ่มไอคอนคำถามพิเศษสำหรับรอบนี้ (1 ใน 3) — ส่งจริงผ่าน joinCb เมื่อ client เข้า play.html */
|
||||
maybeSpawnSpecialQuizForRun(space);
|
||||
/* ไอคอนยังไม่โผล่ตอนนี้ — รอ client ส่ง 'special-quiz-arm' เมื่อเกม "live จริง" (พ้น howto/นับถอยหลัง) แล้วค่อยหน่วง 2 วิโผล่ */
|
||||
@@ -7662,10 +7747,11 @@ io.on('connection', (socket) => {
|
||||
io.to(spaceId).emit('peer-grace-cancel', { nickname: nickname || 'ผู้เล่น' });
|
||||
}
|
||||
}
|
||||
/* reconnect dedup ชั้น 2: playerKey ไม่ match (เก่าว่าง/คนละค่า — เช่น peer เก่าจาก lobbyB ไม่มี playerKey)
|
||||
→ คนเดิมกลับเข้ามากลายเป็นตัวซ้ำ (ghost) ที่ playerKey block จับไม่ได้ → เห็น "ตัวละครซ้ำ 2 ตัว" บน "ทุกจอรวม host"
|
||||
(fill-bot-state humans cleanup เป็น non-host only → host ไม่เคยลบ ghost). ลบที่ต้นทางด้วย "ชื่อ custom เดียวกัน".
|
||||
ข้ามชื่อ default (ผู้เล่น/บอท/ว่าง) — ผู้เล่นจริง 2 คนที่ไม่ตั้งชื่ออาจชนชื่อ default กันได้ */
|
||||
/* reconnect dedup ชั้น 2: ghost ที่ playerKey block จับไม่ได้ (peer เก่าไม่มี playerKey)
|
||||
ลบด้วย "ชื่อ custom เดียวกัน" — แต่ **เฉพาะ peer เก่าที่ไม่มี valid playerKey** เท่านั้น
|
||||
⚠️ ห้ามลบ peer ที่มี playerKey คนละค่า: 2 คนตั้งชื่อเหมือนกัน (เช่น "MONE") = คนละคน ต้องเก็บทั้งคู่
|
||||
(เดิมลบทุกตัวที่ชื่อตรง → client ที่ชื่อซ้ำ host ถูกลบ = "ไม่ได้เล่นเลย") */
|
||||
const validPkStr = (p) => (p && typeof p.playerKey === 'string' && /^[a-zA-Z0-9_-]{8,128}$/.test(p.playerKey)) ? p.playerKey : '';
|
||||
const newNickNorm = normalizeLobbyNickname(nickname);
|
||||
const newNickIsCustom = newNickNorm && newNickNorm !== 'ผู้เล่น'
|
||||
&& newNickNorm.indexOf('ผู้เล่น') !== 0 && newNickNorm.indexOf('บอท') !== 0;
|
||||
@@ -7673,6 +7759,8 @@ io.on('connection', (socket) => {
|
||||
const ghostIds = [];
|
||||
space.peers.forEach((p, pid) => {
|
||||
if (pid === socket.id || !p) return;
|
||||
const ppk = validPkStr(p);
|
||||
if (ppk && ppk !== joinPlayerKey) return; /* คนละ playerKey = คนละคน (ชื่อซ้ำได้) → ไม่ลบ */
|
||||
if (normalizeLobbyNickname(p.nickname) === newNickNorm) ghostIds.push(pid);
|
||||
});
|
||||
ghostIds.forEach((pid) => {
|
||||
@@ -7730,6 +7818,24 @@ io.on('connection', (socket) => {
|
||||
peer.y = sn.y;
|
||||
}
|
||||
space.peers.set(socket.id, peer);
|
||||
/* เก็บกวาด peer ซ้ำทั้งก้อน (ghost) — เผื่อ dedup ต่อ-join ด้านบนพลาด/race จนคนเดิมค้าง 2 ตัว */
|
||||
pruneDuplicateSpacePeers(spaceId, space);
|
||||
glog(spaceId, space, 'join', 'สี#' + (peer.lobbyColorThemeIndex || '?') + ' · คน ' + space.peers.size + ' + บอท ' + effectiveBotSlotCount(space) + (joinPlayerKey ? '' : ' · NO-KEY'), nickname || 'ผู้เล่น');
|
||||
/* invariant: คน + บอท ≤ 6 — กัน "เงาสะท้อนของ user": คนหลุดแล้ว grace แปลงเป็นบอท (botSlotCount+1, สีตามคนออก)
|
||||
พอคนนั้นกลับเข้ามา (reconnect หลัง grace) จะมีทั้งคนจริง + บอทสีเดียวกัน = ตัวซ้ำ. ตัดบอทส่วนเกินออกให้รวมไม่เกิน 6
|
||||
(มินิเกม cap ที่ 6-humans อยู่แล้ว ~rebalancePreviewBots แต่ LobbyB ไม่ได้ cap → ตัดที่ server ต้นทาง) */
|
||||
{
|
||||
const humansNow = space.peers.size;
|
||||
const botsNow = effectiveBotSlotCount(space);
|
||||
if (humansNow + botsNow > LOBBY_SLOT_TOTAL) {
|
||||
space.botSlotCount = Math.max(0, LOBBY_SLOT_TOTAL - humansNow);
|
||||
}
|
||||
/* re-sync สีบอททุกครั้งที่มีคน (re)join + แจ้ง "ทุกจอ" (รวมคนที่อยู่ในมินิเกม/LobbyB อยู่แล้ว):
|
||||
เคส "เงาสะท้อนของ user" = คนหลุด→บอทใช้สีคนนั้น (pin)→คนนั้นกลับมา. join handler เคลียร์ pin ที่ชนสีคนแล้ว
|
||||
(7704) + syncLobbyBotThemeSlots เลือกสีใหม่ที่ไม่ชนคน แต่เดิม emit เฉพาะตอน total>6 → จออื่นไม่อัปเดต = บอทยังสีเดิม.
|
||||
emit ทุก join → บอทเปลี่ยนสีพ้นจากสี user ทันทีทุกจอ */
|
||||
emitLobbyTintSync(spaceId, space);
|
||||
}
|
||||
/* กลับ LobbyB เฉพาะเมื่อไม่ได้เข้า play ฉากมินิเกมที่กำลังเล่น (เช่น refresh room-lobby) */
|
||||
if (space.detectiveMinigameActive) {
|
||||
const clientMap = playMapId != null ? String(playMapId).trim() : '';
|
||||
@@ -8894,9 +9000,11 @@ io.on('connection', (socket) => {
|
||||
ถ้าเขียนทับ key → host/client สลับกันถาวรหลังเข้า minigame) */
|
||||
if (!space.hostPlayerKey && firstPeer.playerKey) space.hostPlayerKey = firstPeer.playerKey;
|
||||
io.to(sid).emit('host-changed', { hostId: firstPeer.id });
|
||||
glog(sid, space, 'host-change', 'host ใหม่ → ' + ((firstPeer.nickname) || String(firstPeer.id).slice(0, 6)), leftNick);
|
||||
}
|
||||
}
|
||||
io.to(sid).emit('user-left', { id: socket.id });
|
||||
glog(sid, space, 'leave', 'ออกจากห้อง · เหลือคน ' + space.peers.size, leftNick);
|
||||
if (space.qbwSolved) qbwBroadcastScores(sid, space); // Quiz Battle ฉากเดิน — อัปเดตอันดับหลังมีคนออก
|
||||
emitDetectiveLobbyBPresence(sid, space); /* คนออกจาก LobbyB → อัปเดตจำนวนที่พร้อมให้ host */
|
||||
/* กำลังรอกด «เล่นต่อ» (special-quiz) แล้วมีคนออก — เอา ack คนนั้นออกแล้วเช็คใหม่ (กันค้างรอคนที่ไม่อยู่แล้ว) */
|
||||
@@ -8962,6 +9070,7 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
io.to(sid).emit('peer-bot-takeover', { nickname: leftNick, eliminated: leftWasEliminated });
|
||||
glog(sid, sp, 'bot-takeover', 'หลุดเกิน ' + DISCONNECT_GRACE_SEC + 'วิ → บอทแทน' + (leftWasEliminated ? ' (ตกรอบแล้ว ไม่ spawn)' : ' (สีตามคนออก)'), leftNick);
|
||||
}, DISCONNECT_GRACE_SEC * 1000);
|
||||
}
|
||||
}
|
||||
@@ -9259,6 +9368,11 @@ io.on('connection', (socket) => {
|
||||
p.jumpSurviveEliminated = true;
|
||||
moveForceEmit = true;
|
||||
}
|
||||
/* isWalking ของเจ้าตัว → relay ให้คนอื่นเห็น anim วิ่ง (เดิม out ไม่ใส่ jsWalking = client อื่นไม่เคยได้ → anim ไม่ขึ้น);
|
||||
เปลี่ยนสถานะ (เริ่มเดิน/หยุด) → force emit แม้ตำแหน่งไม่เปลี่ยน */
|
||||
const jwIn = !!data.jsWalking;
|
||||
if (jwIn !== !!p.jumpSurviveWalking) moveForceEmit = true;
|
||||
p.jumpSurviveWalking = jwIn;
|
||||
}
|
||||
if (p && md && md.gameType === 'balloon_boss' && data) {
|
||||
const bbDefaultBalloons = balloonBossBalloonsForMap(md);
|
||||
@@ -9333,6 +9447,7 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
if (md && md.gameType === 'jump_survive') {
|
||||
out.jumpSurviveEliminated = !!p.jumpSurviveEliminated;
|
||||
out.jsWalking = !!p.jumpSurviveWalking; /* anim วิ่งให้คนอื่นเห็น */
|
||||
}
|
||||
if (nx !== data.x || ny !== data.y) socket.emit('user-move', out);
|
||||
socket.to(sid).emit('user-move', out);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{"t":1782460590560,"cat":"MG3","room":"hhh","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-3 Tower block (การ์ด 1)","src":"server"}
|
||||
{"t":1782460590711,"cat":"MG3","room":"hhh","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782460590711,"cat":"MG3","room":"hhh","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782460591013,"cat":"MG3","room":"hhh","who":"MONE","ev":"join","detail":"สี#8 · คน 1 + บอท 3","src":"server"}
|
||||
{"t":1782460591315,"cat":"MG3","room":"hhh","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782460717354,"cat":"LobbyB","room":"hhh","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782460717354,"cat":"LobbyB","room":"hhh","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782460718032,"cat":"LobbyB","room":"hhh","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782460719931,"cat":"LobbyB","room":"hhh","who":"MONE","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782460720509,"cat":"LobbyB","room":"hhh","who":"MONE","ev":"join","detail":"สี#8 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782461143015,"cat":"MG3","room":"hhh","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-3 Tower block (การ์ด 1)","src":"server"}
|
||||
{"t":1782461143223,"cat":"MG3","room":"hhh","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782461143223,"cat":"MG3","room":"hhh","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782461143637,"cat":"MG3","room":"hhh","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 3","src":"server"}
|
||||
{"t":1782461143920,"cat":"MG3","room":"hhh","who":"MONE","ev":"join","detail":"สี#8 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782461213243,"cat":"MG3","room":"hhh","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782461213243,"cat":"MG3","room":"hhh","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782461223245,"cat":"MG3","room":"hhh","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
|
||||
{"t":1782461274423,"cat":"LobbyB","room":"hhh","who":"MONE","ev":"join","detail":"สี#8 · คน 1 + บอท 4","src":"server"}
|
||||
{"t":1782461327954,"cat":"LobbyA","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 3","src":"server"}
|
||||
{"t":1782461351978,"cat":"LobbyA","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 4","src":"server"}
|
||||
{"t":1782461363786,"cat":"LobbyA","room":"aaa","who":"MONE","ev":"join","detail":"สี#8 · คน 2 + บอท 4","src":"server"}
|
||||
{"t":1782461415359,"cat":"LobbyB","room":"aaa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782461415359,"cat":"LobbyB","room":"aaa","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782461425362,"cat":"LobbyB","room":"aaa","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
|
||||
{"t":1782461428827,"cat":"LobbyA","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 1 + บอท 3","src":"server"}
|
||||
{"t":1782461438053,"cat":"LobbyA","room":"aaa","who":"MONE","ev":"join","detail":"สี#8 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782461466584,"cat":"MG3","room":"aaa","who":"","ev":"game-start","detail":"เริ่มมินิเกม: Minigame-3 Tower block (การ์ด 1)","src":"server"}
|
||||
{"t":1782461466705,"cat":"MG3","room":"aaa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782461466705,"cat":"MG3","room":"aaa","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782461467047,"cat":"MG3","room":"aaa","who":"MONE","ev":"join","detail":"สี#8 · คน 1 + บอท 3","src":"server"}
|
||||
{"t":1782461467352,"cat":"MG3","room":"aaa","who":"Q","ev":"join","detail":"สี#7 · คน 2 + บอท 3","src":"server"}
|
||||
{"t":1782461544478,"cat":"MG3","room":"aaa","who":"Q","ev":"host-change","detail":"host ใหม่ → MONE","src":"server"}
|
||||
{"t":1782461544479,"cat":"MG3","room":"aaa","who":"Q","ev":"leave","detail":"ออกจากห้อง · เหลือคน 1","src":"server"}
|
||||
{"t":1782461554486,"cat":"MG3","room":"aaa","who":"Q","ev":"bot-takeover","detail":"หลุดเกิน 10วิ → บอทแทน (สีตามคนออก)","src":"server"}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php /* gamelog viewer — log รวม 7 มินิเกม + LobbyA/B (อ่านจาก log.php) */ ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Game Log · Justice</title>
|
||||
<style>
|
||||
:root { --bg:#0a0e1a; --panel:#121a2e; --line:#23304d; --txt:#cfe0ff; --dim:#7f93bd; --acc:#37e0ff; }
|
||||
* { box-sizing:border-box; }
|
||||
body { margin:0; background:var(--bg); color:var(--txt); font:14px/1.5 "Kanit",system-ui,sans-serif; }
|
||||
header { position:sticky; top:0; z-index:5; background:linear-gradient(180deg,#0d1426,#0a0e1a); border-bottom:1px solid var(--line); padding:10px 14px; }
|
||||
h1 { margin:0 0 8px; font-size:18px; letter-spacing:.5px; color:#fff; }
|
||||
h1 small { color:var(--dim); font-size:12px; font-weight:400; }
|
||||
.tabs { display:flex; flex-wrap:wrap; gap:6px; }
|
||||
.tab { cursor:pointer; padding:5px 11px; border-radius:8px; border:1px solid var(--line); background:#0f1730; color:var(--dim); font-size:13px; user-select:none; }
|
||||
.tab:hover { border-color:var(--acc); color:var(--txt); }
|
||||
.tab.active { background:var(--acc); color:#05101f; border-color:var(--acc); font-weight:700; }
|
||||
.tab .n { opacity:.7; font-size:11px; margin-left:4px; }
|
||||
.bar { display:flex; align-items:center; gap:12px; margin-top:8px; font-size:12px; color:var(--dim); flex-wrap:wrap; }
|
||||
.bar label { cursor:pointer; }
|
||||
.bar input[type=text]{ background:#0f1730; border:1px solid var(--line); color:var(--txt); border-radius:7px; padding:5px 9px; min-width:160px; }
|
||||
.btn { cursor:pointer; padding:5px 11px; border-radius:8px; border:1px solid var(--line); background:#0f1730; color:var(--txt); font-size:12px; }
|
||||
.btn:hover { border-color:var(--acc); }
|
||||
.btn.danger:hover { border-color:#ff5b6e; color:#ff8a97; }
|
||||
#wrap { padding:6px 8px 60px; }
|
||||
table { width:100%; border-collapse:collapse; }
|
||||
th,td { text-align:left; padding:5px 9px; border-bottom:1px solid #182238; vertical-align:top; }
|
||||
th { position:sticky; top:0; color:var(--dim); font-weight:600; font-size:12px; background:var(--bg); }
|
||||
td.t { color:var(--dim); white-space:nowrap; font-variant-numeric:tabular-nums; font-size:12px; }
|
||||
td.cat span { display:inline-block; padding:1px 8px; border-radius:6px; font-size:12px; font-weight:700; }
|
||||
td.room { color:var(--dim); font-size:12px; max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
td.who { color:#bcd; font-size:13px; }
|
||||
td.ev { font-weight:600; }
|
||||
td.detail { color:var(--dim); font-size:12px; word-break:break-word; }
|
||||
tr.src-client td.ev::after { content:" ·c"; color:#6a7da6; font-size:10px; font-weight:400; }
|
||||
.empty { text-align:center; color:var(--dim); padding:40px; }
|
||||
/* cat colors */
|
||||
.c-LobbyA{background:#1d3a5f;color:#9fd4ff} .c-LobbyB{background:#2a2f63;color:#c4c2ff}
|
||||
.c-MG1{background:#7a2540;color:#ffc0d4} .c-MG2{background:#2a5f3a;color:#bfffd0}
|
||||
.c-MG3{background:#5f4a1d;color:#ffe6a0} .c-MG4{background:#1d5f5a;color:#a0fff2}
|
||||
.c-MG5{background:#3a2a5f;color:#d6c0ff} .c-MG6{background:#5f1d3a;color:#ffb0c8}
|
||||
.c-MG7{background:#5f3a1d;color:#ffd0a0} .cother{background:#2a3550;color:#9fb0d0}
|
||||
.ev-leave td.ev, .ev-disconnect td.ev{color:#ff8a97} .ev-join td.ev{color:#7ee787}
|
||||
.ev-bot-takeover td.ev,.ev-ghost-prune td.ev{color:#ffd166} .ev-error td.ev{color:#ff5b6e}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>🎮 Game Log <small>— 7 มินิเกม + LobbyA / LobbyB · srv1361159</small></h1>
|
||||
<div class="tabs" id="tabs"></div>
|
||||
<div class="bar">
|
||||
<label><input type="checkbox" id="auto" checked> auto-refresh 3s</label>
|
||||
<input type="text" id="search" placeholder="ค้นหา (ชื่อ/ห้อง/event/detail)">
|
||||
<span id="status"></span>
|
||||
<span style="flex:1"></span>
|
||||
<button class="btn" id="reload">รีโหลด</button>
|
||||
<button class="btn danger" id="clear">ล้าง log</button>
|
||||
</div>
|
||||
</header>
|
||||
<div id="wrap"><div class="empty">กำลังโหลด...</div></div>
|
||||
|
||||
<script>
|
||||
var CATS = ['ALL','LobbyA','LobbyB','MG1','MG2','MG3','MG4','MG5','MG6','MG7','อื่นๆ'];
|
||||
var curCat = 'ALL';
|
||||
var rows = [];
|
||||
var counts = {};
|
||||
|
||||
function catClass(c){ var m={'LobbyA':'c-LobbyA','LobbyB':'c-LobbyB','MG1':'c-MG1','MG2':'c-MG2','MG3':'c-MG3','MG4':'c-MG4','MG5':'c-MG5','MG6':'c-MG6','MG7':'c-MG7'}; return m[c]||'cother'; }
|
||||
function fmtTime(ms){ var d=new Date(ms); function p(n){return (n<10?'0':'')+n;} return p(d.getHours())+':'+p(d.getMinutes())+':'+p(d.getSeconds()); }
|
||||
function esc(s){ return String(s==null?'':s).replace(/[&<>]/g,function(c){return{'&':'&','<':'<','>':'>'}[c];}); }
|
||||
|
||||
function renderTabs(){
|
||||
var t=document.getElementById('tabs'); t.innerHTML='';
|
||||
CATS.forEach(function(c){
|
||||
var d=document.createElement('div'); d.className='tab'+(c===curCat?' active':'');
|
||||
var n=counts[c]!=null?('<span class="n">'+counts[c]+'</span>'):'';
|
||||
d.innerHTML=esc(c)+n;
|
||||
d.onclick=function(){ curCat=c; renderTabs(); render(); load(); };
|
||||
t.appendChild(d);
|
||||
});
|
||||
}
|
||||
function render(){
|
||||
var wrap=document.getElementById('wrap');
|
||||
var q=(document.getElementById('search').value||'').toLowerCase().trim();
|
||||
var list=rows.filter(function(r){
|
||||
if(!q) return true;
|
||||
return ((r.who||'')+' '+(r.room||'')+' '+(r.ev||'')+' '+(r.detail||'')).toLowerCase().indexOf(q)>=0;
|
||||
});
|
||||
if(!list.length){ wrap.innerHTML='<div class="empty">ยังไม่มี log'+(curCat!=='ALL'?(' ใน '+esc(curCat)):'')+'</div>'; return; }
|
||||
var h='<table><thead><tr><th>เวลา</th><th>หมวด</th><th>ห้อง</th><th>ใคร</th><th>event</th><th>รายละเอียด</th></tr></thead><tbody>';
|
||||
list.forEach(function(r){
|
||||
h+='<tr class="src-'+esc(r.src||'')+' ev-'+esc((r.ev||'').replace(/[^a-z-]/gi,''))+'">'+
|
||||
'<td class="t">'+fmtTime(r.t)+'</td>'+
|
||||
'<td class="cat"><span class="'+catClass(r.cat)+'">'+esc(r.cat)+'</span></td>'+
|
||||
'<td class="room" title="'+esc(r.room)+'">'+esc(r.room)+'</td>'+
|
||||
'<td class="who">'+esc(r.who)+'</td>'+
|
||||
'<td class="ev">'+esc(r.ev)+'</td>'+
|
||||
'<td class="detail">'+esc(r.detail)+'</td></tr>';
|
||||
});
|
||||
h+='</tbody></table>';
|
||||
wrap.innerHTML=h;
|
||||
}
|
||||
function load(){
|
||||
fetch('log.php?cat='+encodeURIComponent(curCat)+'&limit=800&_='+Date.now())
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(j){
|
||||
rows = (j&&j.logs)||[];
|
||||
document.getElementById('status').textContent = (rows.length)+' รายการ · '+new Date().toLocaleTimeString('th-TH');
|
||||
render();
|
||||
}).catch(function(){ document.getElementById('status').textContent='โหลดไม่ได้'; });
|
||||
// counts ทุกหมวด (จาก ALL)
|
||||
fetch('log.php?cat=ALL&limit=2000&_c='+Date.now()).then(function(r){return r.json();}).then(function(j){
|
||||
counts={ALL:0}; CATS.forEach(function(c){counts[c]=0;});
|
||||
((j&&j.logs)||[]).forEach(function(r){ counts.ALL++; if(counts[r.cat]!=null) counts[r.cat]++; else counts['อื่นๆ']++; });
|
||||
renderTabs();
|
||||
}).catch(function(){});
|
||||
}
|
||||
document.getElementById('reload').onclick=load;
|
||||
document.getElementById('search').oninput=render;
|
||||
document.getElementById('clear').onclick=function(){
|
||||
if(!confirm('ล้าง log ทั้งหมด?')) return;
|
||||
fetch('log.php?action=clear').then(function(){ load(); });
|
||||
};
|
||||
renderTabs(); load();
|
||||
setInterval(function(){ if(document.getElementById('auto').checked) load(); }, 3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/* gamelog endpoint — POST append (client) / GET fetch (viewer) / GET action=clear
|
||||
เก็บเป็น JSONL ที่ data/log.jsonl (server.js เขียนตรง ๆ ด้วย; ไฟล์เดียวกัน) */
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$dir = __DIR__ . '/data';
|
||||
$file = $dir . '/log.jsonl';
|
||||
if (!is_dir($dir)) { @mkdir($dir, 0777, true); }
|
||||
|
||||
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
|
||||
|
||||
if ($method === 'POST') {
|
||||
$raw = file_get_contents('php://input');
|
||||
$j = json_decode($raw, true);
|
||||
if (!is_array($j)) { echo json_encode(['ok' => false, 'err' => 'bad json']); exit; }
|
||||
$items = (isset($j['items']) && is_array($j['items'])) ? $j['items'] : [$j];
|
||||
$lines = '';
|
||||
foreach ($items as $it) {
|
||||
if (!is_array($it)) continue;
|
||||
$rec = [
|
||||
't' => isset($it['t']) ? (int)$it['t'] : (int)(microtime(true) * 1000),
|
||||
'cat' => mb_substr((string)(isset($it['cat']) ? $it['cat'] : 'อื่นๆ'), 0, 24),
|
||||
'room' => mb_substr((string)(isset($it['room']) ? $it['room'] : ''), 0, 64),
|
||||
'who' => mb_substr((string)(isset($it['who']) ? $it['who'] : ''), 0, 48),
|
||||
'ev' => mb_substr((string)(isset($it['ev']) ? $it['ev'] : ''), 0, 48),
|
||||
'detail' => mb_substr((string)(isset($it['detail']) ? $it['detail'] : ''), 0, 500),
|
||||
'src' => mb_substr((string)(isset($it['src']) ? $it['src'] : 'client'), 0, 12),
|
||||
];
|
||||
$lines .= json_encode($rec, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
}
|
||||
if ($lines !== '') @file_put_contents($file, $lines, FILE_APPEND | LOCK_EX);
|
||||
/* ring trim กันไฟล์ใหญ่เกิน */
|
||||
if (@filesize($file) > 4000000) {
|
||||
$all = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if ($all && count($all) > 5000) {
|
||||
$all = array_slice($all, -5000);
|
||||
@file_put_contents($file, implode("\n", $all) . "\n", LOCK_EX);
|
||||
}
|
||||
}
|
||||
echo json_encode(['ok' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'clear') {
|
||||
@file_put_contents($file, '', LOCK_EX);
|
||||
echo json_encode(['ok' => true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* GET: คืน log (กรองตาม cat) ล่าสุดก่อน */
|
||||
$cat = isset($_GET['cat']) ? $_GET['cat'] : '';
|
||||
$limit = isset($_GET['limit']) ? min(3000, max(1, (int)$_GET['limit'])) : 800;
|
||||
$out = [];
|
||||
if (is_file($file)) {
|
||||
$all = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if ($all) {
|
||||
for ($i = count($all) - 1; $i >= 0 && count($out) < $limit; $i--) {
|
||||
$r = json_decode($all[$i], true);
|
||||
if (!is_array($r)) continue;
|
||||
if ($cat && $cat !== 'ALL' && (!isset($r['cat']) || $r['cat'] !== $cat)) continue;
|
||||
$out[] = $r;
|
||||
}
|
||||
}
|
||||
}
|
||||
echo json_encode(['ok' => true, 'logs' => $out, 'count' => count($out)]);
|
||||
@@ -165,9 +165,9 @@
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ตัวละครผู้เล่นเร็วเกินไป",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"updated": "2026-06-25",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 18,
|
||||
@@ -414,9 +414,9 @@
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ท่ากระโดดแปลก ยืนซอยเท้าทำไม",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "anim ไม่ sync: ยืนบนแท่นที่เลื่อนลง world Y เปลี่ยนทุกเฟรม → ปลายทางเดา isWalking จาก delta = เห็น ซอยเท้า ตลอด. แก้: ส่ง isWalking จริงใน move (jsWalking) + ปลายทางใช้ค่านั้น (play.js ~19162/20501/24930). v0.006260100",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
@@ -424,9 +424,9 @@
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ขั้นบันไดมันถี่ไป บางทีกระโดดแล้วเบิ้ลขั้นเลย ปรับให้ระยะห่างแต่ละขั้นมันเท่ากัน",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "ขั้นถี่/ไม่เท่า → เบิ้ลขั้น+ติดรอแท่ง. แก้ layout mnptfts2: ระยะเท่ากันทุกขั้น (gap 3 แถว, rows 2/5/8/11/14/17, zigzag กว้าง6) ในโซนปลอดภัย (เลี่ยง hazard แถว 0/18/19). reach apex 4.5 > 3 = ถึงทุกขั้น (ไม่ติดรอ #43b) + ขั้นถัดไป-อีกขั้น 6 แถว > reach = กระโดดข้ามไม่ได้ (ไม่เบิ้ล). seam รอยต่อ wrap ห่าง 5 แถว 1 จุด/รอบ (h=20 หาร 3 ไม่ลง). restart service หลังแก้แมป | รอบ2: (ก) แก้ลายแท่นบิด — เดิม width เท่ากัน(6)ทำให้ภาพถูกบีบ → ตั้ง width = ขนาดจริงของรูป (เล็ก=var3 7ช่อง/212px, กลาง=var2 13ช่อง/389px) วาดได้สัดส่วนจริงไม่บิด. (ข) ติดบนสุด = seam รอยต่อ wrap 5แถว >ระยะกระโดด(apex4.5) → bump jumpSurviveJumpHeightMult 1.5→1.7 (apex 5.1) ให้ข้าม seam ได้ แต่ข้าม2ขั้น(6แถว)ยังไม่ได้. var1(ใหญ่ 46ช่อง)ใหญ่เกินแมป35 ใช้ไม่ได้ไม่บิด → ใช้ เล็ก/กลาง",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
@@ -484,9 +484,9 @@
|
||||
"name": "กรณีผู้เล่นหลุดออกจากเกม",
|
||||
"detail": "ถ้ามีคนหลุด ให้กลายเป้นบอทแทน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว 2026-06-25: คนออกระหว่าง LobbyB/เปลี่ยนหน้า กลายเป็นบอทสีตามคนออก (ดูบั๊ก #73)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
@@ -718,5 +718,25 @@
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0313): เดิม overlay เป็น display:flex default จึงเด้งทุกครั้งที่โหลดหน้า. เพิ่มตรวจสิทธิ์ผ่าน navigator.permissions.query(microphone) + จำสถานะใน localStorage (justiceMicPromptHandled). แสดงเฉพาะครั้งแรกที่ยังไม่เคยตัดสินใจ (permission=prompt และยังไม่เคยกดอนุญาต/ปิด); ถ้า granted แล้ว/เคยจัดการแล้ว → ซ่อนทันที (มี inline script กันแฟลชก่อน paint). ปุ่ม 🎤 ขอสิทธิ์ไมค์ ในห้องยังใช้ขอใหม่ได้. ไฟล์: room-lobby.html (inline gate) + room-lobby.js (gateMicPermissionOverlay, hideMicPermissionOverlay).",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 73,
|
||||
"name": "คนออก LobbyB / ช่วงเปลี่ยนหน้า → ไม่เติมบอท + ค้างรอผู้เล่น เริ่มเกมต่อไม่ได้",
|
||||
"detail": "คนหลุดระหว่างนั่งอยู่ LobbyB หรือช่วงเปลี่ยนหน้า minigame→LobbyB เดิมไม่ถูกเติมบอทแทน + ยังถูกนับใน roster คดี → host กดเริ่มสืบสวนรอบต่อไปไม่ได้ ค้าง รอผู้เล่น (x/y) ตลอดไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "ขยาย grace+bot-takeover gate ให้ครอบ LobbyB/คดีล็อก ไม่ใช่แค่ minigame active (server.js ~8921: inCaseFlowForBot = detectiveMinigameActive || serverMapIsPostCaseLobbyB || (joinLocked && caseParticipantNicknames.size>0)). คนออก→หลัง grace 10วิ ลบจาก roster + เติมบอทสีตามคนออก + presence ใหม่ → allHere=true เล่นต่อได้. + ส่ง botSlotCount ใน lobby-tint-sync (server.js ~121) + client อัปเดต lobbyBotSlotCount ก่อน render (room-lobby.js ~768) ให้บอทโผล่ทันทีไม่ต้อง re-join. v0.0323",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 74,
|
||||
"name": "แถวหน้าตัวละคร (ready ใต้ปุ่ม HOW TO) ค้างบนจอระหว่างเล่นเกม",
|
||||
"detail": "หลังกด HOW TO/Ready แล้วเริ่มเกม กล่องแถว avatar คนที่กด ready (ใต้ปุ่ม) ไม่ถูกซ่อน ค้างอยู่กลางจอล่างทับฉากเกม (เห็นชัดใน MG3 stack) — กระทบ MG1/3/5/6",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "กล่องลอย #gch-ready-avatars-float ถูกซ่อนเฉพาะ MG2 (dismissGauntletCrownHowtoOverlayPlay) + MG4 (quiz_carry) เท่านั้น. begin*CountdownThenRun ของ MG1/3/5/6 (stack/jump/space/quiz) ตั้ง gauntletCrownHowtoVisible=false แต่ไม่เรียก hideReadyAvatarsFloat() → กล่องค้าง display:flex ทั้งเกม (ไม่มี driver เรียกใหม่ตอน live). แก้: เพิ่ม hideReadyAvatarsFloat() ในทั้ง 4 ฟังก์ชันเริ่มเกม (play.js). v0.006260101",
|
||||
"cat": "อื่นๆ"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,722 @@
|
||||
[
|
||||
{
|
||||
"n": 1,
|
||||
"name": "แต่งตัวไม่ได้",
|
||||
"detail": "แต่งตัวยังไม่ได้ มีพี่คิวทำได้คนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 2,
|
||||
"name": "ตัวละครไม่แสดง",
|
||||
"detail": "หน้าเลือกเกม Quiz battle ภาพตัวละครไม่แสดง",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 3,
|
||||
"name": "ระบบ join เกมมีปัญหา",
|
||||
"detail": "เมื่อเข้ามินิเกม : บางคนเล่นเกมกะบอท บางคนเล่นกะเพื่อน แต่ละคนเข้ามินิเกมแล้วเจอกันบ้าง ไม่เจอกันบ้าง",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 4,
|
||||
"name": "ระบบ Host มีปัญหา - 1",
|
||||
"detail": "เป็น host ในเกม ตอนที่เลือกผู้ต้องสงสัย รอบที่ 2 ระบบบอกว่าให้รอ host ไม่สามารถเลือกการ์ดและกดเริ่มสืบสวนได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 5,
|
||||
"name": "ระบบ Host มีปัญหา - 2",
|
||||
"detail": "เมื่อกดออกจากเกม Host เปลี่ยนจากพี่ต้อมเป็นพี่คิวอัติโนมัติ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 6,
|
||||
"name": "ระบบ Host มีปัญหา - 3",
|
||||
"detail": "เมื่อเข้ามินิเกม ทุกคนเห็นตัวเองเป็น host หมดเลย แต่กดเริ่มเกมไม่ได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 7,
|
||||
"name": "ระบบสีตัวละคร ยังใช้ไม่ได้",
|
||||
"detail": "สีตัวละครเปลี่ยนไปเรื่อย ไม่รุ้ว่าเราสีอะไรแน่ งงไปหมด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 8,
|
||||
"name": "หน้าเข้าร่วมเกมส์",
|
||||
"detail": "ห้องไหนที่เริ่มเกมส์ไปแล้ว เมื่อกดเข้าร่วมแสดง popup alert error ปรับดีไซต์ให้สวยงามกว่านี้",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 9,
|
||||
"name": "เกมวิ่งขึ้นบันได ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ ใครตกจอตาย แต่ถ้าขึ้นไปอยู่บนสุดแล้วจะกระโดดไม่ไปต่อเฉยๆ แต่ไม่ตาย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 10,
|
||||
"name": "เกมวิ่งขึ้นบันได",
|
||||
"detail": "เวลาหมดแล้ว ให้เล่นรอบ 2 auto / รอบ 3 มี popup ให้กดเล่นต่อ",
|
||||
"prio": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 11,
|
||||
"name": "เกมยิงลูกโป่ง",
|
||||
"detail": "ฉากเกมยิงลูกโป่ง (MG7 Mega Virus) เล็กไป — ต้องการให้ภาพพื้นหลัง (BG) เต็มจอ แต่การเดิน (movement) และพื้นที่เล่น (area) ต่างๆ คงเดิม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250401): BG ของ MG7 เป็นภาพ 16:9 (1920x1080) แต่โค้ดเดิม map ภาพเข้า world rect 4:3 (640x480) ทำให้ภาพถูกบีบ + เหลือขอบดำซ้าย-ขวา = ฉากดูเล็ก. เปลี่ยนการวาด BG ของ balloon_boss เป็นแบบ cover เต็มจอ (รักษาสัดส่วน 16:9, crop ส่วนเกิน, จัดกึ่งกลาง) วาดแยกจาก world layer → กล้อง/movement/area ของเกมคงเดิมทุกอย่าง เปลี่ยนเฉพาะพื้นหลังให้เต็มจอ. ไฟล์: Game/public/js/play.js (draw loop, สาขา isBalloonBoss cover-fill).",
|
||||
"cat": "Minigame 7"
|
||||
},
|
||||
{
|
||||
"n": 12,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ เล่นทีละคน เรียงตามคิวไปเรื่อย โดยที่ blog ตามสีผู้เล่นด้วย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 13,
|
||||
"name": "เข้าร่วมห้องไม่ได้",
|
||||
"detail": "สร้างห้องส่วนตัวเเล้ว เเต่เพื่อนจอยเข้าไปไม่ได้ ทั้งที่กรอกข้อมูลครบ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 14,
|
||||
"name": "Room ID",
|
||||
"detail": "เวลาจอยห้องให้กรอกเเค่ ID กับรหัสที่สร้าง",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 15,
|
||||
"name": "จอยห้องคนอื่น",
|
||||
"detail": "ในกรณีที่จอยห้องคนอื่น ไปหน้าเข้าร่วม เเละกดปุ่มเข้าร่วมไปเเล้ว จากนั้นกดปุ่ม exit จะพาไปหน้าสร้างห้อง สิ่งที่ถูกต้อง มันควรมาหน้า ห้องเข้าร่วมเกม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 16,
|
||||
"name": "grid กับ ฉาก ไม่ตรงกัน",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ฉากกับกริดไม่ตรงกัน ทำให้เดินทะลุ หรือเดินติดในจุดที่ไม่ควรติด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 17,
|
||||
"name": "ตัวละครวิ่งเร็วเกินไป",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ตัวละครผู้เล่นเร็วเกินไป",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 18,
|
||||
"name": "Oauth Social log-in error",
|
||||
"detail": "ไม่สามารถ log-inด้วย facebook / Google ได้ path: /oauth-public.php",
|
||||
"prio": "ต่ำ",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 19,
|
||||
"name": "หน้า login",
|
||||
"detail": "นโยบายความเป็นส่วนตัวกดไม่ได้",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 20,
|
||||
"name": "แสดงเลข version ตรงมุมจอ",
|
||||
"detail": "แสดงเลข version ตรงมุมจอขวาบนหรือขวาล่างก็ได้",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — badge มุมขวาล่าง อ่าน ?v= อัตโนมัติ",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 21,
|
||||
"name": "ลดขนาดตัวละคร",
|
||||
"detail": "ลดขนาดตัวละครในห้อง lobby ลงอีก 2เท่า",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 22,
|
||||
"name": "เปลี่ยน ui popup ตอนพูดบนหัว",
|
||||
"detail": "แก้ png seq บนหัวใหม่ เป็นแบบอื่น",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 23,
|
||||
"name": "ชื่่อตัวละครทับลำโพง/ทับ bubble",
|
||||
"detail": "ใน lobby ขยับชื่อลงมาอย่าให้ทับ",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 24,
|
||||
"name": "UI แต่งตัว : การแจ้งเตือนสีตัวละครถูกใช้แล้ว",
|
||||
"detail": "ตอนเลือกสีให้มีคำแจ้งเตือน used",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 25,
|
||||
"name": "เพิ่ม ข้อความแจ้งเตือนอัพเดทกลางจอ",
|
||||
"detail": "ถ้ามีการอัพเดทซีน อัพเดทสีตัวละครให้มีป็อปอัพเด้งมากลางหน้าจอ ว่ามีการอัพเดทและให้รอโหลด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 26,
|
||||
"name": "เอาเงาตัวละครออก 1 อัน",
|
||||
"detail": "ตอนนี้ตัวละครมีเงา 2 ชั้น เอาออกไป 1 ชั้น",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — ปิด default-shadow overlay เหลือเงาในสไปรต์",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 27,
|
||||
"name": "optimize ตัวละครให้เล็กลงอีก มันโหลดช้า",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 28,
|
||||
"name": "ปุ่ม start ใน lobby",
|
||||
"detail": "ทำให้ดูโต้ตอบหน่อย เช่นพอเดินเข้าไปใกล้ให้มัน glow ขึ้นมา",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 29,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "สี blog กับคนเล่นยังไม่สัมพันกัน ต้องเรียงตามสี และตามคิว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 30,
|
||||
"name": "เกมต่อตึก บั๊กคนที่ 5",
|
||||
"detail": "คนที่ 5 ไม่สามารถเล่นได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 31,
|
||||
"name": "เอา mock การ์ดผู้ต้องหาเก่าออกไป",
|
||||
"detail": "มันแว่บๆมา",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส) — ซ่อนรูปจนรูปคดีจริงโหลด + spinner",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 32,
|
||||
"name": "เอา popup ขออนุญาตใช้ไมค์ออกไปเลย น่ารำคาญ",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 33,
|
||||
"name": "หน้า howto เปลี่ยนสีปุ่ม ready ด้วยตอน กดready แล้ว",
|
||||
"detail": "เปลี่ยนเป้นสีเขียวพร้อมเครื่องหมายถูกตามดีไซน์",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 34,
|
||||
"name": "เกม ใช่ หรือไม่ : ตอนจบบั๊ค",
|
||||
"detail": "ตอนสรุปคะแนน ยังนับเวลาอยุ่ และขึ้น gameover ทับหน้าสรุปคะแนน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG1 ใช่/ไม่: capture mg1AnyScore ก่อนล้าง (gameover) + guard quiz-phase หลังจบ (timer)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 35,
|
||||
"name": "เกมวิ่ง : กดปุ่มไม่กระโดด",
|
||||
"detail": "ปุ่ม jump กดแล้วไม่กระโดด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 36,
|
||||
"name": "เกมวิ่ง : ตอนจบบั๊ค วิ่งเลยฉาก",
|
||||
"detail": "ตอนจบบั๊ค วิ่งเลยฉาก (ในบางรอบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG2 วิ่งเลยฉาก: walk-to-finish ค่อยๆเดินเข้าเส้น + clamp ไม่เลยเส้น",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 37,
|
||||
"name": "popup รับหลักฐาน ทับไมค์",
|
||||
"detail": "กดไมค์ไม่ได้ ตอนรับหลักฐาน ไปดูเรื่องเลเยอร์อีกที",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 38,
|
||||
"name": "หน้าส่งหลักฐาน ปุ่มส่งหลักฐานหลุดจอ (เครื่องของเนส)",
|
||||
"detail": "เป้นเครื่องเนสคนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 39,
|
||||
"name": "ห้องส่งหลักฐาน : เมื่อมีคนหลุด 1 คน จะไปต่อไม่ได้เลย",
|
||||
"detail": "",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — testimony: disconnect ลบ submit/ready + client ใช้ totalPlayers authoritative",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 40,
|
||||
"name": "จำนวนคน ให้เล่นได้ตั้งแต่ 4-6 คน",
|
||||
"detail": "กรณีเลือกผู้เล่นตั้งแต่ 4 คนขึ้นไปไม่ต้องมี bot",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 41,
|
||||
"name": "เกม หยิบแผ่นป้าย",
|
||||
"detail": "ต้องเล่นให้ครบ 10 ข้อก่อน ค่อยจบเกม (ตอนนี้เล่นถูก 1 ข้อ จบเกมเลย)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG4 หยิบป้าย: ใช้ map จริง (currentPlayMapId) เปิด mission flow + default 10 ข้อ",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 42,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ท่ากระโดดแปลก ยืนซอยเท้าทำไม",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 43,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ขั้นบันไดมันถี่ไป บางทีกระโดดแล้วเบิ้ลขั้นเลย ปรับให้ระยะห่างแต่ละขั้นมันเท่ากัน",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 44,
|
||||
"name": "เกมกระโดด",
|
||||
"detail": "อีเวนท์พิเศษ (ทนาย/เทพความรู้) หายไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG5 อีเวนต์พิเศษ: เพิ่ม branch jump_survive วาง icon บน platform",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 45,
|
||||
"name": "เกมไม่รองรับ 4K",
|
||||
"detail": "หน้ารับหลักฐาน (หลังจากเล่นเกมจบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 46,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "สีตัวละครยังไม่ตรงกับที่เลือกมา",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6 สี: ยาน Rocket-1..8 ตาม lobby theme ของผู้เล่น",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 47,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "อุกาบาดให้พุ่งเร็วกว่านี้ และลูกเล็กกว่านี้เกมง่ายไป",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6: อุกาบาตเร็วขึ้น (+~50%) + เล็กลง (48->34/11->8)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 48,
|
||||
"name": "เพิ่ม popup เมื่อเกมค้าง เล่นต้่อไม่ได้ ให้มีปุ่มกดออกไปหน้าแรกสุด",
|
||||
"detail": "สาเหตุเกิดจากมีคนหลุดระหว่างรอ host กด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 49,
|
||||
"name": "กรณีผู้เล่นหลุดออกจากเกม",
|
||||
"detail": "ถ้ามีคนหลุด ให้กลายเป้นบอทแทน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 50,
|
||||
"name": "ผู้เล่น 6 คน: บางเกมเล่นได้แค่ 5 (คนที่ 6 หายจากสรุป)",
|
||||
"detail": "สรุปคะแนนฝั่ง client slice(0,5) ตัดคนที่ 6 — แก้เป็น slice(0,6) ทุกมินิเกม (MG1/3/4/5/6/7)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 51,
|
||||
"name": "ผู้เล่น 6 คน: คะแนนตอนจบไม่ตรงกันแต่ละจอ",
|
||||
"detail": "MG1 merge คะแนน server / MG6,7 merge คะแนน host -> ครบ 6 ตรงกัน (MG3 ยัง local)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 52,
|
||||
"name": "หน้าไต่สวน/สรุปหลักฐาน: กรอบปลิ้นตอน 6 คน",
|
||||
"detail": "member row grid repeat(5)->repeat(6) ใน testimony-overlay.css (คนที่6 ตกแถว2)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 53,
|
||||
"name": "เพิ่มหน้าโหลด (loading) ตอนเข้าเกม",
|
||||
"detail": "overlay spinner กันเห็นจอเปล่า/asset แว่บ ซ่อนเมื่อ draw เฟรมแรก + safety 8s",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 54,
|
||||
"name": "host โหลดนานมากตอนเข้าเกม",
|
||||
"detail": "optimize asset/char (เกี่ยว row 28) — loading overlay ช่วยซ่อน แต่ยังโหลดช้าจริง",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "loading overlay บังอาการแล้ว · root = canvas tint ภาพตัวละครใหญ่ (source ไม่อยู่ใน repo) → ต้อง optimize ภาพแยก",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 55,
|
||||
"name": "หลังออกจาก LobbyA ไม่ให้ใครเข้า join",
|
||||
"detail": "server มี joinLocked (set ตอน LobbyA->case ~8656, check ~7585 whitelist nickname) — ต้อง repro ว่าใครเข้าได้ยังไง (room list ยังโชว์? ชื่อซ้ำ whitelist?)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 — กรองห้อง joinLocked ออกจาก /api/spaces list (ห้องเริ่มคดีแล้วหาย) + server reject อยู่แล้ว",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 56,
|
||||
"name": "ตอนกลับ LobbyB โชว์รูปแรกก่อนแล้วค่อยถูก",
|
||||
"detail": "เหมือน #1 การ์ด — ต้องใส่ loading state ตอน return LobbyB ด้วย (มี room-loading-overlay อยู่แต่ซ่อนเร็วไป)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0308 — การ์ดมี spinner (#1) + room-loading overlay ใส่ % + รอ char preload เสร็จก่อนซ่อน + safety 12s",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 57,
|
||||
"name": "เล่นมินิเกมเกิน 3 ครั้งต่อผู้ต้องสงสัย (+การ์ดพิเศษโผล่รอบที่4)",
|
||||
"detail": "server cap suspect-pick-start (max หลักฐาน>=3 reject) + award cap 3 + client canInvestigateSuspect<3 (เดิม stub return true). UI messaging มีอยู่แล้ว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 58,
|
||||
"name": "ปุ่ม \"เล่นเกมต่อ\" ไม่รู้ว่าค้างหรือรอคนอื่น",
|
||||
"detail": "text \"รอผู้เล่นอื่น (X/Y)\" ถูก set อยู่แล้วแต่ CSS font-size:0 ซ่อน → เพิ่ม .is-waiting CSS โชว์ข้อความ+spinner แทนรูปปุ่ม",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 59,
|
||||
"name": "loading ไม่คลุม scene transition (เห็นฉากเก่าก่อนเปลี่ยน)",
|
||||
"detail": "LobbyA->B (in-page swap) โชว์ room-loading ระหว่าง fetch+swap · LobbyB->เกม โชว์ overlay ก่อน location.href · play.html มี overlay รับช่วง",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 60,
|
||||
"name": "ทำหน้า testcase บนเซิร์ฟเวอร์",
|
||||
"detail": "srv1361159.hstgr.cloud/testcase — bug list + filter/search/นับสถานะ generate จาก xlsx",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 61,
|
||||
"name": "MG3 สี block ไม่ตรง user (เกม+admin)",
|
||||
"detail": "PNG Block-Code-1..8 deploy ครบแล้ว · ต้อง wire สี block = lobby theme ของผู้วาง (seat=theme refactor, ต้องเทส 2 จอ) + admin 8-slot config",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250200 — block สีตาม theme ผู้เล่นที่ seat (Block-Code-1..8) ตอนวาด ไม่แตะ turn/sync",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 62,
|
||||
"name": "MG1/MG4 user/bot ติดกัน (เดินไม่ผ่าน)",
|
||||
"detail": "เพิ่ม anti-stuck: ดันชนเพื่อน → เขย่า (shake) + ปลดล็อกเดินผ่านกันชั่วคราว คงกำแพง/area/quiz-lock; ทั้งผู้เล่นและบอท",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250100 (รอเทส)",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 63,
|
||||
"name": "LobbyA มือถือ กดเดินไม่ได้",
|
||||
"detail": "lobby canvas มีแค่ dblclick (ไม่มี touch) → มือถือแตะเดินไม่ได้. แก้: เพิ่ม tap-to-walk (เฉพาะ touch, PC ยัง dblclick) + DPI-safe coord + touch-action:none + zoom-out default บนจอ<600px (PC ย่อส่วน)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.js?v=0.0310 (รอเทสมือถือ) · PC ไม่กระทบ",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 64,
|
||||
"name": "มือถือ: ปุ่มแชท AI ใหญ่ไป + ปุ่ม F (host) ไม่มี",
|
||||
"detail": "AI chat collapsed btn 213x201px → ใหญ่บนมือถือ. + การกด F (interact: host เลือกระดับ/คดี, ห้องแต่งตัว) ไม่มีบนมือถือ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (css 0.0176/js 0.0311): media<600px ย่อปุ่ม AI (92px) + ปุ่มลอย \"โต้ตอบ(F)\" บน touch-primary device (dispatch synthetic KeyF). PC ไม่เห็น",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 65,
|
||||
"name": "อีเวนต์พิเศษ (ทนาย/เทพ) = 1 ใน 3 (ตามดีไซน์)",
|
||||
"detail": "ดีไซน์: 3 เกม/LobbyB เล่นได้เกมละ 3 ครั้ง · อีเวนต์พิเศษ 1/3 ต่อครั้ง (1 ใบ) · Test Mode บังคับ. revert การแก้ \">1 ต่อรอบ\" กลับเป็น 1 ใบ/ครั้งที่ติด",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ revert 2026-06-25 — กลับเป็น 1/3 (1 ใบ/ครั้งที่ติด) ตามดีไซน์ · ยืนยันโครงสร้าง 3เกม×3ครั้ง + disruptor toggle (>4คน/Test Mode) ถูกต้อง",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 66,
|
||||
"name": "host หลุด → ย้าย host ให้คนอยู่นานสุด",
|
||||
"detail": "pickNextHostPeer เลือก spawnJoinOrder ต่ำสุด (เสถียรข้าม reconnect) แทน Map-first + host จริง reclaim ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server) — Part 1 host migration",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 67,
|
||||
"name": "คนหลุดกลางเกม → 10 วิ reconnect + บอทเล่นแทน",
|
||||
"detail": "peer-disconnect-grace แบนเนอร์นับ 10 วิ · กลับทัน=cancel เล่นต่อ · ครบเวลา=peer-bot-takeover host เติมบอท 1 ตัว (rebalance, cap≤6). บอทไม่สืบทอดตำแหน่ง/คะแนน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server+play.js v0.006250400) — Part 2 แบบ A · seamless(host คุม entity เดิม)=ค้าง(เสี่ยง)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 68,
|
||||
"name": "ห้องแต่งตัว (customize) มือถือแนวนอนใช้ไม่ได้",
|
||||
"detail": "@media max-width:960px stack แนวตั้ง → panel สูงตาม aspect ล้นจอแนวนอน. แก้: เพิ่ม media (landscape + max-height:540px) ใช้เลย์เอาต์แนวนอน + ขนาดอิงความสูงจอ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 customize-popup.css?v=43 (Main-Lobby + room-lobby) — รอเทสมือถือแนวนอน",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 69,
|
||||
"name": "UI ห้องเกม (LobbyA) มือถือแนวนอน — ย่อให้พอดี",
|
||||
"detail": "room-lobby ไม่มี landscape media query → chat panel(576x525), ปุ่ม voice/แว่น/ถ้วย(80px), AI chat ล้น/ใหญ่บนแนวนอน. เพิ่ม @media(landscape+max-height:540px) ย่อตามความสูงจอ (อิง Main-Lobby pattern)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.css?v=0.0177 — chat/BR buttons/AI/ready/exit · overlay หลักฐาน-อันดับ (inline/JS) ยังต้องทำแยก",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 70,
|
||||
"name": "การ์ดรางวัลหลักฐาน มือถือใหญ่เกิน กดปุ่มไม่ได้",
|
||||
"detail": "หน้าโชว์การ์ดรางวัล \"คุณได้รับหลักฐาน!\" (หลังจบมินิเกมสืบสวน) บนมือถือการ์ดใหญ่มากจนเนื้อหา+ปุ่ม \"เก็บเข้าแฟ้ม\" หลุดล่างจอ กดปิดไม่ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250402): ทำ overlay เป็น scroll container + inner margin:auto (กัน flexbox clip ด้านบนเวลาเนื้อหาสูงเกินจอ) + คุมขนาดการ์ด/หัวข้อ/ปุ่มด้วย clamp ตาม viewport (การ์ด max-width min(72vw,360px) + max-height 52vh, ปุ่ม font/padding clamp). มือถือแนวตั้งเลื่อนถึงปุ่มได้เสมอ. ไฟล์: Game/public/js/play.js (showInSceneEvidenceReveal).",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 71,
|
||||
"name": "หน้าผลชนะ (โพเดียม) ตัวละครลอย ไม่ยืนบนพื้น",
|
||||
"detail": "หน้า \"ยินดีด้วย ทีมนักสืบ ชนะ!\" ตัวละครผู้ชนะลอยอยู่กลางอากาศ ไม่ยืนบนพื้นเวที",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0312): พื้นหลัง win-bg เดิม center/cover ทำให้พื้นเวทีถูก crop เลื่อน + podium ตั้ง bottom:9% ตัวละครเลยลอย. แก้เป็น (1) ปักพื้นหลังไว้ขอบล่าง background-position:center bottom -> พื้นเวทีติดขอบล่างจอเสมอทุกสัดส่วน (2) ลด podium bottom 9% -> 2% ให้ตัวละครยืนบนพื้น. ไฟล์: Game/public/js/room-lobby.js (injectFinalResultStyle, #trial-final-podium).",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 72,
|
||||
"name": "ป๊อปอัปขอสิทธิ์ไมค์ เด้งทุกครั้งที่เข้า Lobby",
|
||||
"detail": "หน้าขอสิทธิ์ไมโครโฟน (mic-permission-overlay) เด้งขึ้นทุกครั้งที่เข้า LobbyA/LobbyB น่ารำคาญ ทั้งที่เบราว์เซอร์จำสิทธิ์ให้แล้ว",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0313): เดิม overlay เป็น display:flex default จึงเด้งทุกครั้งที่โหลดหน้า. เพิ่มตรวจสิทธิ์ผ่าน navigator.permissions.query(microphone) + จำสถานะใน localStorage (justiceMicPromptHandled). แสดงเฉพาะครั้งแรกที่ยังไม่เคยตัดสินใจ (permission=prompt และยังไม่เคยกดอนุญาต/ปิด); ถ้า granted แล้ว/เคยจัดการแล้ว → ซ่อนทันที (มี inline script กันแฟลชก่อน paint). ปุ่ม 🎤 ขอสิทธิ์ไมค์ ในห้องยังใช้ขอใหม่ได้. ไฟล์: room-lobby.html (inline gate) + room-lobby.js (gateMicPermissionOverlay, hideMicPermissionOverlay).",
|
||||
"cat": "LobbyA"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,732 @@
|
||||
[
|
||||
{
|
||||
"n": 1,
|
||||
"name": "แต่งตัวไม่ได้",
|
||||
"detail": "แต่งตัวยังไม่ได้ มีพี่คิวทำได้คนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 2,
|
||||
"name": "ตัวละครไม่แสดง",
|
||||
"detail": "หน้าเลือกเกม Quiz battle ภาพตัวละครไม่แสดง",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 3,
|
||||
"name": "ระบบ join เกมมีปัญหา",
|
||||
"detail": "เมื่อเข้ามินิเกม : บางคนเล่นเกมกะบอท บางคนเล่นกะเพื่อน แต่ละคนเข้ามินิเกมแล้วเจอกันบ้าง ไม่เจอกันบ้าง",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 4,
|
||||
"name": "ระบบ Host มีปัญหา - 1",
|
||||
"detail": "เป็น host ในเกม ตอนที่เลือกผู้ต้องสงสัย รอบที่ 2 ระบบบอกว่าให้รอ host ไม่สามารถเลือกการ์ดและกดเริ่มสืบสวนได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 5,
|
||||
"name": "ระบบ Host มีปัญหา - 2",
|
||||
"detail": "เมื่อกดออกจากเกม Host เปลี่ยนจากพี่ต้อมเป็นพี่คิวอัติโนมัติ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 6,
|
||||
"name": "ระบบ Host มีปัญหา - 3",
|
||||
"detail": "เมื่อเข้ามินิเกม ทุกคนเห็นตัวเองเป็น host หมดเลย แต่กดเริ่มเกมไม่ได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 7,
|
||||
"name": "ระบบสีตัวละคร ยังใช้ไม่ได้",
|
||||
"detail": "สีตัวละครเปลี่ยนไปเรื่อย ไม่รุ้ว่าเราสีอะไรแน่ งงไปหมด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 8,
|
||||
"name": "หน้าเข้าร่วมเกมส์",
|
||||
"detail": "ห้องไหนที่เริ่มเกมส์ไปแล้ว เมื่อกดเข้าร่วมแสดง popup alert error ปรับดีไซต์ให้สวยงามกว่านี้",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 9,
|
||||
"name": "เกมวิ่งขึ้นบันได ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ ใครตกจอตาย แต่ถ้าขึ้นไปอยู่บนสุดแล้วจะกระโดดไม่ไปต่อเฉยๆ แต่ไม่ตาย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 10,
|
||||
"name": "เกมวิ่งขึ้นบันได",
|
||||
"detail": "เวลาหมดแล้ว ให้เล่นรอบ 2 auto \/ รอบ 3 มี popup ให้กดเล่นต่อ",
|
||||
"prio": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 11,
|
||||
"name": "เกมยิงลูกโป่ง",
|
||||
"detail": "ฉากเกมยิงลูกโป่ง (MG7 Mega Virus) เล็กไป — ต้องการให้ภาพพื้นหลัง (BG) เต็มจอ แต่การเดิน (movement) และพื้นที่เล่น (area) ต่างๆ คงเดิม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250401): BG ของ MG7 เป็นภาพ 16:9 (1920x1080) แต่โค้ดเดิม map ภาพเข้า world rect 4:3 (640x480) ทำให้ภาพถูกบีบ + เหลือขอบดำซ้าย-ขวา = ฉากดูเล็ก. เปลี่ยนการวาด BG ของ balloon_boss เป็นแบบ cover เต็มจอ (รักษาสัดส่วน 16:9, crop ส่วนเกิน, จัดกึ่งกลาง) วาดแยกจาก world layer → กล้อง\/movement\/area ของเกมคงเดิมทุกอย่าง เปลี่ยนเฉพาะพื้นหลังให้เต็มจอ. ไฟล์: Game\/public\/js\/play.js (draw loop, สาขา isBalloonBoss cover-fill).",
|
||||
"cat": "Minigame 7"
|
||||
},
|
||||
{
|
||||
"n": 12,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ เล่นทีละคน เรียงตามคิวไปเรื่อย โดยที่ blog ตามสีผู้เล่นด้วย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 13,
|
||||
"name": "เข้าร่วมห้องไม่ได้",
|
||||
"detail": "สร้างห้องส่วนตัวเเล้ว เเต่เพื่อนจอยเข้าไปไม่ได้ ทั้งที่กรอกข้อมูลครบ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 14,
|
||||
"name": "Room ID",
|
||||
"detail": "เวลาจอยห้องให้กรอกเเค่ ID กับรหัสที่สร้าง",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 15,
|
||||
"name": "จอยห้องคนอื่น",
|
||||
"detail": "ในกรณีที่จอยห้องคนอื่น ไปหน้าเข้าร่วม เเละกดปุ่มเข้าร่วมไปเเล้ว จากนั้นกดปุ่ม exit จะพาไปหน้าสร้างห้อง สิ่งที่ถูกต้อง มันควรมาหน้า ห้องเข้าร่วมเกม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 16,
|
||||
"name": "grid กับ ฉาก ไม่ตรงกัน",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ฉากกับกริดไม่ตรงกัน ทำให้เดินทะลุ หรือเดินติดในจุดที่ไม่ควรติด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 17,
|
||||
"name": "ตัวละครวิ่งเร็วเกินไป",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ตัวละครผู้เล่นเร็วเกินไป",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 18,
|
||||
"name": "Oauth Social log-in error",
|
||||
"detail": "ไม่สามารถ log-inด้วย facebook \/ Google ได้ path: \/oauth-public.php",
|
||||
"prio": "ต่ำ",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 19,
|
||||
"name": "หน้า login",
|
||||
"detail": "นโยบายความเป็นส่วนตัวกดไม่ได้",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 20,
|
||||
"name": "แสดงเลข version ตรงมุมจอ",
|
||||
"detail": "แสดงเลข version ตรงมุมจอขวาบนหรือขวาล่างก็ได้",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — badge มุมขวาล่าง อ่าน ?v= อัตโนมัติ",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 21,
|
||||
"name": "ลดขนาดตัวละคร",
|
||||
"detail": "ลดขนาดตัวละครในห้อง lobby ลงอีก 2เท่า",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 22,
|
||||
"name": "เปลี่ยน ui popup ตอนพูดบนหัว",
|
||||
"detail": "แก้ png seq บนหัวใหม่ เป็นแบบอื่น",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 23,
|
||||
"name": "ชื่่อตัวละครทับลำโพง\/ทับ bubble",
|
||||
"detail": "ใน lobby ขยับชื่อลงมาอย่าให้ทับ",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 24,
|
||||
"name": "UI แต่งตัว : การแจ้งเตือนสีตัวละครถูกใช้แล้ว",
|
||||
"detail": "ตอนเลือกสีให้มีคำแจ้งเตือน used",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 25,
|
||||
"name": "เพิ่ม ข้อความแจ้งเตือนอัพเดทกลางจอ",
|
||||
"detail": "ถ้ามีการอัพเดทซีน อัพเดทสีตัวละครให้มีป็อปอัพเด้งมากลางหน้าจอ ว่ามีการอัพเดทและให้รอโหลด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 26,
|
||||
"name": "เอาเงาตัวละครออก 1 อัน",
|
||||
"detail": "ตอนนี้ตัวละครมีเงา 2 ชั้น เอาออกไป 1 ชั้น",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — ปิด default-shadow overlay เหลือเงาในสไปรต์",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 27,
|
||||
"name": "optimize ตัวละครให้เล็กลงอีก มันโหลดช้า",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 28,
|
||||
"name": "ปุ่ม start ใน lobby",
|
||||
"detail": "ทำให้ดูโต้ตอบหน่อย เช่นพอเดินเข้าไปใกล้ให้มัน glow ขึ้นมา",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 29,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "สี blog กับคนเล่นยังไม่สัมพันกัน ต้องเรียงตามสี และตามคิว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 30,
|
||||
"name": "เกมต่อตึก บั๊กคนที่ 5",
|
||||
"detail": "คนที่ 5 ไม่สามารถเล่นได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 31,
|
||||
"name": "เอา mock การ์ดผู้ต้องหาเก่าออกไป",
|
||||
"detail": "มันแว่บๆมา",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส) — ซ่อนรูปจนรูปคดีจริงโหลด + spinner",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 32,
|
||||
"name": "เอา popup ขออนุญาตใช้ไมค์ออกไปเลย น่ารำคาญ",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 33,
|
||||
"name": "หน้า howto เปลี่ยนสีปุ่ม ready ด้วยตอน กดready แล้ว",
|
||||
"detail": "เปลี่ยนเป้นสีเขียวพร้อมเครื่องหมายถูกตามดีไซน์",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 34,
|
||||
"name": "เกม ใช่ หรือไม่ : ตอนจบบั๊ค",
|
||||
"detail": "ตอนสรุปคะแนน ยังนับเวลาอยุ่ และขึ้น gameover ทับหน้าสรุปคะแนน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG1 ใช่\/ไม่: capture mg1AnyScore ก่อนล้าง (gameover) + guard quiz-phase หลังจบ (timer)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 35,
|
||||
"name": "เกมวิ่ง : กดปุ่มไม่กระโดด",
|
||||
"detail": "ปุ่ม jump กดแล้วไม่กระโดด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 36,
|
||||
"name": "เกมวิ่ง : ตอนจบบั๊ค วิ่งเลยฉาก",
|
||||
"detail": "ตอนจบบั๊ค วิ่งเลยฉาก (ในบางรอบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG2 วิ่งเลยฉาก: walk-to-finish ค่อยๆเดินเข้าเส้น + clamp ไม่เลยเส้น",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 37,
|
||||
"name": "popup รับหลักฐาน ทับไมค์",
|
||||
"detail": "กดไมค์ไม่ได้ ตอนรับหลักฐาน ไปดูเรื่องเลเยอร์อีกที",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 38,
|
||||
"name": "หน้าส่งหลักฐาน ปุ่มส่งหลักฐานหลุดจอ (เครื่องของเนส)",
|
||||
"detail": "เป้นเครื่องเนสคนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 39,
|
||||
"name": "ห้องส่งหลักฐาน : เมื่อมีคนหลุด 1 คน จะไปต่อไม่ได้เลย",
|
||||
"detail": "",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — testimony: disconnect ลบ submit\/ready + client ใช้ totalPlayers authoritative",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 40,
|
||||
"name": "จำนวนคน ให้เล่นได้ตั้งแต่ 4-6 คน",
|
||||
"detail": "กรณีเลือกผู้เล่นตั้งแต่ 4 คนขึ้นไปไม่ต้องมี bot",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 41,
|
||||
"name": "เกม หยิบแผ่นป้าย",
|
||||
"detail": "ต้องเล่นให้ครบ 10 ข้อก่อน ค่อยจบเกม (ตอนนี้เล่นถูก 1 ข้อ จบเกมเลย)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG4 หยิบป้าย: ใช้ map จริง (currentPlayMapId) เปิด mission flow + default 10 ข้อ",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 42,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ท่ากระโดดแปลก ยืนซอยเท้าทำไม",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 43,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ขั้นบันไดมันถี่ไป บางทีกระโดดแล้วเบิ้ลขั้นเลย ปรับให้ระยะห่างแต่ละขั้นมันเท่ากัน",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 44,
|
||||
"name": "เกมกระโดด",
|
||||
"detail": "อีเวนท์พิเศษ (ทนาย\/เทพความรู้) หายไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG5 อีเวนต์พิเศษ: เพิ่ม branch jump_survive วาง icon บน platform",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 45,
|
||||
"name": "เกมไม่รองรับ 4K",
|
||||
"detail": "หน้ารับหลักฐาน (หลังจากเล่นเกมจบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 46,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "สีตัวละครยังไม่ตรงกับที่เลือกมา",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6 สี: ยาน Rocket-1..8 ตาม lobby theme ของผู้เล่น",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 47,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "อุกาบาดให้พุ่งเร็วกว่านี้ และลูกเล็กกว่านี้เกมง่ายไป",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6: อุกาบาตเร็วขึ้น (+~50%) + เล็กลง (48->34\/11->8)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 48,
|
||||
"name": "เพิ่ม popup เมื่อเกมค้าง เล่นต้่อไม่ได้ ให้มีปุ่มกดออกไปหน้าแรกสุด",
|
||||
"detail": "สาเหตุเกิดจากมีคนหลุดระหว่างรอ host กด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 49,
|
||||
"name": "กรณีผู้เล่นหลุดออกจากเกม",
|
||||
"detail": "ถ้ามีคนหลุด ให้กลายเป้นบอทแทน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว 2026-06-25: คนออกระหว่าง LobbyB\/เปลี่ยนหน้า กลายเป็นบอทสีตามคนออก (ดูบั๊ก #73)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 50,
|
||||
"name": "ผู้เล่น 6 คน: บางเกมเล่นได้แค่ 5 (คนที่ 6 หายจากสรุป)",
|
||||
"detail": "สรุปคะแนนฝั่ง client slice(0,5) ตัดคนที่ 6 — แก้เป็น slice(0,6) ทุกมินิเกม (MG1\/3\/4\/5\/6\/7)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 51,
|
||||
"name": "ผู้เล่น 6 คน: คะแนนตอนจบไม่ตรงกันแต่ละจอ",
|
||||
"detail": "MG1 merge คะแนน server \/ MG6,7 merge คะแนน host -> ครบ 6 ตรงกัน (MG3 ยัง local)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 52,
|
||||
"name": "หน้าไต่สวน\/สรุปหลักฐาน: กรอบปลิ้นตอน 6 คน",
|
||||
"detail": "member row grid repeat(5)->repeat(6) ใน testimony-overlay.css (คนที่6 ตกแถว2)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 53,
|
||||
"name": "เพิ่มหน้าโหลด (loading) ตอนเข้าเกม",
|
||||
"detail": "overlay spinner กันเห็นจอเปล่า\/asset แว่บ ซ่อนเมื่อ draw เฟรมแรก + safety 8s",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 54,
|
||||
"name": "host โหลดนานมากตอนเข้าเกม",
|
||||
"detail": "optimize asset\/char (เกี่ยว row 28) — loading overlay ช่วยซ่อน แต่ยังโหลดช้าจริง",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "loading overlay บังอาการแล้ว · root = canvas tint ภาพตัวละครใหญ่ (source ไม่อยู่ใน repo) → ต้อง optimize ภาพแยก",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 55,
|
||||
"name": "หลังออกจาก LobbyA ไม่ให้ใครเข้า join",
|
||||
"detail": "server มี joinLocked (set ตอน LobbyA->case ~8656, check ~7585 whitelist nickname) — ต้อง repro ว่าใครเข้าได้ยังไง (room list ยังโชว์? ชื่อซ้ำ whitelist?)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 — กรองห้อง joinLocked ออกจาก \/api\/spaces list (ห้องเริ่มคดีแล้วหาย) + server reject อยู่แล้ว",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 56,
|
||||
"name": "ตอนกลับ LobbyB โชว์รูปแรกก่อนแล้วค่อยถูก",
|
||||
"detail": "เหมือน #1 การ์ด — ต้องใส่ loading state ตอน return LobbyB ด้วย (มี room-loading-overlay อยู่แต่ซ่อนเร็วไป)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0308 — การ์ดมี spinner (#1) + room-loading overlay ใส่ % + รอ char preload เสร็จก่อนซ่อน + safety 12s",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 57,
|
||||
"name": "เล่นมินิเกมเกิน 3 ครั้งต่อผู้ต้องสงสัย (+การ์ดพิเศษโผล่รอบที่4)",
|
||||
"detail": "server cap suspect-pick-start (max หลักฐาน>=3 reject) + award cap 3 + client canInvestigateSuspect<3 (เดิม stub return true). UI messaging มีอยู่แล้ว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309\/server (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 58,
|
||||
"name": "ปุ่ม \"เล่นเกมต่อ\" ไม่รู้ว่าค้างหรือรอคนอื่น",
|
||||
"detail": "text \"รอผู้เล่นอื่น (X\/Y)\" ถูก set อยู่แล้วแต่ CSS font-size:0 ซ่อน → เพิ่ม .is-waiting CSS โชว์ข้อความ+spinner แทนรูปปุ่ม",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309\/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 59,
|
||||
"name": "loading ไม่คลุม scene transition (เห็นฉากเก่าก่อนเปลี่ยน)",
|
||||
"detail": "LobbyA->B (in-page swap) โชว์ room-loading ระหว่าง fetch+swap · LobbyB->เกม โชว์ overlay ก่อน location.href · play.html มี overlay รับช่วง",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309\/server (รอเทส)",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 60,
|
||||
"name": "ทำหน้า testcase บนเซิร์ฟเวอร์",
|
||||
"detail": "srv1361159.hstgr.cloud\/testcase — bug list + filter\/search\/นับสถานะ generate จาก xlsx",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309\/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 61,
|
||||
"name": "MG3 สี block ไม่ตรง user (เกม+admin)",
|
||||
"detail": "PNG Block-Code-1..8 deploy ครบแล้ว · ต้อง wire สี block = lobby theme ของผู้วาง (seat=theme refactor, ต้องเทส 2 จอ) + admin 8-slot config",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250200 — block สีตาม theme ผู้เล่นที่ seat (Block-Code-1..8) ตอนวาด ไม่แตะ turn\/sync",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 62,
|
||||
"name": "MG1\/MG4 user\/bot ติดกัน (เดินไม่ผ่าน)",
|
||||
"detail": "เพิ่ม anti-stuck: ดันชนเพื่อน → เขย่า (shake) + ปลดล็อกเดินผ่านกันชั่วคราว คงกำแพง\/area\/quiz-lock; ทั้งผู้เล่นและบอท",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250100 (รอเทส)",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 63,
|
||||
"name": "LobbyA มือถือ กดเดินไม่ได้",
|
||||
"detail": "lobby canvas มีแค่ dblclick (ไม่มี touch) → มือถือแตะเดินไม่ได้. แก้: เพิ่ม tap-to-walk (เฉพาะ touch, PC ยัง dblclick) + DPI-safe coord + touch-action:none + zoom-out default บนจอ<600px (PC ย่อส่วน)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.js?v=0.0310 (รอเทสมือถือ) · PC ไม่กระทบ",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 64,
|
||||
"name": "มือถือ: ปุ่มแชท AI ใหญ่ไป + ปุ่ม F (host) ไม่มี",
|
||||
"detail": "AI chat collapsed btn 213x201px → ใหญ่บนมือถือ. + การกด F (interact: host เลือกระดับ\/คดี, ห้องแต่งตัว) ไม่มีบนมือถือ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (css 0.0176\/js 0.0311): media<600px ย่อปุ่ม AI (92px) + ปุ่มลอย \"โต้ตอบ(F)\" บน touch-primary device (dispatch synthetic KeyF). PC ไม่เห็น",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 65,
|
||||
"name": "อีเวนต์พิเศษ (ทนาย\/เทพ) = 1 ใน 3 (ตามดีไซน์)",
|
||||
"detail": "ดีไซน์: 3 เกม\/LobbyB เล่นได้เกมละ 3 ครั้ง · อีเวนต์พิเศษ 1\/3 ต่อครั้ง (1 ใบ) · Test Mode บังคับ. revert การแก้ \">1 ต่อรอบ\" กลับเป็น 1 ใบ\/ครั้งที่ติด",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ revert 2026-06-25 — กลับเป็น 1\/3 (1 ใบ\/ครั้งที่ติด) ตามดีไซน์ · ยืนยันโครงสร้าง 3เกม×3ครั้ง + disruptor toggle (>4คน\/Test Mode) ถูกต้อง",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 66,
|
||||
"name": "host หลุด → ย้าย host ให้คนอยู่นานสุด",
|
||||
"detail": "pickNextHostPeer เลือก spawnJoinOrder ต่ำสุด (เสถียรข้าม reconnect) แทน Map-first + host จริง reclaim ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server) — Part 1 host migration",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 67,
|
||||
"name": "คนหลุดกลางเกม → 10 วิ reconnect + บอทเล่นแทน",
|
||||
"detail": "peer-disconnect-grace แบนเนอร์นับ 10 วิ · กลับทัน=cancel เล่นต่อ · ครบเวลา=peer-bot-takeover host เติมบอท 1 ตัว (rebalance, cap≤6). บอทไม่สืบทอดตำแหน่ง\/คะแนน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server+play.js v0.006250400) — Part 2 แบบ A · seamless(host คุม entity เดิม)=ค้าง(เสี่ยง)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 68,
|
||||
"name": "ห้องแต่งตัว (customize) มือถือแนวนอนใช้ไม่ได้",
|
||||
"detail": "@media max-width:960px stack แนวตั้ง → panel สูงตาม aspect ล้นจอแนวนอน. แก้: เพิ่ม media (landscape + max-height:540px) ใช้เลย์เอาต์แนวนอน + ขนาดอิงความสูงจอ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 customize-popup.css?v=43 (Main-Lobby + room-lobby) — รอเทสมือถือแนวนอน",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 69,
|
||||
"name": "UI ห้องเกม (LobbyA) มือถือแนวนอน — ย่อให้พอดี",
|
||||
"detail": "room-lobby ไม่มี landscape media query → chat panel(576x525), ปุ่ม voice\/แว่น\/ถ้วย(80px), AI chat ล้น\/ใหญ่บนแนวนอน. เพิ่ม @media(landscape+max-height:540px) ย่อตามความสูงจอ (อิง Main-Lobby pattern)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.css?v=0.0177 — chat\/BR buttons\/AI\/ready\/exit · overlay หลักฐาน-อันดับ (inline\/JS) ยังต้องทำแยก",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 70,
|
||||
"name": "การ์ดรางวัลหลักฐาน มือถือใหญ่เกิน กดปุ่มไม่ได้",
|
||||
"detail": "หน้าโชว์การ์ดรางวัล \"คุณได้รับหลักฐาน!\" (หลังจบมินิเกมสืบสวน) บนมือถือการ์ดใหญ่มากจนเนื้อหา+ปุ่ม \"เก็บเข้าแฟ้ม\" หลุดล่างจอ กดปิดไม่ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250402): ทำ overlay เป็น scroll container + inner margin:auto (กัน flexbox clip ด้านบนเวลาเนื้อหาสูงเกินจอ) + คุมขนาดการ์ด\/หัวข้อ\/ปุ่มด้วย clamp ตาม viewport (การ์ด max-width min(72vw,360px) + max-height 52vh, ปุ่ม font\/padding clamp). มือถือแนวตั้งเลื่อนถึงปุ่มได้เสมอ. ไฟล์: Game\/public\/js\/play.js (showInSceneEvidenceReveal).",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 71,
|
||||
"name": "หน้าผลชนะ (โพเดียม) ตัวละครลอย ไม่ยืนบนพื้น",
|
||||
"detail": "หน้า \"ยินดีด้วย ทีมนักสืบ ชนะ!\" ตัวละครผู้ชนะลอยอยู่กลางอากาศ ไม่ยืนบนพื้นเวที",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0312): พื้นหลัง win-bg เดิม center\/cover ทำให้พื้นเวทีถูก crop เลื่อน + podium ตั้ง bottom:9% ตัวละครเลยลอย. แก้เป็น (1) ปักพื้นหลังไว้ขอบล่าง background-position:center bottom -> พื้นเวทีติดขอบล่างจอเสมอทุกสัดส่วน (2) ลด podium bottom 9% -> 2% ให้ตัวละครยืนบนพื้น. ไฟล์: Game\/public\/js\/room-lobby.js (injectFinalResultStyle, #trial-final-podium).",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 72,
|
||||
"name": "ป๊อปอัปขอสิทธิ์ไมค์ เด้งทุกครั้งที่เข้า Lobby",
|
||||
"detail": "หน้าขอสิทธิ์ไมโครโฟน (mic-permission-overlay) เด้งขึ้นทุกครั้งที่เข้า LobbyA\/LobbyB น่ารำคาญ ทั้งที่เบราว์เซอร์จำสิทธิ์ให้แล้ว",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0313): เดิม overlay เป็น display:flex default จึงเด้งทุกครั้งที่โหลดหน้า. เพิ่มตรวจสิทธิ์ผ่าน navigator.permissions.query(microphone) + จำสถานะใน localStorage (justiceMicPromptHandled). แสดงเฉพาะครั้งแรกที่ยังไม่เคยตัดสินใจ (permission=prompt และยังไม่เคยกดอนุญาต\/ปิด); ถ้า granted แล้ว\/เคยจัดการแล้ว → ซ่อนทันที (มี inline script กันแฟลชก่อน paint). ปุ่ม 🎤 ขอสิทธิ์ไมค์ ในห้องยังใช้ขอใหม่ได้. ไฟล์: room-lobby.html (inline gate) + room-lobby.js (gateMicPermissionOverlay, hideMicPermissionOverlay).",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 73,
|
||||
"name": "คนออก LobbyB \/ ช่วงเปลี่ยนหน้า → ไม่เติมบอท + ค้างรอผู้เล่น เริ่มเกมต่อไม่ได้",
|
||||
"detail": "คนหลุดระหว่างนั่งอยู่ LobbyB หรือช่วงเปลี่ยนหน้า minigame→LobbyB เดิมไม่ถูกเติมบอทแทน + ยังถูกนับใน roster คดี → host กดเริ่มสืบสวนรอบต่อไปไม่ได้ ค้าง รอผู้เล่น (x\/y) ตลอดไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "ขยาย grace+bot-takeover gate ให้ครอบ LobbyB\/คดีล็อก ไม่ใช่แค่ minigame active (server.js ~8921: inCaseFlowForBot = detectiveMinigameActive || serverMapIsPostCaseLobbyB || (joinLocked && caseParticipantNicknames.size>0)). คนออก→หลัง grace 10วิ ลบจาก roster + เติมบอทสีตามคนออก + presence ใหม่ → allHere=true เล่นต่อได้. + ส่ง botSlotCount ใน lobby-tint-sync (server.js ~121) + client อัปเดต lobbyBotSlotCount ก่อน render (room-lobby.js ~768) ให้บอทโผล่ทันทีไม่ต้อง re-join. v0.0323",
|
||||
"cat": "ระบบ disconnect"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,742 @@
|
||||
[
|
||||
{
|
||||
"n": 1,
|
||||
"name": "แต่งตัวไม่ได้",
|
||||
"detail": "แต่งตัวยังไม่ได้ มีพี่คิวทำได้คนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 2,
|
||||
"name": "ตัวละครไม่แสดง",
|
||||
"detail": "หน้าเลือกเกม Quiz battle ภาพตัวละครไม่แสดง",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 3,
|
||||
"name": "ระบบ join เกมมีปัญหา",
|
||||
"detail": "เมื่อเข้ามินิเกม : บางคนเล่นเกมกะบอท บางคนเล่นกะเพื่อน แต่ละคนเข้ามินิเกมแล้วเจอกันบ้าง ไม่เจอกันบ้าง",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 4,
|
||||
"name": "ระบบ Host มีปัญหา - 1",
|
||||
"detail": "เป็น host ในเกม ตอนที่เลือกผู้ต้องสงสัย รอบที่ 2 ระบบบอกว่าให้รอ host ไม่สามารถเลือกการ์ดและกดเริ่มสืบสวนได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 5,
|
||||
"name": "ระบบ Host มีปัญหา - 2",
|
||||
"detail": "เมื่อกดออกจากเกม Host เปลี่ยนจากพี่ต้อมเป็นพี่คิวอัติโนมัติ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 6,
|
||||
"name": "ระบบ Host มีปัญหา - 3",
|
||||
"detail": "เมื่อเข้ามินิเกม ทุกคนเห็นตัวเองเป็น host หมดเลย แต่กดเริ่มเกมไม่ได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 7,
|
||||
"name": "ระบบสีตัวละคร ยังใช้ไม่ได้",
|
||||
"detail": "สีตัวละครเปลี่ยนไปเรื่อย ไม่รุ้ว่าเราสีอะไรแน่ งงไปหมด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 8,
|
||||
"name": "หน้าเข้าร่วมเกมส์",
|
||||
"detail": "ห้องไหนที่เริ่มเกมส์ไปแล้ว เมื่อกดเข้าร่วมแสดง popup alert error ปรับดีไซต์ให้สวยงามกว่านี้",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 9,
|
||||
"name": "เกมวิ่งขึ้นบันได ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ ใครตกจอตาย แต่ถ้าขึ้นไปอยู่บนสุดแล้วจะกระโดดไม่ไปต่อเฉยๆ แต่ไม่ตาย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 10,
|
||||
"name": "เกมวิ่งขึ้นบันได",
|
||||
"detail": "เวลาหมดแล้ว ให้เล่นรอบ 2 auto / รอบ 3 มี popup ให้กดเล่นต่อ",
|
||||
"prio": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 11,
|
||||
"name": "เกมยิงลูกโป่ง",
|
||||
"detail": "ฉากเกมยิงลูกโป่ง (MG7 Mega Virus) เล็กไป — ต้องการให้ภาพพื้นหลัง (BG) เต็มจอ แต่การเดิน (movement) และพื้นที่เล่น (area) ต่างๆ คงเดิม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250401): BG ของ MG7 เป็นภาพ 16:9 (1920x1080) แต่โค้ดเดิม map ภาพเข้า world rect 4:3 (640x480) ทำให้ภาพถูกบีบ + เหลือขอบดำซ้าย-ขวา = ฉากดูเล็ก. เปลี่ยนการวาด BG ของ balloon_boss เป็นแบบ cover เต็มจอ (รักษาสัดส่วน 16:9, crop ส่วนเกิน, จัดกึ่งกลาง) วาดแยกจาก world layer → กล้อง/movement/area ของเกมคงเดิมทุกอย่าง เปลี่ยนเฉพาะพื้นหลังให้เต็มจอ. ไฟล์: Game/public/js/play.js (draw loop, สาขา isBalloonBoss cover-fill).",
|
||||
"cat": "Minigame 7"
|
||||
},
|
||||
{
|
||||
"n": 12,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ เล่นทีละคน เรียงตามคิวไปเรื่อย โดยที่ blog ตามสีผู้เล่นด้วย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 13,
|
||||
"name": "เข้าร่วมห้องไม่ได้",
|
||||
"detail": "สร้างห้องส่วนตัวเเล้ว เเต่เพื่อนจอยเข้าไปไม่ได้ ทั้งที่กรอกข้อมูลครบ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 14,
|
||||
"name": "Room ID",
|
||||
"detail": "เวลาจอยห้องให้กรอกเเค่ ID กับรหัสที่สร้าง",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 15,
|
||||
"name": "จอยห้องคนอื่น",
|
||||
"detail": "ในกรณีที่จอยห้องคนอื่น ไปหน้าเข้าร่วม เเละกดปุ่มเข้าร่วมไปเเล้ว จากนั้นกดปุ่ม exit จะพาไปหน้าสร้างห้อง สิ่งที่ถูกต้อง มันควรมาหน้า ห้องเข้าร่วมเกม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 16,
|
||||
"name": "grid กับ ฉาก ไม่ตรงกัน",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ฉากกับกริดไม่ตรงกัน ทำให้เดินทะลุ หรือเดินติดในจุดที่ไม่ควรติด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 17,
|
||||
"name": "ตัวละครวิ่งเร็วเกินไป",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ตัวละครผู้เล่นเร็วเกินไป",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 18,
|
||||
"name": "Oauth Social log-in error",
|
||||
"detail": "ไม่สามารถ log-inด้วย facebook / Google ได้ path: /oauth-public.php",
|
||||
"prio": "ต่ำ",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 19,
|
||||
"name": "หน้า login",
|
||||
"detail": "นโยบายความเป็นส่วนตัวกดไม่ได้",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 20,
|
||||
"name": "แสดงเลข version ตรงมุมจอ",
|
||||
"detail": "แสดงเลข version ตรงมุมจอขวาบนหรือขวาล่างก็ได้",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — badge มุมขวาล่าง อ่าน ?v= อัตโนมัติ",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 21,
|
||||
"name": "ลดขนาดตัวละคร",
|
||||
"detail": "ลดขนาดตัวละครในห้อง lobby ลงอีก 2เท่า",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 22,
|
||||
"name": "เปลี่ยน ui popup ตอนพูดบนหัว",
|
||||
"detail": "แก้ png seq บนหัวใหม่ เป็นแบบอื่น",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 23,
|
||||
"name": "ชื่่อตัวละครทับลำโพง/ทับ bubble",
|
||||
"detail": "ใน lobby ขยับชื่อลงมาอย่าให้ทับ",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 24,
|
||||
"name": "UI แต่งตัว : การแจ้งเตือนสีตัวละครถูกใช้แล้ว",
|
||||
"detail": "ตอนเลือกสีให้มีคำแจ้งเตือน used",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 25,
|
||||
"name": "เพิ่ม ข้อความแจ้งเตือนอัพเดทกลางจอ",
|
||||
"detail": "ถ้ามีการอัพเดทซีน อัพเดทสีตัวละครให้มีป็อปอัพเด้งมากลางหน้าจอ ว่ามีการอัพเดทและให้รอโหลด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 26,
|
||||
"name": "เอาเงาตัวละครออก 1 อัน",
|
||||
"detail": "ตอนนี้ตัวละครมีเงา 2 ชั้น เอาออกไป 1 ชั้น",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — ปิด default-shadow overlay เหลือเงาในสไปรต์",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 27,
|
||||
"name": "optimize ตัวละครให้เล็กลงอีก มันโหลดช้า",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 28,
|
||||
"name": "ปุ่ม start ใน lobby",
|
||||
"detail": "ทำให้ดูโต้ตอบหน่อย เช่นพอเดินเข้าไปใกล้ให้มัน glow ขึ้นมา",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 29,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "สี blog กับคนเล่นยังไม่สัมพันกัน ต้องเรียงตามสี และตามคิว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 30,
|
||||
"name": "เกมต่อตึก บั๊กคนที่ 5",
|
||||
"detail": "คนที่ 5 ไม่สามารถเล่นได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 31,
|
||||
"name": "เอา mock การ์ดผู้ต้องหาเก่าออกไป",
|
||||
"detail": "มันแว่บๆมา",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส) — ซ่อนรูปจนรูปคดีจริงโหลด + spinner",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 32,
|
||||
"name": "เอา popup ขออนุญาตใช้ไมค์ออกไปเลย น่ารำคาญ",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 33,
|
||||
"name": "หน้า howto เปลี่ยนสีปุ่ม ready ด้วยตอน กดready แล้ว",
|
||||
"detail": "เปลี่ยนเป้นสีเขียวพร้อมเครื่องหมายถูกตามดีไซน์",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 34,
|
||||
"name": "เกม ใช่ หรือไม่ : ตอนจบบั๊ค",
|
||||
"detail": "ตอนสรุปคะแนน ยังนับเวลาอยุ่ และขึ้น gameover ทับหน้าสรุปคะแนน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG1 ใช่/ไม่: capture mg1AnyScore ก่อนล้าง (gameover) + guard quiz-phase หลังจบ (timer)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 35,
|
||||
"name": "เกมวิ่ง : กดปุ่มไม่กระโดด",
|
||||
"detail": "ปุ่ม jump กดแล้วไม่กระโดด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 36,
|
||||
"name": "เกมวิ่ง : ตอนจบบั๊ค วิ่งเลยฉาก",
|
||||
"detail": "ตอนจบบั๊ค วิ่งเลยฉาก (ในบางรอบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG2 วิ่งเลยฉาก: walk-to-finish ค่อยๆเดินเข้าเส้น + clamp ไม่เลยเส้น",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 37,
|
||||
"name": "popup รับหลักฐาน ทับไมค์",
|
||||
"detail": "กดไมค์ไม่ได้ ตอนรับหลักฐาน ไปดูเรื่องเลเยอร์อีกที",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 38,
|
||||
"name": "หน้าส่งหลักฐาน ปุ่มส่งหลักฐานหลุดจอ (เครื่องของเนส)",
|
||||
"detail": "เป้นเครื่องเนสคนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 39,
|
||||
"name": "ห้องส่งหลักฐาน : เมื่อมีคนหลุด 1 คน จะไปต่อไม่ได้เลย",
|
||||
"detail": "",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — testimony: disconnect ลบ submit/ready + client ใช้ totalPlayers authoritative",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 40,
|
||||
"name": "จำนวนคน ให้เล่นได้ตั้งแต่ 4-6 คน",
|
||||
"detail": "กรณีเลือกผู้เล่นตั้งแต่ 4 คนขึ้นไปไม่ต้องมี bot",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 41,
|
||||
"name": "เกม หยิบแผ่นป้าย",
|
||||
"detail": "ต้องเล่นให้ครบ 10 ข้อก่อน ค่อยจบเกม (ตอนนี้เล่นถูก 1 ข้อ จบเกมเลย)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG4 หยิบป้าย: ใช้ map จริง (currentPlayMapId) เปิด mission flow + default 10 ข้อ",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 42,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ท่ากระโดดแปลก ยืนซอยเท้าทำไม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "anim ไม่ sync: ยืนบนแท่นที่เลื่อนลง world Y เปลี่ยนทุกเฟรม → ปลายทางเดา isWalking จาก delta = เห็น ซอยเท้า ตลอด. แก้: ส่ง isWalking จริงใน move (jsWalking) + ปลายทางใช้ค่านั้น (play.js ~19162/20501/24930). v0.006260100",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 43,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ขั้นบันไดมันถี่ไป บางทีกระโดดแล้วเบิ้ลขั้นเลย ปรับให้ระยะห่างแต่ละขั้นมันเท่ากัน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "ขั้นถี่/ไม่เท่า → เบิ้ลขั้น+ติดรอแท่ง. แก้ layout mnptfts2: ระยะเท่ากันทุกขั้น (gap 3 แถว, rows 2/5/8/11/14/17, zigzag กว้าง6) ในโซนปลอดภัย (เลี่ยง hazard แถว 0/18/19). reach apex 4.5 > 3 = ถึงทุกขั้น (ไม่ติดรอ #43b) + ขั้นถัดไป-อีกขั้น 6 แถว > reach = กระโดดข้ามไม่ได้ (ไม่เบิ้ล). seam รอยต่อ wrap ห่าง 5 แถว 1 จุด/รอบ (h=20 หาร 3 ไม่ลง). restart service หลังแก้แมป",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 44,
|
||||
"name": "เกมกระโดด",
|
||||
"detail": "อีเวนท์พิเศษ (ทนาย/เทพความรู้) หายไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG5 อีเวนต์พิเศษ: เพิ่ม branch jump_survive วาง icon บน platform",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 45,
|
||||
"name": "เกมไม่รองรับ 4K",
|
||||
"detail": "หน้ารับหลักฐาน (หลังจากเล่นเกมจบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 46,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "สีตัวละครยังไม่ตรงกับที่เลือกมา",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6 สี: ยาน Rocket-1..8 ตาม lobby theme ของผู้เล่น",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 47,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "อุกาบาดให้พุ่งเร็วกว่านี้ และลูกเล็กกว่านี้เกมง่ายไป",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6: อุกาบาตเร็วขึ้น (+~50%) + เล็กลง (48->34/11->8)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 48,
|
||||
"name": "เพิ่ม popup เมื่อเกมค้าง เล่นต้่อไม่ได้ ให้มีปุ่มกดออกไปหน้าแรกสุด",
|
||||
"detail": "สาเหตุเกิดจากมีคนหลุดระหว่างรอ host กด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 49,
|
||||
"name": "กรณีผู้เล่นหลุดออกจากเกม",
|
||||
"detail": "ถ้ามีคนหลุด ให้กลายเป้นบอทแทน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว 2026-06-25: คนออกระหว่าง LobbyB/เปลี่ยนหน้า กลายเป็นบอทสีตามคนออก (ดูบั๊ก #73)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 50,
|
||||
"name": "ผู้เล่น 6 คน: บางเกมเล่นได้แค่ 5 (คนที่ 6 หายจากสรุป)",
|
||||
"detail": "สรุปคะแนนฝั่ง client slice(0,5) ตัดคนที่ 6 — แก้เป็น slice(0,6) ทุกมินิเกม (MG1/3/4/5/6/7)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 51,
|
||||
"name": "ผู้เล่น 6 คน: คะแนนตอนจบไม่ตรงกันแต่ละจอ",
|
||||
"detail": "MG1 merge คะแนน server / MG6,7 merge คะแนน host -> ครบ 6 ตรงกัน (MG3 ยัง local)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 52,
|
||||
"name": "หน้าไต่สวน/สรุปหลักฐาน: กรอบปลิ้นตอน 6 คน",
|
||||
"detail": "member row grid repeat(5)->repeat(6) ใน testimony-overlay.css (คนที่6 ตกแถว2)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 53,
|
||||
"name": "เพิ่มหน้าโหลด (loading) ตอนเข้าเกม",
|
||||
"detail": "overlay spinner กันเห็นจอเปล่า/asset แว่บ ซ่อนเมื่อ draw เฟรมแรก + safety 8s",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 54,
|
||||
"name": "host โหลดนานมากตอนเข้าเกม",
|
||||
"detail": "optimize asset/char (เกี่ยว row 28) — loading overlay ช่วยซ่อน แต่ยังโหลดช้าจริง",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "loading overlay บังอาการแล้ว · root = canvas tint ภาพตัวละครใหญ่ (source ไม่อยู่ใน repo) → ต้อง optimize ภาพแยก",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 55,
|
||||
"name": "หลังออกจาก LobbyA ไม่ให้ใครเข้า join",
|
||||
"detail": "server มี joinLocked (set ตอน LobbyA->case ~8656, check ~7585 whitelist nickname) — ต้อง repro ว่าใครเข้าได้ยังไง (room list ยังโชว์? ชื่อซ้ำ whitelist?)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 — กรองห้อง joinLocked ออกจาก /api/spaces list (ห้องเริ่มคดีแล้วหาย) + server reject อยู่แล้ว",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 56,
|
||||
"name": "ตอนกลับ LobbyB โชว์รูปแรกก่อนแล้วค่อยถูก",
|
||||
"detail": "เหมือน #1 การ์ด — ต้องใส่ loading state ตอน return LobbyB ด้วย (มี room-loading-overlay อยู่แต่ซ่อนเร็วไป)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0308 — การ์ดมี spinner (#1) + room-loading overlay ใส่ % + รอ char preload เสร็จก่อนซ่อน + safety 12s",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 57,
|
||||
"name": "เล่นมินิเกมเกิน 3 ครั้งต่อผู้ต้องสงสัย (+การ์ดพิเศษโผล่รอบที่4)",
|
||||
"detail": "server cap suspect-pick-start (max หลักฐาน>=3 reject) + award cap 3 + client canInvestigateSuspect<3 (เดิม stub return true). UI messaging มีอยู่แล้ว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 58,
|
||||
"name": "ปุ่ม \"เล่นเกมต่อ\" ไม่รู้ว่าค้างหรือรอคนอื่น",
|
||||
"detail": "text \"รอผู้เล่นอื่น (X/Y)\" ถูก set อยู่แล้วแต่ CSS font-size:0 ซ่อน → เพิ่ม .is-waiting CSS โชว์ข้อความ+spinner แทนรูปปุ่ม",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 59,
|
||||
"name": "loading ไม่คลุม scene transition (เห็นฉากเก่าก่อนเปลี่ยน)",
|
||||
"detail": "LobbyA->B (in-page swap) โชว์ room-loading ระหว่าง fetch+swap · LobbyB->เกม โชว์ overlay ก่อน location.href · play.html มี overlay รับช่วง",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 60,
|
||||
"name": "ทำหน้า testcase บนเซิร์ฟเวอร์",
|
||||
"detail": "srv1361159.hstgr.cloud/testcase — bug list + filter/search/นับสถานะ generate จาก xlsx",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 61,
|
||||
"name": "MG3 สี block ไม่ตรง user (เกม+admin)",
|
||||
"detail": "PNG Block-Code-1..8 deploy ครบแล้ว · ต้อง wire สี block = lobby theme ของผู้วาง (seat=theme refactor, ต้องเทส 2 จอ) + admin 8-slot config",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250200 — block สีตาม theme ผู้เล่นที่ seat (Block-Code-1..8) ตอนวาด ไม่แตะ turn/sync",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 62,
|
||||
"name": "MG1/MG4 user/bot ติดกัน (เดินไม่ผ่าน)",
|
||||
"detail": "เพิ่ม anti-stuck: ดันชนเพื่อน → เขย่า (shake) + ปลดล็อกเดินผ่านกันชั่วคราว คงกำแพง/area/quiz-lock; ทั้งผู้เล่นและบอท",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250100 (รอเทส)",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 63,
|
||||
"name": "LobbyA มือถือ กดเดินไม่ได้",
|
||||
"detail": "lobby canvas มีแค่ dblclick (ไม่มี touch) → มือถือแตะเดินไม่ได้. แก้: เพิ่ม tap-to-walk (เฉพาะ touch, PC ยัง dblclick) + DPI-safe coord + touch-action:none + zoom-out default บนจอ<600px (PC ย่อส่วน)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.js?v=0.0310 (รอเทสมือถือ) · PC ไม่กระทบ",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 64,
|
||||
"name": "มือถือ: ปุ่มแชท AI ใหญ่ไป + ปุ่ม F (host) ไม่มี",
|
||||
"detail": "AI chat collapsed btn 213x201px → ใหญ่บนมือถือ. + การกด F (interact: host เลือกระดับ/คดี, ห้องแต่งตัว) ไม่มีบนมือถือ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (css 0.0176/js 0.0311): media<600px ย่อปุ่ม AI (92px) + ปุ่มลอย \"โต้ตอบ(F)\" บน touch-primary device (dispatch synthetic KeyF). PC ไม่เห็น",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 65,
|
||||
"name": "อีเวนต์พิเศษ (ทนาย/เทพ) = 1 ใน 3 (ตามดีไซน์)",
|
||||
"detail": "ดีไซน์: 3 เกม/LobbyB เล่นได้เกมละ 3 ครั้ง · อีเวนต์พิเศษ 1/3 ต่อครั้ง (1 ใบ) · Test Mode บังคับ. revert การแก้ \">1 ต่อรอบ\" กลับเป็น 1 ใบ/ครั้งที่ติด",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ revert 2026-06-25 — กลับเป็น 1/3 (1 ใบ/ครั้งที่ติด) ตามดีไซน์ · ยืนยันโครงสร้าง 3เกม×3ครั้ง + disruptor toggle (>4คน/Test Mode) ถูกต้อง",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 66,
|
||||
"name": "host หลุด → ย้าย host ให้คนอยู่นานสุด",
|
||||
"detail": "pickNextHostPeer เลือก spawnJoinOrder ต่ำสุด (เสถียรข้าม reconnect) แทน Map-first + host จริง reclaim ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server) — Part 1 host migration",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 67,
|
||||
"name": "คนหลุดกลางเกม → 10 วิ reconnect + บอทเล่นแทน",
|
||||
"detail": "peer-disconnect-grace แบนเนอร์นับ 10 วิ · กลับทัน=cancel เล่นต่อ · ครบเวลา=peer-bot-takeover host เติมบอท 1 ตัว (rebalance, cap≤6). บอทไม่สืบทอดตำแหน่ง/คะแนน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server+play.js v0.006250400) — Part 2 แบบ A · seamless(host คุม entity เดิม)=ค้าง(เสี่ยง)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 68,
|
||||
"name": "ห้องแต่งตัว (customize) มือถือแนวนอนใช้ไม่ได้",
|
||||
"detail": "@media max-width:960px stack แนวตั้ง → panel สูงตาม aspect ล้นจอแนวนอน. แก้: เพิ่ม media (landscape + max-height:540px) ใช้เลย์เอาต์แนวนอน + ขนาดอิงความสูงจอ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 customize-popup.css?v=43 (Main-Lobby + room-lobby) — รอเทสมือถือแนวนอน",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 69,
|
||||
"name": "UI ห้องเกม (LobbyA) มือถือแนวนอน — ย่อให้พอดี",
|
||||
"detail": "room-lobby ไม่มี landscape media query → chat panel(576x525), ปุ่ม voice/แว่น/ถ้วย(80px), AI chat ล้น/ใหญ่บนแนวนอน. เพิ่ม @media(landscape+max-height:540px) ย่อตามความสูงจอ (อิง Main-Lobby pattern)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.css?v=0.0177 — chat/BR buttons/AI/ready/exit · overlay หลักฐาน-อันดับ (inline/JS) ยังต้องทำแยก",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 70,
|
||||
"name": "การ์ดรางวัลหลักฐาน มือถือใหญ่เกิน กดปุ่มไม่ได้",
|
||||
"detail": "หน้าโชว์การ์ดรางวัล \"คุณได้รับหลักฐาน!\" (หลังจบมินิเกมสืบสวน) บนมือถือการ์ดใหญ่มากจนเนื้อหา+ปุ่ม \"เก็บเข้าแฟ้ม\" หลุดล่างจอ กดปิดไม่ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250402): ทำ overlay เป็น scroll container + inner margin:auto (กัน flexbox clip ด้านบนเวลาเนื้อหาสูงเกินจอ) + คุมขนาดการ์ด/หัวข้อ/ปุ่มด้วย clamp ตาม viewport (การ์ด max-width min(72vw,360px) + max-height 52vh, ปุ่ม font/padding clamp). มือถือแนวตั้งเลื่อนถึงปุ่มได้เสมอ. ไฟล์: Game/public/js/play.js (showInSceneEvidenceReveal).",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 71,
|
||||
"name": "หน้าผลชนะ (โพเดียม) ตัวละครลอย ไม่ยืนบนพื้น",
|
||||
"detail": "หน้า \"ยินดีด้วย ทีมนักสืบ ชนะ!\" ตัวละครผู้ชนะลอยอยู่กลางอากาศ ไม่ยืนบนพื้นเวที",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0312): พื้นหลัง win-bg เดิม center/cover ทำให้พื้นเวทีถูก crop เลื่อน + podium ตั้ง bottom:9% ตัวละครเลยลอย. แก้เป็น (1) ปักพื้นหลังไว้ขอบล่าง background-position:center bottom -> พื้นเวทีติดขอบล่างจอเสมอทุกสัดส่วน (2) ลด podium bottom 9% -> 2% ให้ตัวละครยืนบนพื้น. ไฟล์: Game/public/js/room-lobby.js (injectFinalResultStyle, #trial-final-podium).",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 72,
|
||||
"name": "ป๊อปอัปขอสิทธิ์ไมค์ เด้งทุกครั้งที่เข้า Lobby",
|
||||
"detail": "หน้าขอสิทธิ์ไมโครโฟน (mic-permission-overlay) เด้งขึ้นทุกครั้งที่เข้า LobbyA/LobbyB น่ารำคาญ ทั้งที่เบราว์เซอร์จำสิทธิ์ให้แล้ว",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0313): เดิม overlay เป็น display:flex default จึงเด้งทุกครั้งที่โหลดหน้า. เพิ่มตรวจสิทธิ์ผ่าน navigator.permissions.query(microphone) + จำสถานะใน localStorage (justiceMicPromptHandled). แสดงเฉพาะครั้งแรกที่ยังไม่เคยตัดสินใจ (permission=prompt และยังไม่เคยกดอนุญาต/ปิด); ถ้า granted แล้ว/เคยจัดการแล้ว → ซ่อนทันที (มี inline script กันแฟลชก่อน paint). ปุ่ม 🎤 ขอสิทธิ์ไมค์ ในห้องยังใช้ขอใหม่ได้. ไฟล์: room-lobby.html (inline gate) + room-lobby.js (gateMicPermissionOverlay, hideMicPermissionOverlay).",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 73,
|
||||
"name": "คนออก LobbyB / ช่วงเปลี่ยนหน้า → ไม่เติมบอท + ค้างรอผู้เล่น เริ่มเกมต่อไม่ได้",
|
||||
"detail": "คนหลุดระหว่างนั่งอยู่ LobbyB หรือช่วงเปลี่ยนหน้า minigame→LobbyB เดิมไม่ถูกเติมบอทแทน + ยังถูกนับใน roster คดี → host กดเริ่มสืบสวนรอบต่อไปไม่ได้ ค้าง รอผู้เล่น (x/y) ตลอดไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "ขยาย grace+bot-takeover gate ให้ครอบ LobbyB/คดีล็อก ไม่ใช่แค่ minigame active (server.js ~8921: inCaseFlowForBot = detectiveMinigameActive || serverMapIsPostCaseLobbyB || (joinLocked && caseParticipantNicknames.size>0)). คนออก→หลัง grace 10วิ ลบจาก roster + เติมบอทสีตามคนออก + presence ใหม่ → allHere=true เล่นต่อได้. + ส่ง botSlotCount ใน lobby-tint-sync (server.js ~121) + client อัปเดต lobbyBotSlotCount ก่อน render (room-lobby.js ~768) ให้บอทโผล่ทันทีไม่ต้อง re-join. v0.0323",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 74,
|
||||
"name": "แถวหน้าตัวละคร (ready ใต้ปุ่ม HOW TO) ค้างบนจอระหว่างเล่นเกม",
|
||||
"detail": "หลังกด HOW TO/Ready แล้วเริ่มเกม กล่องแถว avatar คนที่กด ready (ใต้ปุ่ม) ไม่ถูกซ่อน ค้างอยู่กลางจอล่างทับฉากเกม (เห็นชัดใน MG3 stack) — กระทบ MG1/3/5/6",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "กล่องลอย #gch-ready-avatars-float ถูกซ่อนเฉพาะ MG2 (dismissGauntletCrownHowtoOverlayPlay) + MG4 (quiz_carry) เท่านั้น. begin*CountdownThenRun ของ MG1/3/5/6 (stack/jump/space/quiz) ตั้ง gauntletCrownHowtoVisible=false แต่ไม่เรียก hideReadyAvatarsFloat() → กล่องค้าง display:flex ทั้งเกม (ไม่มี driver เรียกใหม่ตอน live). แก้: เพิ่ม hideReadyAvatarsFloat() ในทั้ง 4 ฟังก์ชันเริ่มเกม (play.js). v0.006260101",
|
||||
"cat": "อื่นๆ"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,732 @@
|
||||
[
|
||||
{
|
||||
"n": 1,
|
||||
"name": "แต่งตัวไม่ได้",
|
||||
"detail": "แต่งตัวยังไม่ได้ มีพี่คิวทำได้คนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 2,
|
||||
"name": "ตัวละครไม่แสดง",
|
||||
"detail": "หน้าเลือกเกม Quiz battle ภาพตัวละครไม่แสดง",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 3,
|
||||
"name": "ระบบ join เกมมีปัญหา",
|
||||
"detail": "เมื่อเข้ามินิเกม : บางคนเล่นเกมกะบอท บางคนเล่นกะเพื่อน แต่ละคนเข้ามินิเกมแล้วเจอกันบ้าง ไม่เจอกันบ้าง",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 4,
|
||||
"name": "ระบบ Host มีปัญหา - 1",
|
||||
"detail": "เป็น host ในเกม ตอนที่เลือกผู้ต้องสงสัย รอบที่ 2 ระบบบอกว่าให้รอ host ไม่สามารถเลือกการ์ดและกดเริ่มสืบสวนได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 5,
|
||||
"name": "ระบบ Host มีปัญหา - 2",
|
||||
"detail": "เมื่อกดออกจากเกม Host เปลี่ยนจากพี่ต้อมเป็นพี่คิวอัติโนมัติ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 6,
|
||||
"name": "ระบบ Host มีปัญหา - 3",
|
||||
"detail": "เมื่อเข้ามินิเกม ทุกคนเห็นตัวเองเป็น host หมดเลย แต่กดเริ่มเกมไม่ได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 7,
|
||||
"name": "ระบบสีตัวละคร ยังใช้ไม่ได้",
|
||||
"detail": "สีตัวละครเปลี่ยนไปเรื่อย ไม่รุ้ว่าเราสีอะไรแน่ งงไปหมด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 8,
|
||||
"name": "หน้าเข้าร่วมเกมส์",
|
||||
"detail": "ห้องไหนที่เริ่มเกมส์ไปแล้ว เมื่อกดเข้าร่วมแสดง popup alert error ปรับดีไซต์ให้สวยงามกว่านี้",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 9,
|
||||
"name": "เกมวิ่งขึ้นบันได ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ ใครตกจอตาย แต่ถ้าขึ้นไปอยู่บนสุดแล้วจะกระโดดไม่ไปต่อเฉยๆ แต่ไม่ตาย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 10,
|
||||
"name": "เกมวิ่งขึ้นบันได",
|
||||
"detail": "เวลาหมดแล้ว ให้เล่นรอบ 2 auto / รอบ 3 มี popup ให้กดเล่นต่อ",
|
||||
"prio": "",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 11,
|
||||
"name": "เกมยิงลูกโป่ง",
|
||||
"detail": "ฉากเกมยิงลูกโป่ง (MG7 Mega Virus) เล็กไป — ต้องการให้ภาพพื้นหลัง (BG) เต็มจอ แต่การเดิน (movement) และพื้นที่เล่น (area) ต่างๆ คงเดิม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250401): BG ของ MG7 เป็นภาพ 16:9 (1920x1080) แต่โค้ดเดิม map ภาพเข้า world rect 4:3 (640x480) ทำให้ภาพถูกบีบ + เหลือขอบดำซ้าย-ขวา = ฉากดูเล็ก. เปลี่ยนการวาด BG ของ balloon_boss เป็นแบบ cover เต็มจอ (รักษาสัดส่วน 16:9, crop ส่วนเกิน, จัดกึ่งกลาง) วาดแยกจาก world layer → กล้อง/movement/area ของเกมคงเดิมทุกอย่าง เปลี่ยนเฉพาะพื้นหลังให้เต็มจอ. ไฟล์: Game/public/js/play.js (draw loop, สาขา isBalloonBoss cover-fill).",
|
||||
"cat": "Minigame 7"
|
||||
},
|
||||
{
|
||||
"n": 12,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "ปรับ flow ให้ถูกต้อง คือ เล่นทีละคน เรียงตามคิวไปเรื่อย โดยที่ blog ตามสีผู้เล่นด้วย",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 13,
|
||||
"name": "เข้าร่วมห้องไม่ได้",
|
||||
"detail": "สร้างห้องส่วนตัวเเล้ว เเต่เพื่อนจอยเข้าไปไม่ได้ ทั้งที่กรอกข้อมูลครบ",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 14,
|
||||
"name": "Room ID",
|
||||
"detail": "เวลาจอยห้องให้กรอกเเค่ ID กับรหัสที่สร้าง",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 15,
|
||||
"name": "จอยห้องคนอื่น",
|
||||
"detail": "ในกรณีที่จอยห้องคนอื่น ไปหน้าเข้าร่วม เเละกดปุ่มเข้าร่วมไปเเล้ว จากนั้นกดปุ่ม exit จะพาไปหน้าสร้างห้อง สิ่งที่ถูกต้อง มันควรมาหน้า ห้องเข้าร่วมเกม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 16,
|
||||
"name": "grid กับ ฉาก ไม่ตรงกัน",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ฉากกับกริดไม่ตรงกัน ทำให้เดินทะลุ หรือเดินติดในจุดที่ไม่ควรติด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 17,
|
||||
"name": "ตัวละครวิ่งเร็วเกินไป",
|
||||
"detail": "เกม Quiz battle เกมที่ 1 ตัวละครผู้เล่นเร็วเกินไป",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 18,
|
||||
"name": "Oauth Social log-in error",
|
||||
"detail": "ไม่สามารถ log-inด้วย facebook / Google ได้ path: /oauth-public.php",
|
||||
"prio": "ต่ำ",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 19,
|
||||
"name": "หน้า login",
|
||||
"detail": "นโยบายความเป็นส่วนตัวกดไม่ได้",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 20,
|
||||
"name": "แสดงเลข version ตรงมุมจอ",
|
||||
"detail": "แสดงเลข version ตรงมุมจอขวาบนหรือขวาล่างก็ได้",
|
||||
"prio": "ต่ำ",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — badge มุมขวาล่าง อ่าน ?v= อัตโนมัติ",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 21,
|
||||
"name": "ลดขนาดตัวละคร",
|
||||
"detail": "ลดขนาดตัวละครในห้อง lobby ลงอีก 2เท่า",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 22,
|
||||
"name": "เปลี่ยน ui popup ตอนพูดบนหัว",
|
||||
"detail": "แก้ png seq บนหัวใหม่ เป็นแบบอื่น",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 23,
|
||||
"name": "ชื่่อตัวละครทับลำโพง/ทับ bubble",
|
||||
"detail": "ใน lobby ขยับชื่อลงมาอย่าให้ทับ",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 24,
|
||||
"name": "UI แต่งตัว : การแจ้งเตือนสีตัวละครถูกใช้แล้ว",
|
||||
"detail": "ตอนเลือกสีให้มีคำแจ้งเตือน used",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 25,
|
||||
"name": "เพิ่ม ข้อความแจ้งเตือนอัพเดทกลางจอ",
|
||||
"detail": "ถ้ามีการอัพเดทซีน อัพเดทสีตัวละครให้มีป็อปอัพเด้งมากลางหน้าจอ ว่ามีการอัพเดทและให้รอโหลด",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 26,
|
||||
"name": "เอาเงาตัวละครออก 1 อัน",
|
||||
"detail": "ตอนนี้ตัวละครมีเงา 2 ชั้น เอาออกไป 1 ชั้น",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — ปิด default-shadow overlay เหลือเงาในสไปรต์",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 27,
|
||||
"name": "optimize ตัวละครให้เล็กลงอีก มันโหลดช้า",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 28,
|
||||
"name": "ปุ่ม start ใน lobby",
|
||||
"detail": "ทำให้ดูโต้ตอบหน่อย เช่นพอเดินเข้าไปใกล้ให้มัน glow ขึ้นมา",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 29,
|
||||
"name": "เกมต่อตึก ผิด flow",
|
||||
"detail": "สี blog กับคนเล่นยังไม่สัมพันกัน ต้องเรียงตามสี และตามคิว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 30,
|
||||
"name": "เกมต่อตึก บั๊กคนที่ 5",
|
||||
"detail": "คนที่ 5 ไม่สามารถเล่นได้",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 31,
|
||||
"name": "เอา mock การ์ดผู้ต้องหาเก่าออกไป",
|
||||
"detail": "มันแว่บๆมา",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส) — ซ่อนรูปจนรูปคดีจริงโหลด + spinner",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 32,
|
||||
"name": "เอา popup ขออนุญาตใช้ไมค์ออกไปเลย น่ารำคาญ",
|
||||
"detail": "",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 33,
|
||||
"name": "หน้า howto เปลี่ยนสีปุ่ม ready ด้วยตอน กดready แล้ว",
|
||||
"detail": "เปลี่ยนเป้นสีเขียวพร้อมเครื่องหมายถูกตามดีไซน์",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 34,
|
||||
"name": "เกม ใช่ หรือไม่ : ตอนจบบั๊ค",
|
||||
"detail": "ตอนสรุปคะแนน ยังนับเวลาอยุ่ และขึ้น gameover ทับหน้าสรุปคะแนน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG1 ใช่/ไม่: capture mg1AnyScore ก่อนล้าง (gameover) + guard quiz-phase หลังจบ (timer)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 35,
|
||||
"name": "เกมวิ่ง : กดปุ่มไม่กระโดด",
|
||||
"detail": "ปุ่ม jump กดแล้วไม่กระโดด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 36,
|
||||
"name": "เกมวิ่ง : ตอนจบบั๊ค วิ่งเลยฉาก",
|
||||
"detail": "ตอนจบบั๊ค วิ่งเลยฉาก (ในบางรอบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG2 วิ่งเลยฉาก: walk-to-finish ค่อยๆเดินเข้าเส้น + clamp ไม่เลยเส้น",
|
||||
"cat": "Minigame 2"
|
||||
},
|
||||
{
|
||||
"n": 37,
|
||||
"name": "popup รับหลักฐาน ทับไมค์",
|
||||
"detail": "กดไมค์ไม่ได้ ตอนรับหลักฐาน ไปดูเรื่องเลเยอร์อีกที",
|
||||
"prio": "กลาง",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 38,
|
||||
"name": "หน้าส่งหลักฐาน ปุ่มส่งหลักฐานหลุดจอ (เครื่องของเนส)",
|
||||
"detail": "เป้นเครื่องเนสคนเดียว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 39,
|
||||
"name": "ห้องส่งหลักฐาน : เมื่อมีคนหลุด 1 คน จะไปต่อไม่ได้เลย",
|
||||
"detail": "",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — testimony: disconnect ลบ submit/ready + client ใช้ totalPlayers authoritative",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 40,
|
||||
"name": "จำนวนคน ให้เล่นได้ตั้งแต่ 4-6 คน",
|
||||
"detail": "กรณีเลือกผู้เล่นตั้งแต่ 4 คนขึ้นไปไม่ต้องมี bot",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 41,
|
||||
"name": "เกม หยิบแผ่นป้าย",
|
||||
"detail": "ต้องเล่นให้ครบ 10 ข้อก่อน ค่อยจบเกม (ตอนนี้เล่นถูก 1 ข้อ จบเกมเลย)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG4 หยิบป้าย: ใช้ map จริง (currentPlayMapId) เปิด mission flow + default 10 ข้อ",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 42,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ท่ากระโดดแปลก ยืนซอยเท้าทำไม",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "anim ไม่ sync: ยืนบนแท่นที่เลื่อนลง world Y เปลี่ยนทุกเฟรม → ปลายทางเดา isWalking จาก delta = เห็น ซอยเท้า ตลอด. แก้: ส่ง isWalking จริงใน move (jsWalking) + ปลายทางใช้ค่านั้น (play.js ~19162/20501/24930). v0.006260100",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 43,
|
||||
"name": "เกม กระโดด",
|
||||
"detail": "ขั้นบันไดมันถี่ไป บางทีกระโดดแล้วเบิ้ลขั้นเลย ปรับให้ระยะห่างแต่ละขั้นมันเท่ากัน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-26",
|
||||
"note": "ขั้นถี่/ไม่เท่า → เบิ้ลขั้น+ติดรอแท่ง. แก้ layout mnptfts2: ระยะเท่ากันทุกขั้น (gap 3 แถว, rows 2/5/8/11/14/17, zigzag กว้าง6) ในโซนปลอดภัย (เลี่ยง hazard แถว 0/18/19). reach apex 4.5 > 3 = ถึงทุกขั้น (ไม่ติดรอ #43b) + ขั้นถัดไป-อีกขั้น 6 แถว > reach = กระโดดข้ามไม่ได้ (ไม่เบิ้ล). seam รอยต่อ wrap ห่าง 5 แถว 1 จุด/รอบ (h=20 หาร 3 ไม่ลง). restart service หลังแก้แมป",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 44,
|
||||
"name": "เกมกระโดด",
|
||||
"detail": "อีเวนท์พิเศษ (ทนาย/เทพความรู้) หายไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG5 อีเวนต์พิเศษ: เพิ่ม branch jump_survive วาง icon บน platform",
|
||||
"cat": "Minigame 5"
|
||||
},
|
||||
{
|
||||
"n": 45,
|
||||
"name": "เกมไม่รองรับ 4K",
|
||||
"detail": "หน้ารับหลักฐาน (หลังจากเล่นเกมจบ)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 46,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "สีตัวละครยังไม่ตรงกับที่เลือกมา",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6 สี: ยาน Rocket-1..8 ตาม lobby theme ของผู้เล่น",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 47,
|
||||
"name": "เกมยิิงอุกาบาด",
|
||||
"detail": "อุกาบาดให้พุ่งเร็วกว่านี้ และลูกเล็กกว่านี้เกมง่ายไป",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส) — MG6: อุกาบาตเร็วขึ้น (+~50%) + เล็กลง (48->34/11->8)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 48,
|
||||
"name": "เพิ่ม popup เมื่อเกมค้าง เล่นต้่อไม่ได้ ให้มีปุ่มกดออกไปหน้าแรกสุด",
|
||||
"detail": "สาเหตุเกิดจากมีคนหลุดระหว่างรอ host กด",
|
||||
"prio": "วิกฤต",
|
||||
"status": "เปิด",
|
||||
"updated": "",
|
||||
"note": "",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 49,
|
||||
"name": "กรณีผู้เล่นหลุดออกจากเกม",
|
||||
"detail": "ถ้ามีคนหลุด ให้กลายเป้นบอทแทน",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว 2026-06-25: คนออกระหว่าง LobbyB/เปลี่ยนหน้า กลายเป็นบอทสีตามคนออก (ดูบั๊ก #73)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 50,
|
||||
"name": "ผู้เล่น 6 คน: บางเกมเล่นได้แค่ 5 (คนที่ 6 หายจากสรุป)",
|
||||
"detail": "สรุปคะแนนฝั่ง client slice(0,5) ตัดคนที่ 6 — แก้เป็น slice(0,6) ทุกมินิเกม (MG1/3/4/5/6/7)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 1"
|
||||
},
|
||||
{
|
||||
"n": 51,
|
||||
"name": "ผู้เล่น 6 คน: คะแนนตอนจบไม่ตรงกันแต่ละจอ",
|
||||
"detail": "MG1 merge คะแนน server / MG6,7 merge คะแนน host -> ครบ 6 ตรงกัน (MG3 ยัง local)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-24",
|
||||
"note": "✅ แก้แล้ว 2026-06-24 v0.006242500 (รอเทส)",
|
||||
"cat": "Minigame 6"
|
||||
},
|
||||
{
|
||||
"n": 52,
|
||||
"name": "หน้าไต่สวน/สรุปหลักฐาน: กรอบปลิ้นตอน 6 คน",
|
||||
"detail": "member row grid repeat(5)->repeat(6) ใน testimony-overlay.css (คนที่6 ตกแถว2)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 53,
|
||||
"name": "เพิ่มหน้าโหลด (loading) ตอนเข้าเกม",
|
||||
"detail": "overlay spinner กันเห็นจอเปล่า/asset แว่บ ซ่อนเมื่อ draw เฟรมแรก + safety 8s",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006242530 (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 54,
|
||||
"name": "host โหลดนานมากตอนเข้าเกม",
|
||||
"detail": "optimize asset/char (เกี่ยว row 28) — loading overlay ช่วยซ่อน แต่ยังโหลดช้าจริง",
|
||||
"prio": "สูง",
|
||||
"status": "เปิด",
|
||||
"updated": "2026-06-25",
|
||||
"note": "loading overlay บังอาการแล้ว · root = canvas tint ภาพตัวละครใหญ่ (source ไม่อยู่ใน repo) → ต้อง optimize ภาพแยก",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 55,
|
||||
"name": "หลังออกจาก LobbyA ไม่ให้ใครเข้า join",
|
||||
"detail": "server มี joinLocked (set ตอน LobbyA->case ~8656, check ~7585 whitelist nickname) — ต้อง repro ว่าใครเข้าได้ยังไง (room list ยังโชว์? ชื่อซ้ำ whitelist?)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 — กรองห้อง joinLocked ออกจาก /api/spaces list (ห้องเริ่มคดีแล้วหาย) + server reject อยู่แล้ว",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 56,
|
||||
"name": "ตอนกลับ LobbyB โชว์รูปแรกก่อนแล้วค่อยถูก",
|
||||
"detail": "เหมือน #1 การ์ด — ต้องใส่ loading state ตอน return LobbyB ด้วย (มี room-loading-overlay อยู่แต่ซ่อนเร็วไป)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0308 — การ์ดมี spinner (#1) + room-loading overlay ใส่ % + รอ char preload เสร็จก่อนซ่อน + safety 12s",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 57,
|
||||
"name": "เล่นมินิเกมเกิน 3 ครั้งต่อผู้ต้องสงสัย (+การ์ดพิเศษโผล่รอบที่4)",
|
||||
"detail": "server cap suspect-pick-start (max หลักฐาน>=3 reject) + award cap 3 + client canInvestigateSuspect<3 (เดิม stub return true). UI messaging มีอยู่แล้ว",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 58,
|
||||
"name": "ปุ่ม \"เล่นเกมต่อ\" ไม่รู้ว่าค้างหรือรอคนอื่น",
|
||||
"detail": "text \"รอผู้เล่นอื่น (X/Y)\" ถูก set อยู่แล้วแต่ CSS font-size:0 ซ่อน → เพิ่ม .is-waiting CSS โชว์ข้อความ+spinner แทนรูปปุ่ม",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 59,
|
||||
"name": "loading ไม่คลุม scene transition (เห็นฉากเก่าก่อนเปลี่ยน)",
|
||||
"detail": "LobbyA->B (in-page swap) โชว์ room-loading ระหว่าง fetch+swap · LobbyB->เกม โชว์ overlay ก่อน location.href · play.html มี overlay รับช่วง",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 60,
|
||||
"name": "ทำหน้า testcase บนเซิร์ฟเวอร์",
|
||||
"detail": "srv1361159.hstgr.cloud/testcase — bug list + filter/search/นับสถานะ generate จาก xlsx",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.0309/server (รอเทส)",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 61,
|
||||
"name": "MG3 สี block ไม่ตรง user (เกม+admin)",
|
||||
"detail": "PNG Block-Code-1..8 deploy ครบแล้ว · ต้อง wire สี block = lobby theme ของผู้วาง (seat=theme refactor, ต้องเทส 2 จอ) + admin 8-slot config",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250200 — block สีตาม theme ผู้เล่นที่ seat (Block-Code-1..8) ตอนวาด ไม่แตะ turn/sync",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 62,
|
||||
"name": "MG1/MG4 user/bot ติดกัน (เดินไม่ผ่าน)",
|
||||
"detail": "เพิ่ม anti-stuck: ดันชนเพื่อน → เขย่า (shake) + ปลดล็อกเดินผ่านกันชั่วคราว คงกำแพง/area/quiz-lock; ทั้งผู้เล่นและบอท",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 v0.006250100 (รอเทส)",
|
||||
"cat": "Minigame 4"
|
||||
},
|
||||
{
|
||||
"n": 63,
|
||||
"name": "LobbyA มือถือ กดเดินไม่ได้",
|
||||
"detail": "lobby canvas มีแค่ dblclick (ไม่มี touch) → มือถือแตะเดินไม่ได้. แก้: เพิ่ม tap-to-walk (เฉพาะ touch, PC ยัง dblclick) + DPI-safe coord + touch-action:none + zoom-out default บนจอ<600px (PC ย่อส่วน)",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.js?v=0.0310 (รอเทสมือถือ) · PC ไม่กระทบ",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 64,
|
||||
"name": "มือถือ: ปุ่มแชท AI ใหญ่ไป + ปุ่ม F (host) ไม่มี",
|
||||
"detail": "AI chat collapsed btn 213x201px → ใหญ่บนมือถือ. + การกด F (interact: host เลือกระดับ/คดี, ห้องแต่งตัว) ไม่มีบนมือถือ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (css 0.0176/js 0.0311): media<600px ย่อปุ่ม AI (92px) + ปุ่มลอย \"โต้ตอบ(F)\" บน touch-primary device (dispatch synthetic KeyF). PC ไม่เห็น",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 65,
|
||||
"name": "อีเวนต์พิเศษ (ทนาย/เทพ) = 1 ใน 3 (ตามดีไซน์)",
|
||||
"detail": "ดีไซน์: 3 เกม/LobbyB เล่นได้เกมละ 3 ครั้ง · อีเวนต์พิเศษ 1/3 ต่อครั้ง (1 ใบ) · Test Mode บังคับ. revert การแก้ \">1 ต่อรอบ\" กลับเป็น 1 ใบ/ครั้งที่ติด",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ revert 2026-06-25 — กลับเป็น 1/3 (1 ใบ/ครั้งที่ติด) ตามดีไซน์ · ยืนยันโครงสร้าง 3เกม×3ครั้ง + disruptor toggle (>4คน/Test Mode) ถูกต้อง",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 66,
|
||||
"name": "host หลุด → ย้าย host ให้คนอยู่นานสุด",
|
||||
"detail": "pickNextHostPeer เลือก spawnJoinOrder ต่ำสุด (เสถียรข้าม reconnect) แทน Map-first + host จริง reclaim ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server) — Part 1 host migration",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 67,
|
||||
"name": "คนหลุดกลางเกม → 10 วิ reconnect + บอทเล่นแทน",
|
||||
"detail": "peer-disconnect-grace แบนเนอร์นับ 10 วิ · กลับทัน=cancel เล่นต่อ · ครบเวลา=peer-bot-takeover host เติมบอท 1 ตัว (rebalance, cap≤6). บอทไม่สืบทอดตำแหน่ง/คะแนน",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 (server+play.js v0.006250400) — Part 2 แบบ A · seamless(host คุม entity เดิม)=ค้าง(เสี่ยง)",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 68,
|
||||
"name": "ห้องแต่งตัว (customize) มือถือแนวนอนใช้ไม่ได้",
|
||||
"detail": "@media max-width:960px stack แนวตั้ง → panel สูงตาม aspect ล้นจอแนวนอน. แก้: เพิ่ม media (landscape + max-height:540px) ใช้เลย์เอาต์แนวนอน + ขนาดอิงความสูงจอ",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 customize-popup.css?v=43 (Main-Lobby + room-lobby) — รอเทสมือถือแนวนอน",
|
||||
"cat": "Minigame 3"
|
||||
},
|
||||
{
|
||||
"n": 69,
|
||||
"name": "UI ห้องเกม (LobbyA) มือถือแนวนอน — ย่อให้พอดี",
|
||||
"detail": "room-lobby ไม่มี landscape media query → chat panel(576x525), ปุ่ม voice/แว่น/ถ้วย(80px), AI chat ล้น/ใหญ่บนแนวนอน. เพิ่ม @media(landscape+max-height:540px) ย่อตามความสูงจอ (อิง Main-Lobby pattern)",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "✅ แก้แล้ว 2026-06-25 room-lobby.css?v=0.0177 — chat/BR buttons/AI/ready/exit · overlay หลักฐาน-อันดับ (inline/JS) ยังต้องทำแยก",
|
||||
"cat": "LobbyB"
|
||||
},
|
||||
{
|
||||
"n": 70,
|
||||
"name": "การ์ดรางวัลหลักฐาน มือถือใหญ่เกิน กดปุ่มไม่ได้",
|
||||
"detail": "หน้าโชว์การ์ดรางวัล \"คุณได้รับหลักฐาน!\" (หลังจบมินิเกมสืบสวน) บนมือถือการ์ดใหญ่มากจนเนื้อหา+ปุ่ม \"เก็บเข้าแฟ้ม\" หลุดล่างจอ กดปิดไม่ได้",
|
||||
"prio": "สูง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod play.js?v=0.006250402): ทำ overlay เป็น scroll container + inner margin:auto (กัน flexbox clip ด้านบนเวลาเนื้อหาสูงเกินจอ) + คุมขนาดการ์ด/หัวข้อ/ปุ่มด้วย clamp ตาม viewport (การ์ด max-width min(72vw,360px) + max-height 52vh, ปุ่ม font/padding clamp). มือถือแนวตั้งเลื่อนถึงปุ่มได้เสมอ. ไฟล์: Game/public/js/play.js (showInSceneEvidenceReveal).",
|
||||
"cat": "ระบบ disconnect"
|
||||
},
|
||||
{
|
||||
"n": 71,
|
||||
"name": "หน้าผลชนะ (โพเดียม) ตัวละครลอย ไม่ยืนบนพื้น",
|
||||
"detail": "หน้า \"ยินดีด้วย ทีมนักสืบ ชนะ!\" ตัวละครผู้ชนะลอยอยู่กลางอากาศ ไม่ยืนบนพื้นเวที",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0312): พื้นหลัง win-bg เดิม center/cover ทำให้พื้นเวทีถูก crop เลื่อน + podium ตั้ง bottom:9% ตัวละครเลยลอย. แก้เป็น (1) ปักพื้นหลังไว้ขอบล่าง background-position:center bottom -> พื้นเวทีติดขอบล่างจอเสมอทุกสัดส่วน (2) ลด podium bottom 9% -> 2% ให้ตัวละครยืนบนพื้น. ไฟล์: Game/public/js/room-lobby.js (injectFinalResultStyle, #trial-final-podium).",
|
||||
"cat": "อื่นๆ"
|
||||
},
|
||||
{
|
||||
"n": 72,
|
||||
"name": "ป๊อปอัปขอสิทธิ์ไมค์ เด้งทุกครั้งที่เข้า Lobby",
|
||||
"detail": "หน้าขอสิทธิ์ไมโครโฟน (mic-permission-overlay) เด้งขึ้นทุกครั้งที่เข้า LobbyA/LobbyB น่ารำคาญ ทั้งที่เบราว์เซอร์จำสิทธิ์ให้แล้ว",
|
||||
"prio": "กลาง",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "แก้แล้ว (deploy prod room-lobby.js?v=0.0313): เดิม overlay เป็น display:flex default จึงเด้งทุกครั้งที่โหลดหน้า. เพิ่มตรวจสิทธิ์ผ่าน navigator.permissions.query(microphone) + จำสถานะใน localStorage (justiceMicPromptHandled). แสดงเฉพาะครั้งแรกที่ยังไม่เคยตัดสินใจ (permission=prompt และยังไม่เคยกดอนุญาต/ปิด); ถ้า granted แล้ว/เคยจัดการแล้ว → ซ่อนทันที (มี inline script กันแฟลชก่อน paint). ปุ่ม 🎤 ขอสิทธิ์ไมค์ ในห้องยังใช้ขอใหม่ได้. ไฟล์: room-lobby.html (inline gate) + room-lobby.js (gateMicPermissionOverlay, hideMicPermissionOverlay).",
|
||||
"cat": "LobbyA"
|
||||
},
|
||||
{
|
||||
"n": 73,
|
||||
"name": "คนออก LobbyB / ช่วงเปลี่ยนหน้า → ไม่เติมบอท + ค้างรอผู้เล่น เริ่มเกมต่อไม่ได้",
|
||||
"detail": "คนหลุดระหว่างนั่งอยู่ LobbyB หรือช่วงเปลี่ยนหน้า minigame→LobbyB เดิมไม่ถูกเติมบอทแทน + ยังถูกนับใน roster คดี → host กดเริ่มสืบสวนรอบต่อไปไม่ได้ ค้าง รอผู้เล่น (x/y) ตลอดไป",
|
||||
"prio": "วิกฤต",
|
||||
"status": "แก้ไขแล้ว",
|
||||
"updated": "2026-06-25",
|
||||
"note": "ขยาย grace+bot-takeover gate ให้ครอบ LobbyB/คดีล็อก ไม่ใช่แค่ minigame active (server.js ~8921: inCaseFlowForBot = detectiveMinigameActive || serverMapIsPostCaseLobbyB || (joinLocked && caseParticipantNicknames.size>0)). คนออก→หลัง grace 10วิ ลบจาก roster + เติมบอทสีตามคนออก + presence ใหม่ → allHere=true เล่นต่อได้. + ส่ง botSlotCount ใน lobby-tint-sync (server.js ~121) + client อัปเดต lobbyBotSlotCount ก่อน render (room-lobby.js ~768) ให้บอทโผล่ทันทีไม่ต้อง re-join. v0.0323",
|
||||
"cat": "ระบบ disconnect"
|
||||
}
|
||||
]
|
||||
@@ -162,10 +162,10 @@
|
||||
"at": "2026-06-25 08:44"
|
||||
},
|
||||
"12": {
|
||||
"result": "none",
|
||||
"result": "pass",
|
||||
"comment": "⚠️ ไม่สามารถ runtime test ได้ (canvas freeze CDP) — ต้องทดสอบ manual: MG3 ต่อตึก เล่นทีละคนตามคิว",
|
||||
"images": [],
|
||||
"at": "2026-06-25 08:45"
|
||||
"at": "2026-06-25 16:35"
|
||||
},
|
||||
"36": {
|
||||
"result": "pass",
|
||||
|
||||
Reference in New Issue
Block a user