update join room
This commit is contained in:
@@ -38,34 +38,36 @@
|
||||
"providerUserId": "p_1775109142385_wq7wfy1p32j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 815,
|
||||
"coins": 897,
|
||||
"createdAt": "2026-04-02T05:52:21+00:00",
|
||||
"updatedAt": "2026-06-18T15:59:38+00:00",
|
||||
"updatedAt": "2026-06-19T07:10:26+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781197200000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 0
|
||||
"lockUntilMs": 1781888400000
|
||||
},
|
||||
"score": 730,
|
||||
"score": 792,
|
||||
"scoreByCase": {
|
||||
"1": 10,
|
||||
"10": 310,
|
||||
"1": 16,
|
||||
"10": 320,
|
||||
"8": 220,
|
||||
"11": 20,
|
||||
"13": 20,
|
||||
"13": 40,
|
||||
"14": 20,
|
||||
"9": 40,
|
||||
"12": 60,
|
||||
"15": 20,
|
||||
"4": 10
|
||||
"4": 10,
|
||||
"5": 20,
|
||||
"7": 6
|
||||
},
|
||||
"lbName": "Q",
|
||||
"achievements": {
|
||||
@@ -158,14 +160,36 @@
|
||||
{
|
||||
"id": "2613daea57c76f13ac9197ec",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"displayName": "Pear",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1780400804441_lsc3lqqvs0i",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"coins": 16,
|
||||
"createdAt": "2026-06-02T11:46:46+00:00",
|
||||
"updatedAt": "2026-06-02T11:46:46+00:00"
|
||||
"updatedAt": "2026-06-19T07:10:26+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
},
|
||||
"score": 6,
|
||||
"scoreByCase": {
|
||||
"1": 4,
|
||||
"7": 2
|
||||
},
|
||||
"lbName": "Pear",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "9404ea306bffed187b43a490",
|
||||
@@ -193,6 +217,227 @@
|
||||
},
|
||||
"createdAt": "2026-06-17T08:22:51+00:00",
|
||||
"updatedAt": "2026-06-17T08:22:51+00:00"
|
||||
},
|
||||
{
|
||||
"id": "c3f2affa3fc67b2411992cdf",
|
||||
"email": "",
|
||||
"displayName": "Nutty",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781849622720_33zppelly7r",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 12,
|
||||
"createdAt": "2026-06-19T06:13:44+00:00",
|
||||
"updatedAt": "2026-06-19T07:10:26+00:00",
|
||||
"score": 12,
|
||||
"scoreByCase": {
|
||||
"1": 12
|
||||
},
|
||||
"lbName": "Nutty",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "71f99e98ffa2e824b225af7c",
|
||||
"email": "",
|
||||
"displayName": "tomato",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781849642042_x2vt68wscm",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 136,
|
||||
"createdAt": "2026-06-19T06:14:02+00:00",
|
||||
"updatedAt": "2026-06-19T07:49:00+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
},
|
||||
"score": 126,
|
||||
"scoreByCase": {
|
||||
"1": 66,
|
||||
"7": 10,
|
||||
"5": 10,
|
||||
"2": 40
|
||||
},
|
||||
"lbName": "tomato",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cdf90c44df43f374c61afe24",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781849653038_swv8lg8jlm",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 10,
|
||||
"createdAt": "2026-06-19T06:14:13+00:00",
|
||||
"updatedAt": "2026-06-19T06:14:35+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "237fe56525ecb6a41d2831ee",
|
||||
"email": "",
|
||||
"displayName": "Chupa Chup",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781849770177_lylmhvm1lp",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 94,
|
||||
"createdAt": "2026-06-19T06:16:09+00:00",
|
||||
"updatedAt": "2026-06-19T07:33:03+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
},
|
||||
"score": 84,
|
||||
"scoreByCase": {
|
||||
"1": 76,
|
||||
"7": 8
|
||||
},
|
||||
"lbName": "Zara Kissy",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c762e08534c6c99a24a49dbe",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781850202249_347o67u9vhq",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 10,
|
||||
"createdAt": "2026-06-19T06:23:24+00:00",
|
||||
"updatedAt": "2026-06-19T06:24:28+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a5e5d250871a8fdc6988bb83",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781850663247_l0g0d1r7pcm",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"createdAt": "2026-06-19T06:31:03+00:00",
|
||||
"updatedAt": "2026-06-19T06:31:03+00:00"
|
||||
},
|
||||
{
|
||||
"id": "0bf4718eeec4434c080d6057",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781851275186_szcmp5pu3s",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 10,
|
||||
"createdAt": "2026-06-19T06:41:15+00:00",
|
||||
"updatedAt": "2026-06-19T07:00:09+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781802000000,
|
||||
"claimedDays": [
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781888400000
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6976f001d7a7eb32c2fd0171",
|
||||
"email": "",
|
||||
"displayName": "MONE",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781852432483_mfrw1xmw0j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 4,
|
||||
"createdAt": "2026-06-19T07:00:33+00:00",
|
||||
"updatedAt": "2026-06-19T07:10:26+00:00",
|
||||
"score": 4,
|
||||
"scoreByCase": {
|
||||
"7": 4
|
||||
},
|
||||
"lbName": "MONE",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "689a199e3291ae6cc2c8bc05",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781855252958_jds3qm3aebt",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"createdAt": "2026-06-19T07:47:33+00:00",
|
||||
"updatedAt": "2026-06-19T07:47:33+00:00"
|
||||
},
|
||||
{
|
||||
"id": "bb6f0d73632880703c833ad5",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "p_1781856969270_oupok32bkh9",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"createdAt": "2026-06-19T08:16:09+00:00",
|
||||
"updatedAt": "2026-06-19T08:16:09+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -89,19 +89,13 @@
|
||||
"balloonBossPlayerBalloonFallbackUrl": "/Game/img/MegaVirus/Artboard%209.png",
|
||||
"balloonBossBalloonsPerPlayer": 3,
|
||||
"forcedMinigameKeys": [
|
||||
"quiz",
|
||||
"gauntlet",
|
||||
"stack"
|
||||
"quiz_carry",
|
||||
"space_shooter",
|
||||
"balloon_boss"
|
||||
],
|
||||
"testSpecialCardByMap": {
|
||||
"mng8a80o": 1,
|
||||
"mno9kb07": 2,
|
||||
"mnn93hpi": 3,
|
||||
"mnorwqx1": 4,
|
||||
"mnptfts2": 5,
|
||||
"mnpz6rkp": 6,
|
||||
"mnq1eml7": 7
|
||||
},
|
||||
"testSpecialCardByMap": {},
|
||||
"troublesomeForceOffer": false,
|
||||
"specialQuizIconExpireSec": 20
|
||||
"specialQuizIconExpireSec": 20,
|
||||
"pickVoteSec": 25,
|
||||
"trialVoteSec": 30
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"qbroom1":[{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781171902962},{"name":"ผู้เล่น8916","score":4,"characterId":"char-1777017632279","ts":1781074083031},{"name":"ผู้เล่น2821","score":4,"characterId":"char-1777017632279","ts":1781079179108}],"qbroom2":[{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781175468983},{"name":"ผู้เล่น2612","score":1,"characterId":"char-1777017632279","ts":1781079252376}],"qbroom3":[{"name":"Q","score":9,"characterId":"char-1777017632279","ts":1781175654893}],"qbroom5":[{"name":"Q","score":6,"characterId":"char-1777017632279","ts":1781675299850}],"qbroom6":[{"name":"Q","score":3,"characterId":"char-1777017632279","ts":1781675383175}],"qbroom9":[{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781696345322}]}
|
||||
{"qbroom1":[{"name":"ผู้เล่น9514","score":17,"characterId":"char-1777017632279","ts":1781850667717},{"name":"MONE","score":10,"characterId":"char-1777017632279","ts":1781850749437},{"name":"tomato","score":9,"characterId":"char-1777017632279","ts":1781850748552},{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781171902962},{"name":"ผู้เล่น1991","score":5,"characterId":"char-1777017632279","ts":1781850950078},{"name":"ผู้เล่น8916","score":4,"characterId":"char-1777017632279","ts":1781074083031},{"name":"ผู้เล่น2821","score":4,"characterId":"char-1777017632279","ts":1781079179108},{"name":"Weenwild","score":4,"characterId":"char-1777017632279","ts":1781849863580}],"qbroom2":[{"name":"ผู้เล่น4472","score":13,"characterId":"char-1777017632279","ts":1781851033914},{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781175468983},{"name":"ผู้เล่น2612","score":1,"characterId":"char-1777017632279","ts":1781079252376}],"qbroom3":[{"name":"Q","score":9,"characterId":"char-1777017632279","ts":1781175654893},{"name":"Weenwild","score":2,"characterId":"char-1777017632279","ts":1781849898452}],"qbroom5":[{"name":"Q","score":6,"characterId":"char-1777017632279","ts":1781675299850}],"qbroom6":[{"name":"Q","score":3,"characterId":"char-1777017632279","ts":1781675383175}],"qbroom9":[{"name":"Q","score":7,"characterId":"char-1777017632279","ts":1781696345322}]}
|
||||
@@ -566,9 +566,28 @@
|
||||
|
||||
function applyCustomizeSelection() {
|
||||
var characterId = getStoredCharacterId();
|
||||
if (!characterId) return Promise.resolve(null);
|
||||
return new Promise(function (resolve) {
|
||||
function runApply() {
|
||||
// ไม่มีตัวละคร (เครื่องที่ยังไม่ได้เลือกตัวละคร) — ยังต้องซิงก์สีไปเซิร์ฟเวอร์
|
||||
// ให้คนอื่นเห็น แต่ข้ามการ composite รูป preview เพราะไม่มี characterId
|
||||
if (!characterId) {
|
||||
try {
|
||||
persistConfirmedState();
|
||||
syncLobbyTintStorageKeys();
|
||||
} catch (e0) { /* ignore */ }
|
||||
try {
|
||||
window.dispatchEvent(new CustomEvent('customize-popup:applied', {
|
||||
detail: {
|
||||
characterId: '',
|
||||
activeColor: clampIndex(state.activeColor, 1, 8),
|
||||
activeSkin: clampIndex(state.activeSkin, 1, 3),
|
||||
avatarSrc: ''
|
||||
}
|
||||
}));
|
||||
} catch (e1) { /* ignore */ }
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
var tint = getSelectedTint();
|
||||
var domAvatar = resolveDomAvatarSource();
|
||||
var fallbackSrc = domAvatar.src || '';
|
||||
@@ -1068,6 +1087,12 @@
|
||||
function closePopup() {
|
||||
if (!refs.overlay) return;
|
||||
stopScrollbarDrag();
|
||||
/* ย้าย focus ออกจาก overlay ก่อนซ่อน — กันคำเตือน "aria-hidden on focused element" (เช่นปุ่มยืนยันที่เพิ่งกด) */
|
||||
try {
|
||||
if (refs.overlay.contains(document.activeElement) && document.activeElement.blur) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
refs.overlay.classList.add('is-hidden');
|
||||
refs.overlay.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
@@ -1124,9 +1149,27 @@
|
||||
});
|
||||
}
|
||||
|
||||
/** ซ่อมรูป avatar ที่ถูก "อบ" (bake) ไว้ผิดสีจากโค้ดเวอร์ชันเก่า (heuristic = ชุดขาว+ขอบสี)
|
||||
→ คอมโพสิตใหม่จากเลเยอร์จริงด้วยสีที่บันทึกไว้ แล้วเขียนทับ bake เดิม (self-heal ทุกเครื่อง) */
|
||||
function rebakeAvatarFromSavedColors() {
|
||||
if (isRoomLobbyPage()) return; // room-lobby มีระบบ tint ของตัวเองอยู่แล้ว
|
||||
var characterId = getStoredCharacterId();
|
||||
if (!characterId) return;
|
||||
var hasSavedColor = false;
|
||||
try { hasSavedColor = !!localStorage.getItem('lobbyThemeColor'); } catch (e) { /* ignore */ }
|
||||
if (!hasSavedColor) return; // ยังไม่เคยเลือกสี → คงรูป default ไว้ ไม่ไปอบทับ
|
||||
readLobbyTintIntoState();
|
||||
composeTintedCharacterPreview(characterId, getSelectedTint()).then(function (outSrc) {
|
||||
if (!outSrc || typeof outSrc !== 'string' || outSrc.indexOf('data:image/') !== 0) return; // composite ไม่สำเร็จ → ไม่เขียนทับด้วย fallback
|
||||
try { localStorage.setItem(LOBBY_IDLE_DOWN_PREFIX + characterId, outSrc); } catch (e) { /* ignore */ }
|
||||
syncLiveAvatarElements(outSrc, 'ตัวละครที่ปรับแต่งแล้ว');
|
||||
}).catch(function () { /* ignore */ });
|
||||
}
|
||||
|
||||
function init() {
|
||||
createPopupIfNeeded();
|
||||
if (triggerSelectors.length) bindTriggers();
|
||||
rebakeAvatarFromSavedColors();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
|
||||
@@ -2436,6 +2436,21 @@
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
/** วินิจฉัยสีในเกม: รันใน console บนแต่ละเครื่อง → เทียบสีที่ render ของทุกตัว */
|
||||
window.__colorDebug = function () {
|
||||
var rows = [];
|
||||
rows.push({ who: '*ME*', id: String(myId == null ? '' : myId).slice(0, 6), renderTint: JSON.stringify(me && me.playTint) });
|
||||
others.forEach(function (o, id) {
|
||||
rows.push({ who: isPreviewBotId(id) ? 'BOT' : 'peer', id: String(id).slice(0, 10), name: o.nickname || '', renderTint: JSON.stringify(o.playTint) });
|
||||
});
|
||||
try { console.table(rows); } catch (e) { console.log(JSON.stringify(rows, null, 2)); }
|
||||
var lsTheme = '?'; var lsTint = '?';
|
||||
try { lsTheme = localStorage.getItem('lobbyThemeColor'); lsTint = localStorage.getItem(LOBBY_PLAYER_TINT_KEY); } catch (e) { /* ignore */ }
|
||||
console.log('[color-dbg/game] myId=' + myId + ' playLobbyBotThemes=' + JSON.stringify(playLobbyBotThemes) +
|
||||
' LS.lobbyThemeColor=' + lsTheme + ' LS.PLAYER_TINT=' + lsTint);
|
||||
return rows;
|
||||
};
|
||||
|
||||
function getPlayTintForMissionAvatar(peerId) {
|
||||
const sid = peerId != null ? String(peerId) : '';
|
||||
if (myId != null && sid === String(myId)) {
|
||||
@@ -3194,6 +3209,24 @@
|
||||
let playLiveQuizScores = {};
|
||||
/** เกมถูก/ผิด (multiplayer): ใครเคยตอบผิดในเซสชันนี้ — ใช้โชว์แสตมป์ในสรุปภารกิจ mng8a80o */
|
||||
let playQuizEverWrong = {};
|
||||
/** MG1: กันสั่งจบซ้ำเมื่อตรวจพบ "ตายหมด" (รวมบอท ฝั่ง client) */
|
||||
let mg1AllDeadEndScheduled = false;
|
||||
/** MG1: ตรวจว่าทุกคน(เรา+บอท+ผู้เล่นอื่น)ถูกล็อกตอบผิดหมด → จบเกมทันที (เฉพาะโหมดสืบสวน) */
|
||||
function checkMg1AllDeadAndEnd() {
|
||||
if (mg1AllDeadEndScheduled || previewMode) return;
|
||||
if (!isQuiz() || !isDetectiveMinigamePlay()) return;
|
||||
const meLocked = !!(playQuizPlayerLocal && playQuizPlayerLocal.cannotTrue && playQuizPlayerLocal.cannotFalse);
|
||||
if (!meLocked) return;
|
||||
let anyAlive = false;
|
||||
others.forEach(function (o, id) {
|
||||
if (!o || o.bannedSpectator) return;
|
||||
if (!playBotsEnabled() && isPreviewBotId(id)) return;
|
||||
if (!(o.quizCannotTrue && o.quizCannotFalse)) anyAlive = true;
|
||||
});
|
||||
if (anyAlive) return;
|
||||
mg1AllDeadEndScheduled = true;
|
||||
setTimeout(function () { try { tryFinishDetectiveMinigameAndReturnLobby(); } catch (e) { /* ignore */ } }, 1600);
|
||||
}
|
||||
/** ผีตามตัวเมื่อตอบผิด — ใช้เฉพาะแมป mng8a80o (เป็นสัญลักษณ์/บทลงโทษเชิงสนุก) */
|
||||
const QUIZ_WRONG_GHOST_URL = BASE + '/img/QUESTION/ghost/ghost-front.png';
|
||||
let quizWrongGhostImg = null;
|
||||
@@ -3588,7 +3621,7 @@
|
||||
playLiveQuizScores = {};
|
||||
playLiveQuizScores[myId] = 0;
|
||||
others.forEach((_, id) => { playLiveQuizScores[id] = 0; });
|
||||
if (isQuiz()) playQuizEverWrong = {};
|
||||
if (isQuiz()) { playQuizEverWrong = {}; mg1AllDeadEndScheduled = false; }
|
||||
renderPlayQuizScoreboard(playLiveQuizScores);
|
||||
}
|
||||
|
||||
@@ -18876,6 +18909,7 @@
|
||||
spawnQuizTrueFalseScorePopupPlay(!!r.correctTrue);
|
||||
}
|
||||
showPlayQuizFeedback(r);
|
||||
checkMg1AllDeadAndEnd();
|
||||
});
|
||||
|
||||
socket.on('quiz-player-state', (st) => {
|
||||
@@ -18888,6 +18922,7 @@
|
||||
score: typeof st.score === 'number' ? st.score : (playQuizPlayerLocal.score || 0),
|
||||
};
|
||||
if (quizGhostConfineActivePlay()) warpMeIntoQuizGhostZonePlay();
|
||||
checkMg1AllDeadAndEnd();
|
||||
});
|
||||
|
||||
socket.on('quiz-ended', () => {
|
||||
|
||||
@@ -603,7 +603,7 @@
|
||||
return parseLobbyThemeIndex(p.lobbyColorThemeIndex) || parseLobbyThemeIndex(p.colorThemeIndex);
|
||||
}
|
||||
|
||||
function getLobbyTakenThemeIndices(excludePeerId) {
|
||||
function getLobbyTakenThemeIndices(excludePeerId, humanOnly) {
|
||||
var taken = [];
|
||||
var seen = {};
|
||||
peers.forEach(function (p, id) {
|
||||
@@ -611,10 +611,12 @@
|
||||
var ti = peerLobbyThemeIndex(p);
|
||||
if (ti && !seen[ti]) { seen[ti] = true; taken.push(ti); }
|
||||
});
|
||||
lobbyBots.forEach(function (b) {
|
||||
var ti = peerLobbyThemeIndex(b);
|
||||
if (ti && !seen[ti]) { seen[ti] = true; taken.push(ti); }
|
||||
});
|
||||
if (!humanOnly) {
|
||||
lobbyBots.forEach(function (b) {
|
||||
var ti = peerLobbyThemeIndex(b);
|
||||
if (ti && !seen[ti]) { seen[ti] = true; taken.push(ti); }
|
||||
});
|
||||
}
|
||||
return taken;
|
||||
}
|
||||
|
||||
@@ -623,7 +625,8 @@
|
||||
if (!ti) return false;
|
||||
var myIdx = getMyLobbyThemeIndex();
|
||||
if (forSelfChange && myIdx === ti) return true;
|
||||
return getLobbyTakenThemeIndices(forSelfChange ? socket.id : null).indexOf(ti) < 0;
|
||||
/* บอทไม่บล็อก: ถ้าสีถูกบอทยึดอยู่ ยังเลือกได้ (server จะดันบอทไปสีอื่นให้) — บล็อกเฉพาะสีที่ "คนจริง" ใช้ */
|
||||
return getLobbyTakenThemeIndices(forSelfChange ? socket.id : null, true).indexOf(ti) < 0;
|
||||
}
|
||||
|
||||
function applyThemeIndexToOccupant(target, themeIndex, skinIndex, done) {
|
||||
@@ -672,6 +675,29 @@
|
||||
tick();
|
||||
}
|
||||
|
||||
/** วินิจฉัยสี: รันใน console บนแต่ละเครื่อง → เทียบ index/สี ของทุกตัว เพื่อหาจุดที่ไม่ตรง */
|
||||
window.__colorDebug = function () {
|
||||
var rows = [];
|
||||
peers.forEach(function (p, id) {
|
||||
rows.push({
|
||||
who: id === socket.id ? '*ME*' : 'peer',
|
||||
id: String(id).slice(0, 6),
|
||||
name: p.nickname || '',
|
||||
serverIdx: p.lobbyColorThemeIndex || null,
|
||||
skinIdx: p.lobbySkinToneIndex || null,
|
||||
renderRGB: (id === socket.id ? (myTintTheme || '') : (p.colorTheme || '(none)')),
|
||||
});
|
||||
});
|
||||
(lobbyBots || []).forEach(function (b, i) {
|
||||
rows.push({ who: 'BOT', id: String(b.id || ('bot' + i)).slice(0, 10), name: b.nickname || '', serverIdx: peerLobbyThemeIndex(b), skinIdx: b.lobbySkinToneIndex || null, renderRGB: b.colorTheme || '(none)' });
|
||||
});
|
||||
try { console.table(rows); } catch (e) { console.log(JSON.stringify(rows, null, 2)); }
|
||||
console.log('[color-dbg/lobby] mySocket=' + socket.id + ' myTintTheme=' + myTintTheme + ' myTintSkin=' + myTintSkin +
|
||||
' botThemesFromServer=' + JSON.stringify(lobbyBotThemesFromServer) +
|
||||
' LS.lobbyThemeColor=' + (function () { try { return localStorage.getItem('lobbyThemeColor'); } catch (e) { return '?'; } })());
|
||||
return rows;
|
||||
};
|
||||
|
||||
function applyLobbyTintSyncPayload(data) {
|
||||
if (!data) return;
|
||||
if (Array.isArray(data.lobbyBotThemes)) lobbyBotThemesFromServer = data.lobbyBotThemes;
|
||||
@@ -4576,6 +4602,14 @@
|
||||
socket.off('user-joined');
|
||||
socket.on('user-joined', async (data) => {
|
||||
peers.set(data.id, normalizeLobbyPeerFromServer(data, mapData));
|
||||
/* แก้สีชุด peer ใหม่ไม่ขึ้นบนเครื่องคนอื่น: resolve RGB ของธีมสีทันที
|
||||
(lobby-tint-sync ถูก emit ก่อน user-joined จึงข้าม peer นี้เพราะยังไม่อยู่ใน map) */
|
||||
applyPeerThemeFromServer(peers.get(data.id), data.lobbyColorThemeIndex, data.lobbySkinToneIndex, function () {
|
||||
updatePlayersHud();
|
||||
renderPeers();
|
||||
redrawLobbyMap();
|
||||
if (typeof window.refreshCustomizeColorRow === 'function') window.refreshCustomizeColorRow();
|
||||
});
|
||||
updatePlayersHud();
|
||||
renderPeers();
|
||||
ensureReadyControlEnabled();
|
||||
|
||||
@@ -5124,7 +5124,7 @@
|
||||
#special-quiz-overlay .sq-ov-result--fail { color: #fff; background: rgba(247,118,142,0.9); }
|
||||
#special-quiz-overlay #sq-ov-quiz-body.is-hidden { display: none; }
|
||||
/* ===== หน้ารับการ์ด (mock 10-Game-1-result) — เต็มจอ ไม่มีกรอบควิซ ===== */
|
||||
#special-quiz-overlay.is-award .sq-ov-panel { background: none !important; padding: 2vh 4vw !important; min-height: 0 !important; box-shadow: none; }
|
||||
#special-quiz-overlay.is-award .sq-ov-panel { background: none !important; padding: 2vh 4vw !important; min-height: 0 !important; width: auto !important; max-width: 96vw !important; aspect-ratio: auto !important; max-height: 96vh !important; overflow: visible !important; box-shadow: none; }
|
||||
#special-quiz-overlay .sq-ov-award { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; }
|
||||
#special-quiz-overlay .sq-ov-award.is-hidden { display: none; }
|
||||
#special-quiz-overlay .sq-ov-award-stage { position: relative; display: flex; align-items: center; justify-content: center; }
|
||||
@@ -5205,7 +5205,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.006131839"></script>
|
||||
<script src="js/play.js?v=0.006131841"></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)
|
||||
|
||||
@@ -1615,9 +1615,9 @@
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/display-name.js?v=2"></script>
|
||||
<script src="js/version.js?v=0.0122"></script>
|
||||
<script src="js/customize-popup.js?v=31" data-customize-triggers="" data-customize-asset-base="img/03-5-Customize"></script>
|
||||
<script src="js/customize-popup.js?v=35" 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.0291"></script>
|
||||
<script src="js/room-lobby.js?v=0.0293"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+37
-5
@@ -1101,6 +1101,9 @@ function applySpecialCardImmediate(sid, space, card) {
|
||||
}
|
||||
|
||||
/* ===== คลังการ์ดพิเศษที่สะสมในเคสนี้ (ใช้ได้พร้อมกันทุกใบ ไม่ทับกัน) ===== */
|
||||
function dbgCards(space) {
|
||||
return JSON.stringify((space && space.pendingSpecialCards || []).map((e) => e.cardId + (e.consumed ? 'x' : '')));
|
||||
}
|
||||
function pendingSpecialCardList(space) {
|
||||
if (!Array.isArray(space.pendingSpecialCards)) space.pendingSpecialCards = [];
|
||||
return space.pendingSpecialCards;
|
||||
@@ -1110,9 +1113,10 @@ function addPendingSpecialCard(space, card) {
|
||||
if (!card || !card.cardId) return null;
|
||||
const list = pendingSpecialCardList(space);
|
||||
const cid = Number(card.cardId);
|
||||
if (list.some((e) => Number(e.cardId) === cid && !e.consumed)) return null; /* มีใบนี้อยู่แล้ว ไม่ซ้ำ */
|
||||
if (list.some((e) => Number(e.cardId) === cid && !e.consumed)) { console.log('[card-dbg] add skip dup card' + cid + ' list=' + dbgCards(space)); return null; }
|
||||
const entry = { cardId: cid, card: card, consumed: false };
|
||||
list.push(entry);
|
||||
console.log('[card-dbg] +card' + cid + ' list=' + dbgCards(space));
|
||||
return entry;
|
||||
}
|
||||
/** หาการ์ดในคลังตาม cardId ที่ยังไม่ถูกใช้ */
|
||||
@@ -1132,6 +1136,7 @@ function endSpecialQuizSession(sid, space, success) {
|
||||
sq.done = true;
|
||||
sq.session = null;
|
||||
let cardOut = null;
|
||||
console.log('[card-dbg] specialQuiz end: success=' + !!success + ' cardId=' + (sq.card && sq.card.cardId));
|
||||
if (success && sq.card && (sq.card.cardId || sq.card.imageUrl)) {
|
||||
space.specialCardAwardedThisRun = sq.card;
|
||||
cardOut = specialCardClientPayload(sq.card);
|
||||
@@ -3610,6 +3615,7 @@ function returnDetectiveSpaceToLobbyB(sid, space, message, awardOptions) {
|
||||
/* Card 3 Ban (when: lobby_next): ชนะการ์ดแบนรอบนี้ → "โหวตทันทีที่กลับ LobbyB หลังเกมจบ"
|
||||
ตั้ง bannedPlayerId ให้มินิเกมรอบถัดไปกันไม่ให้คนที่ถูกโหวตเล่น */
|
||||
const banPend = findPendingSpecialCard(space, 3);
|
||||
console.log('[card-dbg] lobby return: ban(3)=' + !!banPend + ' pickVote=' + !!space.pickVote + ' list=' + dbgCards(space));
|
||||
if (banPend && !space.pickVote) {
|
||||
banPend.consumed = true;
|
||||
/* ตั้ง flag — เริ่มโหวตตอนผู้เล่น "rejoin เข้า room-lobby" (ตอน return ผู้เล่นกำลัง navigate play.html→room-lobby.html อยู่ event จะหาย) */
|
||||
@@ -4032,6 +4038,7 @@ function advanceTestimony(sid, space) {
|
||||
space.silencedPlayerId = null;
|
||||
/* Card 4 Silence — โหวตเลือกผู้เล่น 1 คนให้ห้ามโหวต ก่อนเข้าโหวตจริง */
|
||||
const pend = findPendingSpecialCard(space, 4);
|
||||
console.log('[card-dbg] trial pre-vote: silence(4)=' + !!pend + ' list=' + dbgCards(space));
|
||||
if (pend) {
|
||||
pend.consumed = true;
|
||||
io.to(sid).emit('special-card-applied', { card: specialCardClientPayload(pend.card), context: 'silence' });
|
||||
@@ -5781,7 +5788,7 @@ const server = http.createServer((req, res) => {
|
||||
return;
|
||||
}
|
||||
if (url.startsWith(BASE_PATH + '/api/spaces')) {
|
||||
const parts = url.slice((BASE_PATH + '/api/spaces').length).split('/').filter(Boolean);
|
||||
const parts = url.slice((BASE_PATH + '/api/spaces').length).split('?')[0].split('#')[0].split('/').filter(Boolean);
|
||||
const spaceId = parts[0];
|
||||
if (req.method === 'GET' && !spaceId) {
|
||||
const now = Date.now();
|
||||
@@ -7381,7 +7388,16 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
socket.join(spaceId);
|
||||
socket.data.spaceId = spaceId;
|
||||
if (!space.hostId) space.hostId = socket.id;
|
||||
/* Host เสถียรข้ามการเปลี่ยนหน้า (lobby→play→lobbyB): จำ host ด้วย playerKey (คงที่ต่อ browser)
|
||||
ไม่ใช่ socket.id (เปลี่ยนทุกครั้งที่โหลดหน้าใหม่ → host สลับไปมาแบบสุ่ม) */
|
||||
const joinPlayerKey = (typeof playerKey === 'string' && /^[a-zA-Z0-9_-]{8,128}$/.test(playerKey)) ? playerKey : '';
|
||||
const prevHostId = space.hostId;
|
||||
if (joinPlayerKey && space.hostPlayerKey && joinPlayerKey === space.hostPlayerKey) {
|
||||
space.hostId = socket.id; /* host ตัวจริงกลับเข้ามา → คืนสถานะ host เสมอ */
|
||||
} else if (!space.hostId) {
|
||||
space.hostId = socket.id; /* ยังไม่มี host → คนนี้เป็น host (ชั่วคราวถ้ามี hostPlayerKey อยู่แล้ว) */
|
||||
if (!space.hostPlayerKey && joinPlayerKey) space.hostPlayerKey = joinPlayerKey;
|
||||
}
|
||||
if (serverMapIsPostCaseLobbyB(space)) initTroublesomeState(space);
|
||||
ensurePeerLobbyThemes(space);
|
||||
const mdJoin = (space.mapId && maps.get(space.mapId)) || space.mapData;
|
||||
@@ -7524,6 +7540,7 @@ io.on('connection', (socket) => {
|
||||
const sqIcon = specialQuizIconPayload(space);
|
||||
if (sqIcon) joinCb.specialQuizIcon = sqIcon;
|
||||
if (typeof cb === 'function') cb(joinCb);
|
||||
if (space.hostId !== prevHostId) io.to(spaceId).emit('host-changed', { hostId: space.hostId }); /* host เปลี่ยน (เช่น host ตัวจริงกลับเข้ามา) → sync ทุกคน */
|
||||
emitLobbyTintSync(spaceId, space);
|
||||
socket.to(spaceId).emit('user-joined', peer);
|
||||
if (space.voiceInits) {
|
||||
@@ -7665,8 +7682,20 @@ io.on('connection', (socket) => {
|
||||
if (!Number.isFinite(skinIndex) || skinIndex < 1 || skinIndex > LOBBY_SKIN_COUNT) {
|
||||
skinIndex = peer.lobbySkinToneIndex || 1;
|
||||
}
|
||||
const taken = getTakenLobbyThemeIndices(space, socket.id);
|
||||
if (taken.has(themeIndex)) return reply({ ok: false, error: 'สีเสื้อนี้มีคนใช้แล้ว' });
|
||||
/* ชนกับ "คนจริง" คนอื่น → ปฏิเสธ; ชนกับ "บอท" เท่านั้น → ดันบอทไปสีอื่น (ให้สอดคล้องกับตอน join)
|
||||
กันอาการ "เปลี่ยนสีไม่ได้" เมื่อบอทยึดสีไว้ */
|
||||
let humanTaken = false;
|
||||
space.peers.forEach((op, oid) => {
|
||||
if (oid === socket.id) return;
|
||||
if (parseInt(op.lobbyColorThemeIndex, 10) === themeIndex) humanTaken = true;
|
||||
});
|
||||
if (humanTaken) return reply({ ok: false, error: 'สีเสื้อนี้มีคนใช้แล้ว' });
|
||||
if (space.lobbyBotThemeBySlot && typeof space.lobbyBotThemeBySlot === 'object') {
|
||||
Object.keys(space.lobbyBotThemeBySlot).forEach((k) => {
|
||||
const slot = space.lobbyBotThemeBySlot[k];
|
||||
if (slot && parseInt(slot.themeIndex, 10) === themeIndex) slot.themeIndex = 0; /* syncLobbyBotThemeSlots จะหาสีใหม่ให้ */
|
||||
});
|
||||
}
|
||||
peer.lobbyColorThemeIndex = themeIndex;
|
||||
peer.lobbySkinToneIndex = skinIndex;
|
||||
emitLobbyTintSync(sid, space);
|
||||
@@ -7715,6 +7744,8 @@ io.on('connection', (socket) => {
|
||||
|| (space.isPrivate && String(space.spaceName || '').toLowerCase() === 'preview'));
|
||||
if (!isPreviewEditorRoom) return reply({ ok: false, error: 'ใช้ได้เฉพาะห้องทดสอบจากเอดิเตอร์' });
|
||||
space.hostId = socket.id;
|
||||
const claimPeer = space.peers.get(socket.id);
|
||||
if (claimPeer && claimPeer.playerKey) space.hostPlayerKey = claimPeer.playerKey;
|
||||
io.to(sid).emit('host-changed', { hostId: socket.id });
|
||||
return reply({ ok: true });
|
||||
});
|
||||
@@ -8325,6 +8356,7 @@ io.on('connection', (socket) => {
|
||||
const firstPeer = space.peers.values().next().value;
|
||||
if (firstPeer) {
|
||||
space.hostId = firstPeer.id;
|
||||
space.hostPlayerKey = firstPeer.playerKey || space.hostPlayerKey; /* host ออกจริง (ไม่ใช่เปลี่ยนหน้า) → ย้าย host ให้คนถัดไปแบบถาวร */
|
||||
io.to(sid).emit('host-changed', { hostId: firstPeer.id });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,6 @@
|
||||
|
||||
<script src="../app-base.js?v=2"></script>
|
||||
<script src="../Game/js/display-name.js?v=2"></script>
|
||||
<script src="join.js?v=0.006131120"></script>
|
||||
<script src="join.js?v=0.006131121"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -191,7 +191,8 @@
|
||||
});
|
||||
|
||||
btnPrivateJoin?.addEventListener('click', function () {
|
||||
var code = (idPrivate?.value || '').trim();
|
||||
/* รหัสห้องเป็นตัวพิมพ์ใหญ่เสมอ (ABCDEFGHJKLMNPQRSTUVWXYZ23456789) — บังคับ uppercase กันพิมพ์เล็กแล้วหาห้องไม่เจอ (404) */
|
||||
var code = (idPrivate?.value || '').trim().toUpperCase();
|
||||
var pass = (passPrivate?.value || '').trim();
|
||||
if (!code) {
|
||||
alert('กรุณากรอกรหัสห้อง');
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
<script src="../app-base.js?v=2"></script>
|
||||
<script src="../Game/js/display-name.js?v=2"></script>
|
||||
<script src="daily-popup.js?v=26" data-daily-trigger="#btn-daily" data-daily-asset-base="IMAGE/Daily" data-daily-test-reset-seconds="0"></script>
|
||||
<script src="../Game/js/customize-popup.js?v=31" data-customize-triggers="#btn-cloth" data-customize-asset-base="/Game/img/03-5-Customize"></script>
|
||||
<script src="../Game/js/customize-popup.js?v=35" data-customize-triggers="#btn-cloth" data-customize-asset-base="/Game/img/03-5-Customize"></script>
|
||||
<script src="../Game/js/achievements.js?v=0.002" data-asset-base="/Game/img/03-6-Profile"></script>
|
||||
<script src="../Game/js/profile-popup.js?v=7" data-profile-trigger="#btn-myprofile" data-profile-asset-base="/Game/img/03-6-Profile"></script>
|
||||
<script src="lobby.js?v=0.0192"></script>
|
||||
|
||||
Reference in New Issue
Block a user