From 3d375adfaa5adff2b1adfbc1f1b7aaae5c3af204 Mon Sep 17 00:00:00 2001 From: giteaadmin Date: Sun, 14 Jun 2026 14:31:49 +0000 Subject: [PATCH] update card --- www/html/Admin/private/store.json | 10 ++-- .../public/img/Jumper/mission-complete.png | Bin 0 -> 4850 bytes .../img/ViolentCrime/mission-complete.png | Bin 0 -> 4850 bytes www/html/Game/public/js/play.js | 44 ++++++++++---- www/html/Game/public/js/room-lobby.js | 15 +++-- www/html/Game/public/play.html | 2 +- www/html/Game/public/room-lobby.html | 2 +- www/html/Game/server.js | 56 ++++++++++++++++-- 8 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 www/html/Game/public/img/Jumper/mission-complete.png create mode 100644 www/html/Game/public/img/ViolentCrime/mission-complete.png 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 0000000000000000000000000000000000000000..0b15d97ddf873ebc04f5320d5a6b19620b1e7d5d GIT binary patch literal 4850 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 Y0r7s&O)n9jvj6}907*qoM6N<$f^JS5i~s-t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0b15d97ddf873ebc04f5320d5a6b19620b1e7d5d GIT binary patch literal 4850 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 Y0r7s&O)n9jvj6}907*qoM6N<$f^JS5i~s-t literal 0 HcmV?d00001 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 —