From c67ce56920705d631fe659869b85b3512106b636 Mon Sep 17 00:00:00 2001 From: giteaadmin Date: Fri, 1 May 2026 10:08:01 +0000 Subject: [PATCH] update menu admin and rocker 1.1 --- www/html/Admin/admin.css | 36 ++++++ www/html/Admin/admin.js | 128 ++++++++++++++++++- www/html/Admin/index.html | 40 ++++-- www/html/Game/data/game-timing.json | 5 +- www/html/Game/public/js/play.js | 188 ++++++++++++++++++++++++---- www/html/Game/public/play.html | 2 +- www/html/Game/server.js | 74 +++++++++++ 7 files changed, 437 insertions(+), 36 deletions(-) diff --git a/www/html/Admin/admin.css b/www/html/Admin/admin.css index ad6093d..a992581 100644 --- a/www/html/Admin/admin.css +++ b/www/html/Admin/admin.css @@ -1,5 +1,17 @@ /* Admin — JD JUSTICE DIVERS · UX-focused responsive layout */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + :root { --bg: #0b0d14; --bg-elevated: #12141f; @@ -2407,3 +2419,27 @@ code { border: 1px solid var(--border); border-radius: 8px; } +.space-shooter-damage-prev { + flex: 0 0 56px; +} +.space-shooter-damage-file { + max-width: 200px; + font-size: 0.78rem; +} + +.space-shooter-asteroid-urls-ta { + width: 100%; + max-width: 720px; + min-height: 9rem; + margin-top: 0.35rem; + padding: 0.55rem 0.65rem; + font-family: ui-monospace, monospace; + font-size: 0.82rem; + line-height: 1.45; + border-radius: 8px; + border: 1px solid var(--border); + background: rgba(0, 0, 0, 0.25); + color: var(--text); + resize: vertical; + box-sizing: border-box; +} diff --git a/www/html/Admin/admin.js b/www/html/Admin/admin.js index 4b16960..f54bf5c 100644 --- a/www/html/Admin/admin.js +++ b/www/html/Admin/admin.js @@ -2674,6 +2674,100 @@ return urls; } + function updateSpaceShooterDamageOverlayPreview(hit) { + var img = el('space-shooter-damage-prev-' + hit); + var inp = el('space-shooter-damage-url-' + hit); + if (!img) return; + var v = inp && inp.value ? String(inp.value).trim() : ''; + if (!v) { + img.removeAttribute('src'); + img.alt = ''; + return; + } + if (!/^https?:\/\//i.test(v) && v.charAt(0) !== '/') v = '/' + v.replace(/^\/+/, ''); + img.alt = 'Damage overlay hit ' + hit; + img.src = v; + } + + function readSpaceShooterShipDamageOverlayUrlsFromForm() { + var urls = []; + for (var h = 1; h <= 3; h++) { + var inp = el('space-shooter-damage-url-' + h); + urls.push(inp && inp.value ? String(inp.value).trim() : ''); + } + return urls; + } + + function bindSpaceShooterDamageOverlayPanel() { + for (var h = 1; h <= 3; h++) { + (function (hit) { + var inp = el('space-shooter-damage-url-' + hit); + var btnClr = el('btn-space-shooter-damage-clear-' + hit); + var btnUp = el('btn-space-shooter-damage-upload-' + hit); + var fileInp = el('space-shooter-damage-file-' + hit); + if (inp) { + inp.addEventListener('change', function () { updateSpaceShooterDamageOverlayPreview(hit); }); + inp.addEventListener('blur', function () { updateSpaceShooterDamageOverlayPreview(hit); }); + } + if (btnClr) { + btnClr.addEventListener('click', function () { + var i = el('space-shooter-damage-url-' + hit); + if (i) i.value = ''; + if (fileInp) fileInp.value = ''; + updateSpaceShooterDamageOverlayPreview(hit); + }); + } + if (btnUp && fileInp) { + btnUp.addEventListener('click', function () { + if (!fileInp.files || !fileInp.files[0]) { + setMsg('space-shooter-timing-msg', 'เลือกไฟล์รูปก่อน · Pick an image file first', 'error'); + return; + } + readFileAsDataURL(fileInp.files[0]) + .then(function (dataUrl) { + return gauntletAssetsJsonFetch(GAME_GAUNTLET_ASSETS_API + '/upload', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + imageDataUrl: dataUrl, + label: 'space-shooter-damage-hit-' + hit, + }), + }); + }) + .then(function (j) { + if (!j || !j.ok || !j.url) { + throw new Error((j && j.error) || 'อัปโหลดไม่สำเร็จ · Upload failed'); + } + var inpU = el('space-shooter-damage-url-' + hit); + if (inpU) inpU.value = String(j.url).trim(); + updateSpaceShooterDamageOverlayPreview(hit); + setMsg( + 'space-shooter-timing-msg', + 'อัปโหลด overlay ช่อง ' + hit + ' แล้ว — กดบันทึกด้านล่างเพื่อเก็บลง game-timing.json · Uploaded; click Save', + 'ok' + ); + }) + .catch(function (e) { + setMsg('space-shooter-timing-msg', e.message || 'อัปโหลดไม่สำเร็จ', 'error'); + }); + }); + } + })(h); + } + } + + function parseSpaceShooterAsteroidSpriteUrlsTextarea() { + var ta = el('space-shooter-asteroid-sprite-urls'); + var raw = ta && ta.value ? String(ta.value) : ''; + var lines = raw.split(/[\n\r]+/); + var out = []; + for (var i = 0; i < lines.length && out.length < 32; i++) { + var t = lines[i].trim(); + if (t) out.push(t); + } + return out; + } + function loadSpaceShooterTimingPanel() { gameTimingFetch('GET') .then(function (data) { @@ -2689,6 +2783,24 @@ if (inpU) inpU.value = urls[k - 1] != null ? String(urls[k - 1]) : ''; updateSpaceShooterShipPreview(k); } + var taAst = el('space-shooter-asteroid-sprite-urls'); + if (taAst) { + var astArr = data.spaceShooterAsteroidSpriteUrls; + if (!Array.isArray(astArr)) astArr = []; + taAst.value = astArr.map(function (u) { return String(u || '').trim(); }).filter(Boolean).join('\n'); + } + var inpFms = el('space-shooter-asteroid-explode-ms'); + if (inpFms) { + var ef = Number(data.spaceShooterAsteroidExplodeFrameMs); + inpFms.value = String(Number.isFinite(ef) ? Math.max(30, Math.min(500, Math.round(ef))) : 70); + } + var dmg = data.spaceShooterShipDamageOverlayUrls; + if (!Array.isArray(dmg)) dmg = []; + for (var dh = 1; dh <= 3; dh++) { + var inpD = el('space-shooter-damage-url-' + dh); + if (inpD) inpD.value = dmg[dh - 1] != null ? String(dmg[dh - 1]) : ''; + updateSpaceShooterDamageOverlayPreview(dh); + } setMsg('space-shooter-timing-msg', '', ''); }) .catch(function (e) { @@ -2701,14 +2813,23 @@ if (Number.isNaN(limSs) || limSs < 0) limSs = 0; limSs = limSs <= 0 ? 0 : Math.max(10, Math.min(7200, limSs)); var shipUrls = readSpaceShooterShipImageUrlsFromForm(); + var damageUrls = readSpaceShooterShipDamageOverlayUrlsFromForm(); + var astUrls = parseSpaceShooterAsteroidSpriteUrlsTextarea(); + var inpFms = el('space-shooter-asteroid-explode-ms'); + var explodeMs = inpFms ? parseInt(String(inpFms.value), 10) : 70; + if (Number.isNaN(explodeMs)) explodeMs = 70; + explodeMs = Math.max(30, Math.min(500, explodeMs)); gameTimingFetch('GET') .then(function (data) { data.spaceShooterMissionTimeSec = limSs; data.spaceShooterShipImageUrls = shipUrls; + data.spaceShooterShipDamageOverlayUrls = damageUrls; + data.spaceShooterAsteroidSpriteUrls = astUrls; + data.spaceShooterAsteroidExplodeFrameMs = explodeMs; return gameTimingFetch('PUT', data); }) .then(function () { - setMsg('space-shooter-timing-msg', 'บันทึกแล้ว — ผู้เล่นยิงยานอวกาศได้ค่าใหม่เมื่อรีเฟรชหน้าเล่นหรือโหลด /api/game-timing', 'ok'); + setMsg('space-shooter-timing-msg', 'บันทึกแล้ว — เวลา / รูปยาน / overlay ความเสียหาย / อุกาบาต มีผลเมื่อรีเฟรชหน้าเล่นหรือโหลด /api/game-timing', 'ok'); }) .catch(function (e) { setMsg('space-shooter-timing-msg', e.message || 'บันทึกไม่ได้', 'error'); @@ -2766,7 +2887,7 @@ return gameTimingFetch('PUT', data); }) .then(function () { - setMsg('game-timing-msg', 'บันทึกแล้ว — พรมแดง tick·กระโดด / รูปอุปสรรค์ · Minigame-5 / Minigame-6 ตั้งแท็บของแต่ละเกม · ค่า Stack แก้ที่แท็บ Stack เท่านั้น · จำกัดเวลาพรมแดงใช้กับรอบถัดไปที่โฮสต์เริ่มเกม', 'ok'); + setMsg('game-timing-msg', 'บันทึกแล้ว — พรมแดง tick·กระโดด / รูปอุปสรรค์ · Minigame-5 / Minigame-6 ตั้งแท็บของแต่ละเกม · ค่า Tower block (Stack) แก้ที่แท็บ Minigame-3 เท่านั้น · จำกัดเวลาพรมแดงใช้กับรอบถัดไปที่โฮสต์เริ่มเกม', 'ok'); }) .catch(function (e) { setMsg('game-timing-msg', e.message || 'บันทึกไม่ได้', 'error'); @@ -2941,6 +3062,7 @@ btnSpaceShooterSave.addEventListener('click', saveSpaceShooterTimingPanel); } bindSpaceShooterShipUrlInputs(); + bindSpaceShooterDamageOverlayPanel(); var btnStackGameSave = el('btn-stack-game-save'); if (btnStackGameSave) { btnStackGameSave.addEventListener('click', saveStackGamePanel); @@ -3251,7 +3373,7 @@ try { want = new URL(window.location.href).searchParams.get('tab'); } catch (e) { /* ignore */ } - var allowed = { 'change-password': true, oauth: true, 'map-editor': true, quiz: true, 'quiz-carry': true, 'quiz-battle': true, 'jump-survive': true, 'space-shooter': true, 'game-timing': true, 'stack-game': true, characters: true, accounts: true, admins: true }; + var allowed = { 'change-password': true, oauth: true, 'map-editor': true, quiz: true, 'quiz-carry': true, 'quiz-battle': true, 'jump-survive': true, 'space-shooter': true, 'mega-virus': true, 'game-timing': true, 'stack-game': true, characters: true, accounts: true, admins: true }; if (!want || !allowed[want]) return 'oauth'; if (want === 'admins' && (!admin || admin.role !== 'super')) return 'oauth'; return want; diff --git a/www/html/Admin/index.html b/www/html/Admin/index.html index e085068..75043b6 100644 --- a/www/html/Admin/index.html +++ b/www/html/Admin/index.html @@ -9,7 +9,7 @@ - + @@ -68,13 +68,14 @@ - + + + - - + @@ -135,7 +136,7 @@

Editor ฉาก (Lobby / ZEP / กบ / ตอบคำถาม)

-

สร้างและบันทึกแผนที่ — ใช้รหัสฉากตอนสร้างห้องในล็อบบี้ · คลิกซ้ายวาด / คลิกขวาลบ · ห้องโถง: พื้นที่เริ่มเกม (ส้ม) · เกมตอบคำถาม: วาดโซนถูก/ผิด + พื้นที่โชว์คำถาม (ทอง) — ข้อความคำถามจากแท็บ คำถามเกม · เลื่อนแถบเครื่องมือด้านบนได้แยกจากพื้นที่วาด

+

สร้างและบันทึกแผนที่ — ใช้รหัสฉากตอนสร้างห้องในล็อบบี้ · คลิกซ้ายวาด / คลิกขวาลบ · ห้องโถง: พื้นที่เริ่มเกม (ส้ม) · เกมตอบคำถาม: วาดโซนถูก/ผิด + พื้นที่โชว์คำถาม (ทอง) — ข้อความคำถามจากแท็บ Minigame-1-จริงหรือไม่ · เลื่อนแถบเครื่องมือด้านบนได้แยกจากพื้นที่วาด

@@ -467,7 +468,7 @@ + +