diff --git a/www/html/Admin/private/store.json b/www/html/Admin/private/store.json index a63026d..a33b7db 100644 --- a/www/html/Admin/private/store.json +++ b/www/html/Admin/private/store.json @@ -38,9 +38,9 @@ "providerUserId": "p_1775109142385_wq7wfy1p32j", "notes": "auto: player-coins", "blocked": false, - "coins": 110, + "coins": 180, "createdAt": "2026-04-02T05:52:21+00:00", - "updatedAt": "2026-06-12T08:29:33+00:00", + "updatedAt": "2026-06-14T14:25:05+00:00", "daily": { "anchorMs": 1781197200000, "claimedDays": [ @@ -52,13 +52,13 @@ false, false ], - "lockUntilMs": 1781283600000 + "lockUntilMs": 0 }, - "score": 100, + "score": 170, "scoreByCase": { "1": 10, "10": 70, - "8": 20 + "8": 90 }, "lbName": "Q" }, diff --git a/www/html/Game/public/img/Jumper/mission-complete.png b/www/html/Game/public/img/Jumper/mission-complete.png new file mode 100644 index 0000000..0b15d97 Binary files /dev/null and b/www/html/Game/public/img/Jumper/mission-complete.png differ diff --git a/www/html/Game/public/img/ViolentCrime/mission-complete.png b/www/html/Game/public/img/ViolentCrime/mission-complete.png new file mode 100644 index 0000000..0b15d97 Binary files /dev/null and b/www/html/Game/public/img/ViolentCrime/mission-complete.png differ diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 208880a..ede1ae7 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -36,6 +36,8 @@ const previewMode = params.get('preview') === '1'; /* Card 3 Ban — ผู้ถูกแบนรอบนี้: เข้ามาดูเพื่อนเล่นได้ แต่ควบคุมตัวละคร/ทำแอ็กชันไม่ได้ (รอบหน้าเล่นปกติ) */ const playBannedSpectator = params.get('banned') === '1'; + /* Card 3 Ban (กรณีโหวตแบน "บอท") — slot ของ __pv_bot_N ที่ถูกแบน → ไม่ spawn ลงเล่นรอบนี้ (-1 = ไม่มี) */ + const playBannedBotSlot = (() => { const v = parseInt(params.get('banBot'), 10); return Number.isFinite(v) && v >= 0 ? v : -1; })(); /* shield โปร่งใสคลุมทั้งจอ (z 200) กิน pointer/touch ทุกอย่าง → ปุ่มเกม(jump z59/drop z60/joystick/shoot)+canvas กดไม่ได้ แต่ต่ำกว่า modal (z 10000+) เลย special quiz / สรุปผล ยังกดได้ปกติ + ป้ายบอกสถานะด้านบน */ function setupBannedSpectatorUi() { @@ -5003,7 +5005,12 @@ function rebalancePreviewBots() { if (!playBotsEnabled() || !mapData) return; const human = countPlayHumans(); - const wantBots = Math.max(0, playBotTargetHeadcount() - human); + let wantBots = Math.max(0, playBotTargetHeadcount() - human); + /* Card 3 Ban — บอทที่ถูกแบนรอบนี้ (slot N) ไม่ลงเล่น: ลบทิ้ง + ลดเป้าจำนวนลง 1 + ข้าม slot นั้นตอนสร้าง */ + if (playBannedBotSlot >= 0) { + others.delete(PREVIEW_BOT_PREFIX + playBannedBotSlot); + wantBots = Math.max(0, wantBots - 1); + } [...others.keys()].filter(isPreviewBotId).forEach((bid) => { const o = others.get(bid); if (!o) return; @@ -5043,7 +5050,7 @@ while (botIds.length < wantBots) { /* หา slot ว่างเล็กสุด (0,1,2,…) — IDX ของ ID ต้องตรงกับ playLobbyBotThemes[idx] เพื่อให้สีตรง lobby */ let nextSlot = 0; - while (others.has(PREVIEW_BOT_PREFIX + nextSlot)) nextSlot++; + while (others.has(PREVIEW_BOT_PREFIX + nextSlot) || nextSlot === playBannedBotSlot) nextSlot++; previewBotSeq = Math.max(previewBotSeq, nextSlot + 1); const id = PREVIEW_BOT_PREFIX + nextSlot; const joinIdx = countPlayHumans() + botIds.length; @@ -7454,6 +7461,8 @@ const top = rows.slice(0, 5); const ranked = top.map(function (row, idx) { const pos = idx + 1; + /* โบนัสอยู่รอดจนจบเกม = +20 (เฉพาะคนไม่ตาย) — รวมใน finalScore เหมือน rankBonus ของ MG2 */ + const surviveBonus = row.eliminated ? 0 : 20; return { id: row.id, nickname: row.nickname, @@ -7462,11 +7471,11 @@ eliminated: !!row.eliminated, rank: pos, rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos), - rankBonus: 0, - finalScore: row.baseScore, + rankBonus: surviveBonus, + finalScore: row.baseScore + surviveBonus, }; }); - const totalParts = ranked.map(function (r) { return r.baseScore; }); + const totalParts = ranked.map(function (r) { return r.finalScore; }); const totalSum = totalParts.reduce(function (s, n) { return s + n; }, 0); const n = ranked.length || 1; const averageScore = Math.floor(totalSum / n); @@ -7597,9 +7606,13 @@ return String(a.nickname).localeCompare(String(b.nickname), 'th') || String(a.id).localeCompare(String(b.id)); }); const top = baseRows.slice(0, 5); + /* โบนัสจบเกม: ฆ่าบอส(+100)→ทุกคนที่ร่วมเล่น · รอดชีวิต(+10)→เฉพาะคนไม่ตาย (รวมใน finalScore, clamp ≥0) */ + const endRBonus = mission && mission.balloonBossEndReason; + const killBonusAll = endRBonus === 'victory' ? 100 : 0; const ranked = top.map(function (row, idx) { const pos = idx + 1; const bs = Math.max(0, Number(row.baseScore) || 0); + const bonus = killBonusAll + (row.eliminated ? 0 : 10); return { id: row.id, nickname: row.nickname, @@ -7608,11 +7621,11 @@ eliminated: !!row.eliminated, rank: pos, rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos), - rankBonus: 0, - finalScore: bs, + rankBonus: bonus, + finalScore: Math.max(0, bs + bonus), }; }); - const totalParts = ranked.map(function (r) { return r.baseScore; }); + const totalParts = ranked.map(function (r) { return r.finalScore; }); const totalSum = totalParts.reduce(function (s, n) { return s + n; }, 0); const n2 = ranked.length || 1; const averageScore = Math.floor(totalSum / n2); @@ -7668,9 +7681,12 @@ || String(a.nickname).localeCompare(String(b.nickname), 'th') || String(a.id).localeCompare(String(b.id))); const top = rows.slice(0, 5); + /* โบนัสจบเกม: ฆ่าบอส(+100)→ทุกคนที่ร่วมเล่น · รอดชีวิต(+10)→เฉพาะคนไม่ตาย (รวมใน finalScore, clamp ≥0) */ + const killBonusAll = reason === 'victory' ? 100 : 0; const ranked = top.map((row, idx) => { const pos = idx + 1; const bs = Math.max(0, Number(row.baseScore) || 0); + const bonus = killBonusAll + (row.eliminated ? 0 : 10); return { id: row.id, nickname: row.nickname, @@ -7679,11 +7695,11 @@ eliminated: !!row.eliminated, rank: pos, rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos), - rankBonus: 0, - finalScore: bs, + rankBonus: bonus, + finalScore: Math.max(0, bs + bonus), }; }); - const totalParts = ranked.map((r) => r.baseScore); + const totalParts = ranked.map((r) => r.finalScore); const totalSum = totalParts.reduce((s, n) => s + n, 0); const n2 = ranked.length || 1; const averageScore = Math.floor(totalSum / n2); @@ -15154,7 +15170,7 @@ if (Number.isFinite(t) && t > 0 && t < 7200) return Math.floor(t); const g = Number(playSpaceShooterMissionTimeSec); if (Number.isFinite(g) && g > 0 && g < 7200) return Math.floor(g); - return 90; + return 60; /* สเปก MG6: เล่นภายใน 60 วินาที (default เมื่อ map/game-timing ไม่ได้ตั้งค่า) */ } function spaceShooterRemainingSecPlay() { @@ -16001,6 +16017,8 @@ vy: bvy, ownerId: bid, }); + /* สเปก MG7: บอทยิง 1 ครั้ง = −5 คะแนน เหมือนผู้เล่น (ต่ำสุด 0) */ + o.balloonBossScore = Math.max(0, (o.balloonBossScore | 0) - 5); } }); } @@ -16275,6 +16293,8 @@ vy, ownerId: myId, }); + /* สเปก MG7: ยิง 1 ครั้ง = −5 คะแนน (ต่ำสุด 0 ไม่ติดลบ) */ + me.balloonBossScore = Math.max(0, (me.balloonBossScore | 0) - 5); } stepBalloonBossPreviewBots(dt, mw, mh, bossC.cx, bossC.cy, ts); diff --git a/www/html/Game/public/js/room-lobby.js b/www/html/Game/public/js/room-lobby.js index 1a11b01..def50d2 100644 --- a/www/html/Game/public/js/room-lobby.js +++ b/www/html/Game/public/js/room-lobby.js @@ -6521,10 +6521,17 @@ if (lobbyLevelStr) q += '&lobbyLevel=' + encodeURIComponent(lobbyLevelStr); if (caseIdStr) q += '&case=' + encodeURIComponent(caseIdStr); if (data && data.detectiveMinigame) q += '&detectiveReturn=1'; - /* Card 3 Ban — ผู้ที่ถูกแบนรอบนี้: เข้าไปดูเพื่อนเล่นได้ แต่ควบคุมตัวละครไม่ได้ (รอบหน้าเล่นปกติ) */ - if (data && data.bannedPlayerId && data.bannedPlayerId === socket.id) { - q += '&banned=1'; - appendLobbySystemChat('— คุณถูกแบนรอบนี้ (Ban Card) · เข้าไปดูเพื่อนเล่นได้ แต่เล่นเองไม่ได้ · รอบหน้าเล่นได้ปกติ'); + /* Card 3 Ban — ผู้ที่ถูกแบนรอบนี้: เข้าไปดูเพื่อนเล่นได้ แต่ควบคุมตัวละครไม่ได้ (รอบหน้าเล่นปกติ) + ถ้าถูกแบนเป็น "บอท" (__lobby_bot_N) → ส่ง banBot=N ให้ play.js กันบอทตัวนั้นไม่ให้ลงเล่น (ทุก client ที่จำลองบอท) */ + if (data && data.bannedPlayerId) { + if (data.bannedPlayerId === socket.id) { + q += '&banned=1'; + appendLobbySystemChat('— คุณถูกแบนรอบนี้ (Ban Card) · เข้าไปดูเพื่อนเล่นได้ แต่เล่นเองไม่ได้ · รอบหน้าเล่นได้ปกติ'); + } else if (String(data.bannedPlayerId).indexOf('__lobby_bot_') === 0) { + var banBotSlot = parseInt(String(data.bannedPlayerId).slice('__lobby_bot_'.length), 10); + if (!isNaN(banBotSlot) && banBotSlot >= 0) q += '&banBot=' + banBotSlot; + appendLobbySystemChat('— บอทถูกแบนรอบนี้ (Ban Card) · ไม่ได้ลงเล่นมินิเกมรอบนี้'); + } } location.href = q; }); diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index db23be8..e1b066f 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -5180,7 +5180,7 @@ - +
v —