From cdfd19c7a1eb200a0257c74b305d9a0a5b7b40de Mon Sep 17 00:00:00 2001 From: giteaadmin Date: Sun, 14 Jun 2026 10:46:46 +0000 Subject: [PATCH] update all games and cards --- www/html/Admin/admin.js | 92 +++++++- www/html/Admin/index.html | 6 + www/html/Admin/private/store.json | 13 +- www/html/Game/data/game-timing.json | 15 +- www/html/Game/public/css/room-lobby.css | 158 ++++++++++++++ .../img/Jumper/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../img/MegaVirus/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../img/QUESTION/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../img/TowerBlock/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../img/ViolentCrime/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../gauntlet-assets/popup-result-card-bg.png | Bin 0 -> 4938 bytes .../img/quiz-carry/popup-result-card-bg.png | Bin 0 -> 4938 bytes www/html/Game/public/js/play.js | 202 +++++++++++------- www/html/Game/public/js/room-lobby.js | 54 +++-- www/html/Game/public/play.html | 2 +- www/html/Game/public/room-lobby.html | 4 +- www/html/Game/server.js | 162 +++++++++++++- www/html/Loading/index.html | 2 +- www/html/Loading/loading.js | 61 +++++- 19 files changed, 627 insertions(+), 144 deletions(-) create mode 100644 www/html/Game/public/img/Jumper/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/MegaVirus/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/QUESTION/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/TowerBlock/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/ViolentCrime/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/gauntlet-assets/popup-result-card-bg.png create mode 100644 www/html/Game/public/img/quiz-carry/popup-result-card-bg.png diff --git a/www/html/Admin/admin.js b/www/html/Admin/admin.js index c165dfc..6009ae9 100644 --- a/www/html/Admin/admin.js +++ b/www/html/Admin/admin.js @@ -4019,6 +4019,94 @@ }); } + var SPECIAL_CARD_GAMES = [ + { id: 'mng8a80o', label: 'Minigame-1 จริงหรือไม่' }, + { id: 'mno9kb07', label: 'Minigame-2 วิ่งหลบสิ่งกีดขวาง' }, + { id: 'mnn93hpi', label: 'Minigame-3 Tower block' }, + { id: 'mnorwqx1', label: 'Minigame-4 คำศัพท์ในกระบวนการยุติธรรม' }, + { id: 'mnptfts2', label: 'Minigame-5 กระโดดขึ้นแท่น' }, + { id: 'mnpz6rkp', label: 'Minigame-6 ยิงอุกาบาต Violent Crime' }, + { id: 'mnq1eml7', label: 'Minigame-7 ยิง Mega Virus' } + ]; + var SPECIAL_CARD_OPTS = [ + { v: '', t: '— ไม่บังคับ —' }, + { v: '1', t: '1 · เพิ่มเวลามินิเกม +10s' }, + { v: '2', t: '2 · เพิ่มเวลาโหวต +10s' }, + { v: '3', t: '3 · แบนผู้เล่น 1 คน' }, + { v: '4', t: '4 · ปิดปากผู้เล่น 1 คน' }, + { v: '5', t: '5 · จับผิดตัว โหวตใหม่' }, + { v: '6', t: '6 · ทุกคน +10 COINS' }, + { v: '7', t: '7 · ทุกคนหลักฐานฟรี +1' } + ]; + var specialCardRowsBuilt = false; + function buildSpecialCardByMapRows() { + if (specialCardRowsBuilt) return; + var wrap = el('special-card-by-map-rows'); + if (!wrap) return; + wrap.innerHTML = ''; + SPECIAL_CARD_GAMES.forEach(function (g) { + var row = document.createElement('div'); + row.style.cssText = 'display:flex;align-items:center;gap:10px;flex-wrap:wrap;'; + var name = document.createElement('span'); + name.style.cssText = 'min-width:240px;color:#cfd6ea;font-size:14px;'; + name.textContent = g.label; + var sel = document.createElement('select'); + sel.id = 'tsc-' + g.id; + sel.style.cssText = 'padding:6px 8px;min-width:300px;font-size:14px;'; + SPECIAL_CARD_OPTS.forEach(function (o) { + var op = document.createElement('option'); + op.value = o.v; + op.textContent = o.t; + sel.appendChild(op); + }); + row.appendChild(name); + row.appendChild(sel); + wrap.appendChild(row); + }); + specialCardRowsBuilt = true; + } + function loadSpecialCardByMapPanel() { + buildSpecialCardByMapRows(); + gameTimingFetch('GET') + .then(function (data) { + var m = (data && data.testSpecialCardByMap) || {}; + SPECIAL_CARD_GAMES.forEach(function (g) { + var sel = el('tsc-' + g.id); + if (sel) sel.value = (m[g.id] != null && m[g.id] !== '') ? String(m[g.id]) : ''; + }); + setMsg('special-card-by-map-msg', '', ''); + }) + .catch(function (e) { setMsg('special-card-by-map-msg', e.message || 'โหลดไม่ได้', 'error'); }); + } + function saveSpecialCardByMapPanel() { + var map = {}; + SPECIAL_CARD_GAMES.forEach(function (g) { + var sel = el('tsc-' + g.id); + var v = sel ? parseInt(String(sel.value || ''), 10) : 0; + if (v >= 1 && v <= 7) map[g.id] = v; + }); + var btn = el('btn-special-card-by-map-save'); + if (btn) btn.disabled = true; + gameTimingFetch('GET') + .then(function (data) { + data.testSpecialCardByMap = map; + return gameTimingFetch('PUT', data); + }) + .then(function (res) { + if (res && res.testSpecialCardByMap) { + var m = res.testSpecialCardByMap; + SPECIAL_CARD_GAMES.forEach(function (g) { + var sel = el('tsc-' + g.id); + if (sel) sel.value = (m[g.id] != null) ? String(m[g.id]) : ''; + }); + } + var any = Object.keys(map).length > 0; + setMsg('special-card-by-map-msg', any ? 'บันทึกแล้ว — เกมที่ตั้งจะขึ้นคำถามพิเศษ + การ์ดนั้นแน่นอน' : 'ล้างการบังคับแล้ว (กลับเป็นสุ่มปกติ)', 'ok'); + }) + .catch(function (e) { setMsg('special-card-by-map-msg', e.message || 'บันทึกไม่ได้', 'error'); }) + .then(function () { if (btn) btn.disabled = false; }); + } + function saveJumpSurviveTimingPanel() { var inp = el('jump-survive-height-mult'); var jumpSurvMult = inp ? parseFloat(String(inp.value)) : 1.5; @@ -4537,7 +4625,7 @@ if (name === 'game-timing') loadGameTimingPanel(); if (name === 'stack-game') loadStackGamePanel(); if (name === 'highscore') loadHighscore(); - if (name === 'test-mode') loadForcedMinigamePanel(); + if (name === 'test-mode') { loadForcedMinigamePanel(); loadSpecialCardByMapPanel(); } } function boot() { @@ -4763,6 +4851,8 @@ } var forcedMgSaveBtn = el('btn-forced-minigame-save'); if (forcedMgSaveBtn) forcedMgSaveBtn.addEventListener('click', saveForcedMinigamePanel); + var specialCardSaveBtn = el('btn-special-card-by-map-save'); + if (specialCardSaveBtn) specialCardSaveBtn.addEventListener('click', saveSpecialCardByMapPanel); syncTestModeUi(); /* ฟัง storage event — sync เมื่อเปิดหลายแท็บ */ window.addEventListener('storage', function (e) { diff --git a/www/html/Admin/index.html b/www/html/Admin/index.html index b1dbc34..a6ca6da 100644 --- a/www/html/Admin/index.html +++ b/www/html/Admin/index.html @@ -1155,6 +1155,12 @@

+ +

บังคับการ์ดพิเศษ → เกม (เทสต์)

+

เลือกว่าจะให้ "การ์ดพิเศษใบไหน" ขึ้นใน "เกมไหน" → เกมที่ตั้งไว้คำถามพิเศษจะขึ้นแน่นอนและให้การ์ดใบนั้น · ต้องมีคำถามพิเศษของ Level×Case นั้นด้วย · "— ไม่บังคับ —" = ปกติ (สุ่ม 1/3)

+
+
+

diff --git a/www/html/Admin/private/store.json b/www/html/Admin/private/store.json index 6d707e1..a63026d 100644 --- a/www/html/Admin/private/store.json +++ b/www/html/Admin/private/store.json @@ -82,13 +82,16 @@ "providerUserId": "p_1777015659667_xqfjh247bgp", "notes": "auto: player-coins", "blocked": false, - "coins": 90, + "coins": 320, "createdAt": "2026-04-24T07:28:02+00:00", - "updatedAt": "2026-06-14T04:39:10+00:00", - "score": 90, + "updatedAt": "2026-06-14T10:44:27+00:00", + "score": 320, "scoreByCase": { - "8": 60, - "5": 30 + "8": 210, + "5": 30, + "7": 60, + "2": 10, + "9": 10 }, "lbName": "ผู้เล่น" }, diff --git a/www/html/Game/data/game-timing.json b/www/html/Game/data/game-timing.json index fa15f0a..219cb2f 100644 --- a/www/html/Game/data/game-timing.json +++ b/www/html/Game/data/game-timing.json @@ -89,8 +89,17 @@ "balloonBossPlayerBalloonFallbackUrl": "/Game/img/MegaVirus/Artboard%209.png", "balloonBossBalloonsPerPlayer": 3, "forcedMinigameKeys": [ - "quiz", + "jump_survive", "quiz_carry", - "balloon_boss" - ] + "stack" + ], + "testSpecialCardByMap": { + "mng8a80o": 1, + "mno9kb07": 2, + "mnn93hpi": 3, + "mnorwqx1": 4, + "mnptfts2": 5, + "mnpz6rkp": 6, + "mnq1eml7": 7 + } } \ No newline at end of file diff --git a/www/html/Game/public/css/room-lobby.css b/www/html/Game/public/css/room-lobby.css index c059194..74f3640 100644 --- a/www/html/Game/public/css/room-lobby.css +++ b/www/html/Game/public/css/room-lobby.css @@ -1836,3 +1836,161 @@ body.room-lobby--quiz-active .lobby-b-extra-row { body.room-lobby-page .room-lobby-top-right-actions { display: none !important; } + +/* ---- Player Pick Vote overlay (Card Ban / Silence) — โหวตเลือกผู้เล่น ---- + สร้างด้วย JS ใน room-lobby.js (#pick-vote-overlay) แต่เดิมไม่มี CSS เลย → div จมหลัง UI lobby มองไม่เห็น */ +#pick-vote-overlay { + position: fixed; + inset: 0; + z-index: 12000; + display: flex; + align-items: center; + justify-content: center; + padding: 4vh 4vw; + background: rgba(4, 8, 16, 0.74); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 0.22s ease, visibility 0.22s ease; + font-family: 'Kanit', sans-serif; +} +#pick-vote-overlay.is-open { + opacity: 1; + visibility: visible; + pointer-events: auto; +} +#pick-vote-overlay .pv-shell { + width: min(460px, 92vw); + max-height: 88vh; + overflow-y: auto; + box-sizing: border-box; + padding: 1.4rem 1.5rem 1.6rem; + border-radius: 18px; + text-align: center; + color: #e8f6ff; + background: linear-gradient(165deg, rgba(16, 30, 50, 0.97), rgba(8, 16, 30, 0.97)); + border: 1px solid rgba(83, 224, 255, 0.55); + box-shadow: 0 0 28px rgba(83, 224, 255, 0.35), inset 0 0 24px rgba(80, 180, 240, 0.14); + transform: translateY(12px) scale(0.97); + transition: transform 0.26s cubic-bezier(0.2, 0.9, 0.3, 1.2); +} +#pick-vote-overlay.is-open .pv-shell { + transform: translateY(0) scale(1); +} +#pick-vote-overlay .pv-badge { + display: inline-block; + padding: 0.2rem 0.85rem; + border-radius: 999px; + font-size: 0.74rem; + font-weight: 700; + letter-spacing: 0.08em; + color: #04121e; + background: linear-gradient(90deg, #53e0ff, #8ad8ff); + box-shadow: 0 0 14px rgba(83, 224, 255, 0.55); +} +#pick-vote-overlay .pv-title { + margin: 0.7rem 0 0.3rem; + font-size: 1.32rem; + font-weight: 700; + text-shadow: 0 0 12px rgba(83, 224, 255, 0.45); +} +#pick-vote-overlay .pv-sub { + margin: 0 0 0.7rem; + font-size: 0.86rem; + line-height: 1.35; + color: #a9c7da; +} +#pick-vote-overlay .pv-timer { + font-size: 1.5rem; + font-weight: 700; + color: #53e0ff; + margin-bottom: 0.6rem; + min-height: 1.4em; +} +#pick-vote-overlay .pv-timer.low { + color: #ff7a7a; + animation: pvPulse 0.7s ease-in-out infinite; +} +@keyframes pvPulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.12); } +} +#pick-vote-overlay .pv-list { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 0.6rem; +} +#pick-vote-overlay .pv-cand { + position: relative; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + width: 100%; + padding: 0.7rem 1rem; + border-radius: 12px; + font: 600 1rem 'Kanit', sans-serif; + color: #e8f6ff; + cursor: pointer; + background: rgba(28, 48, 74, 0.85); + border: 1px solid rgba(83, 224, 255, 0.3); + transition: transform 0.12s ease, border-color 0.15s ease, background 0.15s ease; +} +#pick-vote-overlay .pv-cand:hover { + transform: translateY(-1px); + border-color: rgba(83, 224, 255, 0.7); + background: rgba(36, 62, 94, 0.95); +} +#pick-vote-overlay .pv-cand.is-picked { + border-color: #53e0ff; + background: linear-gradient(90deg, rgba(83, 224, 255, 0.28), rgba(83, 224, 255, 0.14)); + box-shadow: 0 0 14px rgba(83, 224, 255, 0.5); +} +#pick-vote-overlay .pv-me { + font-size: 0.78rem; + color: #8ad8ff; +} +#pick-vote-overlay .pv-bot { + font-size: 0.72rem; + color: #04121e; + background: #7fd4a8; + padding: 0.05rem 0.45rem; + border-radius: 999px; +} +#pick-vote-overlay .pv-count { + position: absolute; + right: 0.7rem; + min-width: 1.5em; + height: 1.5em; + line-height: 1.5em; + border-radius: 999px; + font-size: 0.82rem; + font-weight: 700; + color: #04121e; + background: #53e0ff; + opacity: 0; + transform: scale(0.6); + transition: opacity 0.15s ease, transform 0.15s ease; +} +#pick-vote-overlay .pv-count.show { + opacity: 1; + transform: scale(1); +} +#pick-vote-overlay .pv-result { + margin: 0.4rem 0; + padding: 0.7rem; + border-radius: 12px; + font-size: 1.05rem; + font-weight: 700; + color: #fff; + background: rgba(83, 224, 255, 0.16); + border: 1px solid rgba(83, 224, 255, 0.5); +} +#pick-vote-overlay .pv-foot { + font-size: 0.82rem; + color: #a9c7da; + min-height: 1.2em; +} diff --git a/www/html/Game/public/img/Jumper/popup-result-card-bg.png b/www/html/Game/public/img/Jumper/popup-result-card-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b86b6be079f1e71f84fda978364e9e646c0c7828 GIT binary patch literal 4938 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 WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp 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 WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp 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 WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp 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 WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp z8d#mjvU5JOv!1U;Z@PKU1IL4#j)4FG*TsKf_%a=R0sve*F)_Gq6I!_W>@a=GD46#C z4L+(bsuyWo4W%iGuk#dodW6mw6aHd8#0fhvb6k*UW1R6FGaAdVuU@Q^?iNWkLVURy zxNkDn%O}(PN^`@nx?tKmXO@i|cD2CQF6zj(<>eTO1b*^eWbGa5u>?iZo%5E||KP{7 zKWr;(3_&x+bG8W#kdK@Y4oO=^hytsB@oy!Y;eQuOhxMneQC~1`EpQ zK46Kb5YTYUz1wLncA%i4Fg_^{VTu)Wh%4ls!bJ>GN5=62LB6ViH%-9xY%Q)NQ4-IUh=x7ZDJRm$e(sZ_Fx4_89{DzLugS!K zTa#2ONYmYFze4@yz`_&aeGHt!kylT7<{@s@y!6#FP)0S-bmDf`L9RJxtgIj;5t>ik zH}6rE88H0uHIM#qp%1zQvVsW5za}D)w`FJK(1{cxWYGbR?+myl6Ogtmorbxg@9jP* z)#)P`#mm=H%{eTY15L`QuM_7IDaQG~@_N5>D*F`SjwR&8QV7DAp|XIx09!yHphsBb zbR*sfhVp2*I(`jtC}!N%i*YO-QOig5T7afi#+Ny`tR0@4K4j0FQp z#Qk99()uwmeaU5jvMr-aqot3l>wVVs8G2&x*Jyakv+0Rz9vCe}7ND_R#_9~>HPPCwjF zXgcnH;yq%C(%rwb=QO0PvzI!n`$xR0HNe8by!4)J#3DZ5)dgz%(l&LmG$(v{?DgsQ z7Pi?z`_`zd%lDZyScVMbOVd9D2nnef0}(#_MZQfsLGjKmUIkol)10<*S`UiX*9Kjo z$K`S-k;^9Y54OFt2n-tG0v@5Y#{5W&o-`|FMh68w1tJ!^_vOea?C$-N7ed03^ln=E zn!4-R(SX(Bz<0rQmIF`poCEqiy@J zi1OOEEw-qsZ}&4f04O(@bY>PXMzYT#DNjt5jFXEWA9Y*mJjN|i>l z>ry#e&bAQr+v3xw!)txQbru82CU;(+vB5~ZcED19PmMgrOhitTO6u*ca@*Z(DJfo` zJ>3de@8-Q9^JkOl`i4aS)b=Oui3J+e4rBe^(cU%dB*IhuGVP*R4nma~Ex9u<`Z~)rTzG}+=H}tPoeKF$Rl{y7QTd4}M^xY}j2t2utW@*1Mon&e1MyLKH2QSn-7e$@2Jo zZjk#mR!_9AuxeXs*1X&LX^7l-or}VB%bzVa@u~2K5sl$&m_WaD4+vQ6o)GDZIu^#!@@6Wf17yiAGuHKPwju@-`}4bqp@ehwQ9)^ve2aNIO>KKz3)&} z)`z6im{x27X5;rZ*MHLn?Oisut~$|Z(d8)c0lIb2>zFLz_A7Oo#{D*r;o8{8YELJt z9_7<&wge!ktf4X%AaJ1fH5^4P@9K+i$nw0{}i`GtyhFGVe~H9{BfKDcTuFv zSYaWJTdoRGdwVRp+BR2$>o2i$ckwm#H;6xW=P)0k5~Q(&->Nj}EsxGcQ%gaXgn4!m z@w<=z!-JK&090*@NPoLFt~i>#mrE<);#%}iH)~4%(0}MaP+TJW8(Q`Kx~JsuDK@dj z`1lw)d`~qJWg3JQSFnz&tquf3dyH-uc%$EbC35y^Y;lOd1dg_r#Z~#t5WcF;`K=A^Dd=zerf)YvB@|1X1kS2)Z2SAIMB;g;tN zwkW7!B@?H{iIU6oqiu}3lBgQPfYIpFWbRD#_O?goJnrY4xNy!`>lb(Rdy0ILi}U_r zD#dIW4QP;LR|uX)%RXhdbeI#K+vLYJe(KB2!=;k8xG&?06RUmu1~TuzcT(&tbqF2L zw4*OL@{{`tZ(A1`t2Dz5dp_TUs@JP)PLyByo`~=;LH?y`T|C;;$<}I5QI5+vUez9T z4Kqi%^B#+J69zm%b=gR{g|l7h-^-%0aV73W$HM#NH{cAWO5xTHQJz~NNX4@6+{+^` zQ%Fy;l1M1;sZDtDf2aR$|DXT+H9&cmC_P!|vq#507%xE#0&Qa7Zvi|t-@Mm3t6fQU^yjKYf%}P$E>q-O> z^}s!a{OcGwKApkdfam(;71YCoa)ArX)56(|1WB02q|x%Ok`!o5m(Z10ET(WwoCPQU zC*rjoof%rMmgy4g z?n|b&zLOWi_1pr{Hn+h$x49)uSXkC6IC4oNbX((f;O!(U! zyGkj0Uv=D?BNZDTU;j&+5%{n2xLX0_{ZuOEU7R1(7b+sv9|6O`7Ppv635+a`KKL;w zAW{E6PPPDxu-*P#06M6>^0R8LnIQyKSlvIU)O6YzpK4T!EU|4wck+G2IsvaMVPP5K z4~(sr8cg^WJz)$uwi$D06)BwS#k*rb_@PLNH37{mf0^y+KQUmIp0%mM&JEw@*Qxs~_>~GJf0pAnsHakbnjI$3L-J&10M2>`i@$}F$Mab*P%P?>GY&+E~UTWR}{RXWg`{;6wbfT0Vw5S3eAQm@+sGpZqX&eDg+ZL`{e7f?M3F=a^ln zjcV1l!yt9xBEai1apW|<-)FD1rDbhyW*8DC_5tKR|2L?w&!F?co5Ihs(CeF*I5bV7 zGv=3?hqI;rv16|K;Kby_ACd3iqo-h1WV~x;=5jc>v{eH3=X;#)H==y?Cj&l(R9|7D zdOPIh%sf}j@vb_ywKXIv*t^jt=I}*-5rh-%B?}ULET6Q%{2bv2Mim)!sZv?{H%TvS zN%2Z}%5T7+wev6bBIBWavYOo$=gNvh7T1=L^$+yJUy_w}K>j4bu#OF3+0Ve`FOJQvCoUv+>%(+2>rN+vg;zl691< zjI3N*gQJ46-g%0}Cu;+5cNgDFRSp8CkNQO=ceznZf(Wy~A_9rw{GI@RYQX!GxnrLc zIk`n8h(qL2d+g-T(_JB8QZ}Q4;ZTC+&>R9ZoRykrpSz-YvBi3EOeRrkET1gUFtTag zX{GYoe`UfoCOqQLnvbh)zLL%jhunIicY7Qtz*j!&0B8(9WYG7%1aOJ2bEdDAq@PiL zfLp^HSF>^1FT<%d(-L=+>KM;uod4QvA2)BJDyDKd>v^v)Md33YXiofH0b}?gvUBxs z0h`83mA_ha5$~JIurc)2&2n}1v*Uf!J;}$AkH}PM?A$~s_rp$h&pkwz(^uN&Dk$4Q z8z2bS1>~S2CqhHEuKbyq8$4aVNB=Gn+S)Q&artt6HwmT~AaG#ryO6YNq5q<@$=wJw zq=ySm6v!F7Vx!{x&Uy5eQfi)S)JgwVe?N-uFMpQ_GyBEZzAsdnUqg$ZIRfRsnJQJb z7o_uzSZ%JMgac=gx5ci_x@c(}g3nHCE;XDzc8=Y4j2Mqx$j)c0`hkWs%>sw z)l}2JzMOOCLuY^1egRj+vHFuM{;msEn_(mOWZzH2H?h|6B9j*aSnRSkfSkIQ^<`bX z$Zn>o@liwd4qbc`<1HZN^x<`$${GQH6I>`^|sz7Xk@dw~;)m@kMg zkgGC#1+B4)u8*Nc1-$br+Pw0hxpY^T2m`spRM}=WHj;}9wD$a3srKV*-^rCBC39q< zrt^n*v#U35o~dBR%cI(zk*TCC%J*quJ%vhsD3F@lvlt#Ril~6o+0PCW=P$H-E!ES! z#3I%XRtfeupdfWhGWif(pO_PYk|C?WfRogY+M-u=~$ z`QX53W4;9)dzs_u>SfXpSWe+Sl|t!`!XUp|UbzoRe0U9h%f{5rK=tpbfc-mi!W3Vw zEJ;mRkq}Q-a{oUJd-@b+JU;?S44!Oz^iIdim89HFXey3uXZ@^64b*jKY+S5;TVwPs zdR(AW&U&GpeK^e!9Z!&jJarsc_^IvAnN?ow8yjb?V>fa#_4WMt!p0w`F%SKp3psCQ ze@e-vasl~oW+RZ=8}}VmpaSR|Q}N(|4hl1;KuzF>pZVB~oyacf4+ilB1!uI|!iqve zKiCo?D+`lU)e;`eVdwD6QtXl`WriqYmm!~itHrOvERsyPzz9KAEp;U$y$lVMhwZOg z&)U3~B%Gg0re=3o63H_>ra>+KI*Y`YTVle@8?;8eCVMWIIZ6B1F8p6{_}@7dZxS#d WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp 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 WG5z3?J^vg7m>AwNc&+dDxMj@4DZ&W=0C-Gp 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 WG5z3?J^vg7m>AwNc&+dDคุณถูกแบนรอบนี้ · กำลังดูเพื่อนเล่น' + + '
รอบหน้ากลับมาเล่นได้ตามปกติ
'; + document.body.appendChild(banner); + } const specialQuizForceDebug = params.get('sqForce') === '1'; const forceDefaultCharacter = params.get('defaultChar') === '1'; const editorEmbedReturn = params.get('editorEmbed') === '1'; @@ -442,7 +463,7 @@ var img = document.createElement('img'); img.src = c.imageUrl; img.alt = EVIDENCE_RARITY_LABEL[rar] || 'หลักฐาน'; - img.style.cssText = 'display:block;width:min(34vw,260px);height:auto;border-radius:14px;box-shadow:0 0 36px ' + glow + ',0 18px 50px rgba(0,0,0,.6);cursor:zoom-in'; + img.style.cssText = 'display:block;width:min(51vw,390px);height:auto;border-radius:14px;box-shadow:0 0 36px ' + glow + ',0 18px 50px rgba(0,0,0,.6);cursor:zoom-in'; img.title = 'แตะเพื่อดูใหญ่'; img.addEventListener('click', function () { openEvidenceCardLightboxPlay(c.imageUrl, EVIDENCE_RARITY_LABEL[rar] || '', rar); }); img.onerror = function () { this.onerror = null; this.style.width = '200px'; this.style.height = '300px'; this.style.background = '#1b2240'; this.removeAttribute('src'); }; @@ -10538,27 +10559,26 @@ function quizCarryPreviewBotLobbyWanderStep(o, w, h) { const now = Date.now(); o.botIsWalking = false; - if (o.botWanderDx == null || o.botWanderDy == null || (o.botWanderDx === 0 && o.botWanderDy === 0)) { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; + /* เดินแบบมีจุดหมาย (เหมือน lobby) — สุ่มจุดเป้า เดินเข้าหาแบบทแยงลื่น พักบ้าง · เลิกใช้สุ่มทิศแกนที่ดูกระตุก */ + const arrived = (typeof o.botWanderTX === 'number' && typeof o.botWanderTY === 'number') + && Math.hypot(o.botWanderTX - o.x, o.botWanderTY - o.y) < 0.5; + const timedOut = typeof o.botWanderRetargetAt === 'number' && now >= o.botWanderRetargetAt; + if (typeof o.botWanderTX !== 'number' || typeof o.botWanderTY !== 'number' || arrived || timedOut) { + o.botWanderTX = 1 + Math.random() * Math.max(1, w - 2); + o.botWanderTY = 1 + Math.random() * Math.max(1, h - 2); + o.botWanderRetargetAt = now + 3000 + Math.floor(Math.random() * 4500); + o.botWanderPauseUntil = (arrived && Math.random() < 0.45) ? now + 500 + Math.floor(Math.random() * 1600) : 0; } - if (typeof o.botWanderNextTurn !== 'number') o.botWanderNextTurn = now + 600; - if (now >= o.botWanderNextTurn) { - o.botWanderNextTurn = now + 650 + Math.floor(Math.random() * 2200); - if (Math.random() < 0.55) { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; - } - } - const accX = o.botWanderDx; - const accY = o.botWanderDy; - if (Math.abs(accY) > Math.abs(accX)) o.direction = accY > 0 ? 'down' : 'up'; - else if (accX !== 0) o.direction = accX > 0 ? 'right' : 'left'; + if (o.botWanderPauseUntil && now < o.botWanderPauseUntil) return; + let dx = o.botWanderTX - o.x; + let dy = o.botWanderTY - o.y; + const dist = Math.hypot(dx, dy) || 1; + dx /= dist; dy /= dist; + if (Math.abs(dy) > Math.abs(dx)) o.direction = dy > 0 ? 'down' : 'up'; + else o.direction = dx > 0 ? 'right' : 'left'; const step = MOVE_SPEED * quizCarryWalkSpeedMultActive; - const nx = o.x + accX * step; - const ny = o.y + accY * step; + const nx = o.x + dx * step; + const ny = o.y + dy * step; const ox = o.x; const oy = o.y; if (canWalkLikeLobbyForBot(nx, ny, o.x, o.y, o)) { @@ -10570,10 +10590,8 @@ } else if (canWalkLikeLobbyForBot(o.x, ny, o.x, o.y, o)) { o.y = ny; } else { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; - o.botWanderNextTurn = now + 200 + Math.floor(Math.random() * 600); + o.botWanderTX = undefined; + o.botWanderTY = undefined; } } if (!Number.isFinite(o.x)) o.x = 0.5; @@ -11168,6 +11186,8 @@ lastStackPreviewActorId = '__human__'; lastStackPreviewActorUntil = Date.now() + 900; } + /* MG3 stack: เดินไปแตะไอคอนไม่ได้ → "หยอดบล็อก" (บล็อกร่วงผ่านไอคอน) = ทริกคำถามพิเศษเลย */ + if (specialQuizIcon) sendSpecialQuizCollide(); return true; } @@ -11181,6 +11201,8 @@ o.stackBotGlowUntil = Date.now() + Math.max(900, 400 + (stackFall && stackFall.dur ? stackFall.dur : 800)); lastStackPreviewActorId = botId; lastStackPreviewActorUntil = Date.now() + Math.max(1000, 500 + (stackFall && stackFall.dur ? stackFall.dur : 800)); + /* บอทหยอดบล็อกก็ทริกคำถามพิเศษ (กันไม่มีคนหยอดไปโดน) */ + if (specialQuizIcon) sendSpecialQuizCollide(); } /** Stack preview: สลับตา — เฉพาะบอทที่ถึงตาถึงจะจำลองปล่อยหนึ่งครั้งแล้วเลื่อนตา */ @@ -12722,6 +12744,8 @@ try { quizCarryAnswerCloseAt = shift(quizCarryAnswerCloseAt); } catch (e) {} try { if (typeof jumpSurviveSessionStartMs === 'number' && jumpSurviveSessionStartMs > 0) jumpSurviveSessionStartMs += deltaMs; } catch (e) {} try { if (typeof spaceShooterSessionStartMs === 'number' && spaceShooterSessionStartMs > 0) spaceShooterSessionStartMs += deltaMs; } catch (e) {} + try { if (typeof stackTowerSessionStartAt === 'number' && stackTowerSessionStartAt > 0) stackTowerSessionStartAt += deltaMs; } catch (e) {} + try { if (typeof balloonBossSessionStartMs === 'number' && balloonBossSessionStartMs > 0) balloonBossSessionStartMs += deltaMs; } catch (e) {} } /** เรียกทุกเฟรมระหว่าง freeze — ดันเวลาไปข้างหน้าเท่าที่ผ่านไปจริง ทำให้ countdown หยุดนิ่ง */ @@ -12864,12 +12888,22 @@ /** เช็คผู้เล่นเดินชนไอคอน — ใครชนก่อนเป็นคนเปิดคำถามให้ทั้งห้อง (server กันซ้ำ) */ function checkSpecialQuizCollision() { if (!specialQuizIcon || specialQuizCollideSent || specialQuizActiveQuestion) return; - if (!Number.isFinite(me.x) || !Number.isFinite(me.y)) return; - const dx = me.x - specialQuizIcon.x; - const dy = me.y - specialQuizIcon.y; + const ix = specialQuizIcon.x; + const iy = specialQuizIcon.y; /* รัศมีกว้างขึ้น (1.25 tile) — ไอคอนวาดใหญ่ 1.75 tile เดินเข้าใกล้ก็ทริกได้ ไม่ต้องทับ tile กลางพอดี */ - if (dx * dx + dy * dy <= 1.25 * 1.25) { - sendSpecialQuizCollide(); + const within = (ex, ey) => { + if (!Number.isFinite(ex) || !Number.isFinite(ey)) return false; + const dx = ex - ix; + const dy = ey - iy; + return dx * dx + dy * dy <= 1.25 * 1.25; + }; + if (within(me.x, me.y)) { sendSpecialQuizCollide(); return; } + /* บอท (preview / เติมคดี) เดินโดนไอคอนก็เปิดคำถามพิเศษให้ทั้งห้อง (ผู้เล่นจริงเป็นคนตอบ) — กันกรณีไม่มีคนเดินไปโดน */ + if (playBotsEnabled()) { + for (const [id, o] of others) { + if (!isPreviewBotId(id) || !o) continue; + if (within(o.x, o.y)) { sendSpecialQuizCollide(); return; } + } } } @@ -13113,6 +13147,8 @@ if (!ms) return; if (isJumpSurvive() && jumpSurviveSessionStartMs) jumpSurviveSessionStartMs += ms; if (isSpaceShooter() && spaceShooterSessionStartMs) spaceShooterSessionStartMs += ms; + if (isStack() && stackTowerSessionStartAt) stackTowerSessionStartAt += ms; + if (isBalloonBoss() && balloonBossSessionStartMs) balloonBossSessionStartMs += ms; if (isQuizCarry()) { if (quizCarryAnswerCloseAt > 0) quizCarryAnswerCloseAt += ms; if (quizCarryOptionRevealAt > 0) quizCarryOptionRevealAt += ms; @@ -13244,7 +13280,8 @@ if (!disp || !Array.isArray(disp.ranked)) return; /* mission map — สรุปผลภารกิจตาม mockup (Minigame 1/4–7 + Last Light 07-summary) */ const tfMockSummary = isGcmMissionMockSummaryPlay(mission); - const mg2Summary = tfMockSummary && missionSkin === 'gauntlet_crown'; + /* สรุปผลแบบ MG2 (โบนัสพิเศษ 2 คอลัมน์ + popup-result panel) ให้ "ทุกเกม" ใช้ — CSS/asset ต่อ skin มีครบ */ + const mg2Summary = tfMockSummary; ov.classList.toggle('gcm-quiz-tf-mock', tfMockSummary); ov.classList.toggle('gcm-mg2-mock', mg2Summary); const gcmVrule = document.getElementById('gcm-vrule'); @@ -13438,7 +13475,9 @@ bonusEl.innerHTML = ''; const gLetter = String(disp.grade || 'C').toUpperCase().charAt(0); const missionOk = gLetter !== 'F'; + const hasSpecialCard = !!(mission && mission.specialCardAwarded); let bonusCardHost = bonusEl; + /* mg2 summary: โชว์ block "โบนัสพิเศษ" เสมอ (missionOk) — ได้การ์ด=โชว์การ์ด + complete-select, ไม่ได้=complete ธรรมดา + การ์ดเปล่า placeholder */ if (mg2Summary && missionOk) { const bonusWrap = document.createElement('div'); bonusWrap.className = 'gcm-bonus-mg2 sm-bonus'; @@ -13449,12 +13488,12 @@ bonusHead.textContent = 'โบนัสพิเศษ'; const completeImg = document.createElement('img'); completeImg.className = 'gcm-bonus-mg2-complete complete'; - completeImg.src = gcmMissionMockAssetUrl(mission, 'mission-complete-select.png'); + completeImg.src = gcmMissionMockAssetUrl(mission, hasSpecialCard ? 'mission-complete-select.png' : 'mission-complete.png'); completeImg.alt = 'ภารกิจสำเร็จ'; completeImg.decoding = 'async'; completeImg.onerror = function () { this.onerror = null; - this.src = BASE + '/img/gauntlet-assets/result-check.png'; + this.src = gcmMissionMockAssetUrl(mission, 'mission-complete-select.png'); }; bonusLeft.appendChild(bonusHead); bonusLeft.appendChild(completeImg); @@ -13464,7 +13503,16 @@ bonusWrap.appendChild(bonusRight); bonusEl.appendChild(bonusWrap); bonusCardHost = bonusRight; - } else if (missionOk) { + if (!hasSpecialCard) { + /* ไม่ได้การ์ดพิเศษ → โชว์การ์ดพื้นหลังเปล่า (popup-result-card-bg.png) ตามดีไซน์ */ + const bgCard = document.createElement('img'); + bgCard.className = 'bail-card gcm-special-card-img'; + bgCard.src = gcmMissionMockAssetUrl(mission, 'popup-result-card-bg.png'); + bgCard.alt = ''; + bgCard.decoding = 'async'; + bonusRight.appendChild(bgCard); + } + } else if (missionOk && !mg2Summary) { const row = document.createElement('div'); row.className = 'gcm-bonus-okrow'; row.style.display = 'flex'; @@ -13517,15 +13565,6 @@ } if (mission && mission.specialCardAwarded) { renderSpecialCardInBonus(bonusCardHost, mission.specialCardAwarded); - } else if (mg2Summary && missionOk && rc && rc.th) { - const cardId = rc.kind === 'culprit' ? 1 : (rc.kind === 'rare' ? 5 : 3); - renderSpecialCardInBonus(bonusCardHost, { - th: rc.th, - en: rc.en || '', - imageUrl: '/Game/img/special-cards/card-' + cardId + '.png', - cardId: cardId, - rarity: rc.kind === 'rare' ? 'rare' : (rc.kind === 'culprit' ? 'legendary' : 'common'), - }); } } const gcmHead = document.getElementById('gcm-heading'); @@ -18126,6 +18165,7 @@ characterId: getPlayCharacterId(), playMapId: joinPlayMapId || undefined, playerKey: getJdPlayerKey(), + bannedSpectator: playBannedSpectator || undefined, }, (res) => { if (!res || !res.ok) { const errMsg = (res && res.error) || 'เข้าร่วมไม่ได้'; @@ -18139,6 +18179,7 @@ return; } myId = socket.id; + if (playBannedSpectator) setupBannedSpectatorUi(); if (specialQuizForceDebug) { try { socket.emit('special-quiz-force', { on: true }); } catch (eF) { /* ignore */ } } @@ -19776,53 +19817,43 @@ if (inAnswerPhase && o.botPath && o.botPath.length === 0 && !o.botAnswerWander) { return; } - /* ก่อนตอบ / พัก / บอทสับสน / ผี (ตอบผิดไปแล้ว): เดินทุกเฟรมตามทิศ */ - if (o.botWanderDx == null || o.botWanderDy == null || (o.botWanderDx === 0 && o.botWanderDy === 0)) { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; + /* ก่อนตอบ / พัก / บอทสับสน / ผี: เดินแบบมีจุดหมาย (สุ่มจุดเป้า เดินเข้าหาแบบทแยงลื่น พักบ้าง) — เนียนกว่าสุ่มทิศแกน */ + const arrivedQ = (typeof o.botWanderTX === 'number' && typeof o.botWanderTY === 'number') + && Math.hypot(o.botWanderTX - o.x, o.botWanderTY - o.y) < 0.5; + const timedOutQ = typeof o.botWanderRetargetAt === 'number' && now >= o.botWanderRetargetAt; + if (typeof o.botWanderTX !== 'number' || typeof o.botWanderTY !== 'number' || arrivedQ || timedOutQ) { + o.botWanderTX = 1 + Math.random() * Math.max(1, w - 2); + o.botWanderTY = 1 + Math.random() * Math.max(1, h - 2); + o.botWanderRetargetAt = now + 3000 + Math.floor(Math.random() * 4500); + o.botWanderPauseUntil = (arrivedQ && Math.random() < 0.4) ? now + 500 + Math.floor(Math.random() * 1500) : 0; } - if (typeof o.botWanderNextTurn !== 'number') o.botWanderNextTurn = now + 600; - if (now >= o.botWanderNextTurn) { - o.botWanderNextTurn = now + 650 + Math.floor(Math.random() * 2200); - if (Math.random() < 0.55) { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; - } - } - const accX = o.botWanderDx; - const accY = o.botWanderDy; - if (Math.abs(accY) > Math.abs(accX)) o.direction = accY > 0 ? 'down' : 'up'; - else if (accX !== 0) o.direction = accX > 0 ? 'right' : 'left'; - const step = MOVE_SPEED; - const tox = o.x + accX * step; - const toy = o.y + accY * step; const ox = o.x, oy = o.y; - const pathStrictW = isQuizBattle() && quizBattlePathModeActive(mapData); - if (pathStrictW) { - const moved = quizBattleTryStepMovePlay(o.x, o.y, tox, toy, (nx, ny, fx, fy) => canWalkLikeLobbyForBot(nx, ny, fx, fy, o)); - o.x = moved.x; - o.y = moved.y; - if (Math.abs(o.x - ox) < 1e-5 && Math.abs(o.y - oy) < 1e-5) { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; - o.botWanderNextTurn = now + 200 + Math.floor(Math.random() * 600); - } - } else if (canWalkLikeLobbyForBot(tox, toy, o.x, o.y, o)) { - o.x = tox; - o.y = toy; - } else { - if (canWalkLikeLobbyForBot(tox, o.y, o.x, o.y, o)) { + if (!(o.botWanderPauseUntil && now < o.botWanderPauseUntil)) { + let dxq = o.botWanderTX - o.x; + let dyq = o.botWanderTY - o.y; + const distq = Math.hypot(dxq, dyq) || 1; + dxq /= distq; dyq /= distq; + if (Math.abs(dyq) > Math.abs(dxq)) o.direction = dyq > 0 ? 'down' : 'up'; + else o.direction = dxq > 0 ? 'right' : 'left'; + const step = MOVE_SPEED; + const tox = o.x + dxq * step; + const toy = o.y + dyq * step; + const pathStrictW = isQuizBattle() && quizBattlePathModeActive(mapData); + if (pathStrictW) { + const moved = quizBattleTryStepMovePlay(o.x, o.y, tox, toy, (nx, ny, fx, fy) => canWalkLikeLobbyForBot(nx, ny, fx, fy, o)); + o.x = moved.x; + o.y = moved.y; + if (Math.abs(o.x - ox) < 1e-5 && Math.abs(o.y - oy) < 1e-5) { o.botWanderTX = undefined; o.botWanderTY = undefined; } + } else if (canWalkLikeLobbyForBot(tox, toy, o.x, o.y, o)) { + o.x = tox; + o.y = toy; + } else if (canWalkLikeLobbyForBot(tox, o.y, o.x, o.y, o)) { o.x = tox; } else if (canWalkLikeLobbyForBot(o.x, toy, o.x, o.y, o)) { o.y = toy; } else { - const d = pickRandomPreviewBotWanderDir(); - o.botWanderDx = d[0]; - o.botWanderDy = d[1]; - o.botWanderNextTurn = now + 200 + Math.floor(Math.random() * 600); + o.botWanderTX = undefined; + o.botWanderTY = undefined; } } if (!Number.isFinite(o.x)) o.x = 0.5; @@ -22502,6 +22533,11 @@ } document.addEventListener('keydown', (e) => { + /* Card 3 Ban — ผู้ถูกแบนรอบนี้คุมตัวละครไม่ได้: บล็อกคีย์เกมทั้งหมด (ยังพิมพ์แชตได้) */ + if (playBannedSpectator && !isChatFocused()) { + if (isMovementKey(e.code) || ['Space', 'Enter', 'NumpadEnter', 'KeyF'].includes(e.code)) e.preventDefault(); + return; + } /* Test Mode shortcut: Ctrl+Q = ตอบถูกใน special quiz (server เติมคำตอบให้) */ if (e.ctrlKey && (e.code === 'KeyQ' || e.key === 'q' || e.key === 'Q')) { if (specialQuizActiveQuestion && !specialQuizAnswered && __isTestModeOnPlay()) { diff --git a/www/html/Game/public/js/room-lobby.js b/www/html/Game/public/js/room-lobby.js index 40dacf1..1a11b01 100644 --- a/www/html/Game/public/js/room-lobby.js +++ b/www/html/Game/public/js/room-lobby.js @@ -2104,28 +2104,27 @@ const w = mapData.width || 20, h = mapData.height || 15; const now = Date.now(); lobbyBots.forEach((b, id) => { - if (typeof b.botWanderDx !== 'number' || typeof b.botWanderDy !== 'number' || (b.botWanderDx === 0 && b.botWanderDy === 0)) { - const d = LOBBY_BOT_WANDER_DIRS[Math.floor(Math.random() * LOBBY_BOT_WANDER_DIRS.length)]; - b.botWanderDx = d[0]; - b.botWanderDy = d[1]; + /* เดินแบบมีจุดหมาย: สุ่มจุดเป้าในแมป เดินเข้าหาแบบทแยงลื่น แล้วค่อยสุ่มใหม่เมื่อถึง/ติด/หมดเวลา + พักยืนเป็นช่วง — ดูเป็นธรรมชาติ ไม่เร่ร่อนสุ่มทิศ */ + const arrived = (typeof b.botWanderTX === 'number' && typeof b.botWanderTY === 'number') + && Math.hypot(b.botWanderTX - b.x, b.botWanderTY - b.y) < 0.5; + const timedOut = typeof b.botWanderRetargetAt === 'number' && now >= b.botWanderRetargetAt; + if (typeof b.botWanderTX !== 'number' || typeof b.botWanderTY !== 'number' || arrived || timedOut) { + b.botWanderTX = 1 + Math.random() * Math.max(1, w - 2); + b.botWanderTY = 1 + Math.random() * Math.max(1, h - 2); + b.botWanderRetargetAt = now + 3000 + Math.floor(Math.random() * 4500); + b.botWanderPauseUntil = (arrived && Math.random() < 0.45) ? now + 500 + Math.floor(Math.random() * 1600) : 0; } - if (typeof b.botWanderNextTurn !== 'number') b.botWanderNextTurn = now + 600; - if (now >= b.botWanderNextTurn) { - b.botWanderNextTurn = now + 650 + Math.floor(Math.random() * 2200); - if (Math.random() < 0.55) { - const d = LOBBY_BOT_WANDER_DIRS[Math.floor(Math.random() * LOBBY_BOT_WANDER_DIRS.length)]; - b.botWanderDx = d[0]; - b.botWanderDy = d[1]; - } - } - const accX = b.botWanderDx; - const accY = b.botWanderDy; - if (Math.abs(accY) > Math.abs(accX)) b.direction = accY > 0 ? 'down' : 'up'; - else if (accX !== 0) b.direction = accX > 0 ? 'right' : 'left'; + if (b.botWanderPauseUntil && now < b.botWanderPauseUntil) { b.isWalking = false; return; } + let dx = b.botWanderTX - b.x; + let dy = b.botWanderTY - b.y; + const dist = Math.hypot(dx, dy) || 1; + dx /= dist; dy /= dist; + if (Math.abs(dy) > Math.abs(dx)) b.direction = dy > 0 ? 'down' : 'up'; + else b.direction = dx > 0 ? 'right' : 'left'; const ox = b.x, oy = b.y; const step = MOVE_SPEED; - const nx = b.x + accX * step; - const ny = b.y + accY * step; + const nx = b.x + dx * step; + const ny = b.y + dy * step; if (canWalkLobbyBot(nx, ny, b.x, b.y, id)) { b.x = nx; b.y = ny; @@ -2134,10 +2133,8 @@ } else if (canWalkLobbyBot(b.x, ny, b.x, b.y, id)) { b.y = ny; } else { - const d = LOBBY_BOT_WANDER_DIRS[Math.floor(Math.random() * LOBBY_BOT_WANDER_DIRS.length)]; - b.botWanderDx = d[0]; - b.botWanderDy = d[1]; - b.botWanderNextTurn = now + 200 + Math.floor(Math.random() * 600); + b.botWanderTX = undefined; + b.botWanderTY = undefined; // ติดผนัง → หาเป้าใหม่ (ไม่กระตุกชนซ้ำ) } b.x = Math.max(0, Math.min(w - 0.01, b.x)); b.y = Math.max(0, Math.min(h - 0.01, b.y)); @@ -6519,17 +6516,16 @@ sessionStorage.setItem('justicePlayLobbyAvatar:' + cidPlay, avEl.src); } } catch (eAv) { /* ignore */ } - /* Card 3 Ban — ผู้ที่ถูกแบนรอบนี้ไม่เข้ามินิเกม (รออยู่ LobbyB) */ - if (data && data.bannedPlayerId && data.bannedPlayerId === socket.id) { - appendLobbySystemChat('— คุณถูกแบนรอบนี้ (Ban Card) · รอเพื่อนเล่นมินิเกมจบ'); - showBannedNotice(); - return; - } let q = 'play.html?space=' + encodeURIComponent(spaceId) + '&nick=' + encodeURIComponent(getProfileDisplayName()); if (mid) q += '&map=' + encodeURIComponent(mid); 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) · เข้าไปดูเพื่อนเล่นได้ แต่เล่นเองไม่ได้ · รอบหน้าเล่นได้ปกติ'); + } location.href = q; }); diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index 02592e0..db23be8 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -5180,7 +5180,7 @@ - +
v —