Files
justice/www/html/Admin/index.html
T
2026-05-03 12:53:43 +00:00

901 lines
105 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="robots" content="noindex, nofollow">
<meta name="theme-color" content="#0b0d14">
<link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any">
<title>Admin — JD JUSTICE DIVERS</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="admin.css?v=30">
</head>
<body>
<a class="skip-link" href="#admin-main">ข้ามไปเนื้อหา</a>
<div class="admin-bg" aria-hidden="true"></div>
<div class="admin-shell">
<header class="admin-header-bar">
<div class="admin-brand">
<span class="admin-brand-mark" aria-hidden="true"></span>
<div class="admin-brand-text">
<h1 class="admin-title">ศูนย์ควบคุม Admin</h1>
<p class="admin-tagline">JD JUSTICE DIVERS</p>
</div>
</div>
<div class="admin-top-actions" id="top-actions" hidden>
<a href="/Admin/justice-git.html" class="btn btn-ghost" style="text-decoration:none;margin-right:0.35rem;">อัป Git (justice)</a>
<span class="admin-user" id="admin-user-label"></span>
<button type="button" class="btn btn-ghost btn-logout" id="btn-logout">ออกจากระบบ</button>
</div>
</header>
<main id="admin-main" class="admin-main-flow">
<section id="panel-setup" class="card card--auth" hidden>
<h2>ตั้งค่าครั้งแรก</h2>
<p class="muted">สร้างบัญชีแอดมินหลัก (super) — ใช้ชื่อผู้ใช้ <strong>admin</strong></p>
<form id="form-setup" class="form-grid">
<label>รหัสผ่าน (อย่างน้อย 8 ตัว)
<input type="password" name="password" required minlength="8" autocomplete="new-password">
</label>
<label>ยืนยันรหัสผ่าน
<input type="password" name="password2" required minlength="8" autocomplete="new-password">
</label>
<button type="submit" class="btn btn-primary">สร้างแอดมิน</button>
</form>
<p id="setup-msg" class="msg" role="status"></p>
</section>
<section id="panel-login" class="card card--auth" hidden>
<h2>เข้าสู่ระบบ Admin</h2>
<form id="form-login" class="form-grid">
<label>ชื่อผู้ใช้
<input type="text" name="username" autocomplete="username" required>
</label>
<label>รหัสผ่าน
<span class="password-input-wrap">
<input type="password" name="password" id="login-password" autocomplete="current-password" required>
<button type="button" class="btn-password-toggle" id="login-password-toggle" aria-pressed="false" aria-label="แสดงรหัสผ่าน" title="แสดง/ซ่อนรหัสผ่าน">แสดง</button>
</span>
</label>
<button type="submit" class="btn btn-primary">เข้าสู่ระบบ</button>
</form>
<p id="login-msg" class="msg" role="status"></p>
</section>
<div id="panel-app" class="admin-app" hidden>
<nav class="admin-tabs" role="tablist" aria-label="เมนูหลัก Admin">
<button type="button" class="tab" data-tab="change-password" role="tab" id="tab-change-password" aria-selected="false" aria-controls="tab-panel-change-password"><span class="tab-label">รหัสผ่าน</span><span class="tab-desc tab-desc--wide">เปลี่ยนรหัสของคุณ · กดซ้ำเพื่อปิด</span></button>
<button type="button" class="tab is-active" data-tab="oauth" role="tab" id="tab-oauth" aria-selected="true" aria-controls="tab-panel-oauth"><span class="tab-label">OAuth</span><span class="tab-desc">Facebook / Google</span></button>
<button type="button" class="tab" data-tab="map-editor" role="tab" id="tab-map-editor" aria-controls="tab-panel-map-editor"><span class="tab-label">Editor</span><span class="tab-desc">แผนที่ฉาก</span></button>
<button type="button" class="tab" data-tab="quiz" role="tab" id="tab-quiz" aria-controls="tab-panel-quiz"><span class="tab-heading-row"><img class="tab-heading-icon" src="/Game/img/QUESTION/admin-tab-minigame-1.png" width="22" height="22" alt="" decoding="async"><span class="tab-label">Minigame-1-จริงหรือไม่</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="game-timing" role="tab" id="tab-game-timing" aria-controls="tab-panel-game-timing"><span class="tab-heading-row"><img class="tab-heading-icon" src="/Game/img/gauntlet-assets/gauntlet-dd51baed17270995.png" width="22" height="22" alt="" decoding="async"><span class="tab-label">Minigame-2-วิ่งหลบสิ่งกีดขวาง</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="stack-game" role="tab" id="tab-stack-game" aria-controls="tab-panel-stack-game"><span class="tab-heading-row"><img class="tab-heading-icon tab-heading-icon--stack-tab" src="/Game/img/TowerBlock/admin-tab-minigame-3.png" width="48" height="18" alt="" decoding="async"><span class="tab-label">Minigame-3-Tower block</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="quiz-carry" role="tab" id="tab-quiz-carry" aria-controls="tab-panel-quiz-carry"><span class="tab-heading-row"><img class="tab-heading-icon tab-heading-icon--wide" src="/Game/img/quiz-carry/admin-tab-minigame-4.png" width="32" height="22" alt="" decoding="async"><span class="tab-label">Minigame-4-คำศัพท์ในกระบวนการยุติธรรม</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="quiz-battle" role="tab" id="tab-quiz-battle" aria-controls="tab-panel-quiz-battle"><span class="tab-label">Quiz Battle</span><span class="tab-desc">โดม · A B C · หมวด</span></button>
<button type="button" class="tab" data-tab="jump-survive" role="tab" id="tab-jump-survive" aria-controls="tab-panel-jump-survive"><span class="tab-heading-row"><img class="tab-heading-icon" src="/Game/img/Jumper/admin-tab-minigame-5.png" width="22" height="22" alt="" decoding="async"><span class="tab-label">Minigame-5-กระโดดขึ้นแท่น</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="space-shooter" role="tab" id="tab-space-shooter" aria-controls="tab-panel-space-shooter"><span class="tab-heading-row"><img class="tab-heading-icon" src="/Game/img/ViolentCrime/Meteo-1.png" width="22" height="22" alt="" decoding="async"><span class="tab-label">Minigame-6-ยิงอุกาบาต Violent Crime</span></span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="mega-virus" role="tab" id="tab-mega-virus" aria-controls="tab-panel-mega-virus"><span class="tab-label">Minigame-7-ยิง Mega Virus</span><span class="tab-desc" aria-hidden="true"></span></button>
<button type="button" class="tab" data-tab="characters" role="tab" id="tab-characters" aria-controls="tab-panel-characters"><span class="tab-label">ตัวละคร</span><span class="tab-desc">อัปโหลด · เลือกใช้ในเกม</span></button>
<button type="button" class="tab" data-tab="accounts" role="tab" id="tab-accounts" aria-controls="tab-panel-accounts"><span class="tab-label">ผู้ใช้</span><span class="tab-desc">บัญชี &amp; COINS</span></button>
<button type="button" class="tab" data-tab="admins" role="tab" id="tab-admins" aria-controls="tab-panel-admins"><span class="tab-label">แอดมิน</span><span class="tab-desc">สิทธิ์ระบบ</span></button>
</nav>
<section id="tab-panel-change-password" class="tab-panel card card--password" hidden role="tabpanel" aria-labelledby="tab-change-password">
<h2 class="card-title-icon">เปลี่ยนรหัสผ่านของคุณ</h2>
<p class="muted">ใช้รหัสปัจจุบันยืนยันก่อนตั้งรหัสใหม่ (ทุกแอดมินใช้ได้) · <strong>กดแท็บ &quot;รหัสผ่าน&quot; อีกครั้งเพื่อปิด</strong>และกลับไป OAuth</p>
<form id="form-change-password" class="form-grid">
<label>รหัสผ่านปัจจุบัน
<input type="password" name="currentPassword" required autocomplete="current-password">
</label>
<label>รหัสผ่านใหม่ (อย่างน้อย 8 ตัว)
<input type="password" name="newPassword" required minlength="8" autocomplete="new-password">
</label>
<label>ยืนยันรหัสผ่านใหม่
<input type="password" name="newPassword2" required minlength="8" autocomplete="new-password">
</label>
<button type="submit" class="btn btn-primary">บันทึกรหัสใหม่</button>
</form>
<p id="password-self-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-oauth" class="tab-panel card" role="tabpanel" aria-labelledby="tab-oauth">
<h2>Token &amp; OAuth (Facebook / Google)</h2>
<p class="muted">เก็บ App ID / Client ID / Secret และ Redirect URI สำหรับ Login — หน้า Login จะดึงเฉพาะค่าที่ไม่เป็นความลับผ่าน <code>oauth-public.php</code></p>
<form id="form-oauth" class="form-grid form-oauth">
<fieldset>
<legend>Facebook Login</legend>
<label>App ID
<input type="text" name="facebookAppId" autocomplete="off" placeholder="ตัวเลขจาก Meta Developer">
</label>
<label>App Secret
<input type="password" name="facebookAppSecret" autocomplete="off" placeholder="ความลับ — ไม่ส่งให้ลูกค้า">
</label>
<label>Redirect URI
<input type="url" name="facebookRedirectUri" placeholder="https://your.domain/Login/facebook-callback.html">
</label>
</fieldset>
<fieldset>
<legend>Google Sign-In</legend>
<label>Client ID
<input type="text" name="googleClientId" autocomplete="off" placeholder="xxx.apps.googleusercontent.com">
</label>
<label>Client Secret
<input type="password" name="googleClientSecret" autocomplete="off">
</label>
<label>Redirect URI
<input type="url" name="googleRedirectUri" placeholder="https://your.domain/Login/google-callback.html">
</label>
</fieldset>
<button type="submit" class="btn btn-primary">บันทึกการตั้งค่า OAuth</button>
</form>
<p id="oauth-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-map-editor" class="tab-panel card admin-editor-panel" hidden role="tabpanel" aria-labelledby="tab-map-editor">
<div class="admin-editor-panel-head">
<div class="admin-editor-panel-intro">
<h2>Editor ฉาก (Lobby / ZEP / กบ / ตอบคำถาม)</h2>
<p class="muted">สร้างและบันทึกแผนที่ — ใช้รหัสฉากตอนสร้างห้องในล็อบบี้ · คลิกซ้ายวาด / คลิกขวาลบ · ห้องโถง: พื้นที่เริ่มเกม (ส้ม) · <strong>เกมตอบคำถาม</strong>: วาดโซนถูก/ผิด + พื้นที่โชว์คำถาม (ทอง) — ข้อความคำถามจากแท็บ <strong>Minigame-1-จริงหรือไม่</strong> · <strong>เลื่อนแถบเครื่องมือด้านบน</strong>ได้แยกจากพื้นที่วาด</p>
</div>
<button type="button" class="btn btn-ghost btn-editor-fs" id="btn-map-editor-fullscreen" aria-pressed="false" title="ขยายเต็มหน้าจอ (Esc ออก)">เต็มจอ</button>
</div>
<iframe class="admin-editor-iframe" id="admin-map-editor-frame" title="Editor ฉาก" src="/Game/editor.html?embed=1"></iframe>
</section>
<section id="tab-panel-quiz" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-quiz">
<h2>คำถามเกม (ถูก / ผิด)</h2>
<p class="muted">ใช้กับฉากประเภท <strong>เกมตอบคำถาม</strong> ใน Editor — จำนวนข้อที่สุ่มต่อรอบตั้งได้ด้านล่าง (สูงสุด 50, ค่าเริ่ม 10) · เวลาเป็นวินาที (เก็บที่เซิร์ฟเวอร์เกม <code>/Game/data/quiz-settings.json</code>) · <em>English:</em> True/false quiz draws up to N shuffled questions per game session.</p>
<fieldset class="quiz-timing-fieldset">
<legend>เวลาในเกม</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label>อ่านคำถาม (วินาที) <input type="number" id="quiz-read-sec" min="1" max="300" step="1" value="10"></label>
<label>เดินไปยืนในโซนตอบ (วินาที) <input type="number" id="quiz-answer-sec" min="1" max="300" step="1" value="5"></label>
<label>พักระหว่างข้อ (วินาที) <input type="number" id="quiz-between-sec" min="0" max="120" step="1" value="3"></label>
</div>
</fieldset>
<fieldset class="quiz-timing-fieldset quiz-round-q-fieldset">
<legend>จำนวนข้อต่อรอบ</legend>
<p class="muted" style="margin:0 0 0.65rem;font-size:0.82rem;line-height:1.45">สุ่มจากชุดคำถามทั้งหมด (Admin + แมป) แล้วเล่นตามลำดับ — ไม่เกินจำนวนข้อที่มีจริง · เก็บที่ <code>quizRoundQuestionCount</code> ใน <code>quiz-settings.json</code> · <em>English:</em> How many shuffled true/false questions per game (150, default 10).</p>
<label class="quiz-round-q-label" title="150 ข้อต่อรอบ">สุ่มสูงสุดกี่ข้อต่อรอบ <input type="number" id="quiz-round-q-count" min="1" max="50" step="1" value="10" aria-label="จำนวนข้อที่สุ่มต่อรอบ 1 ถึง 50"></label>
</fieldset>
<fieldset class="quiz-tf-theme-fieldset" style="margin:0.75rem 0 1rem;padding:0.75rem 1rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated)">
<legend class="quiz-tf-theme-legend" style="font-size:0.92rem;font-weight:600;padding:0 0.35rem">แผงคำถามบนแผนที่ (โซนทองใน Editor)</legend>
<p class="muted" style="margin:0 0 0.65rem;font-size:0.82rem;line-height:1.45">ใช้กับ <code>#quiz-map-question-panel</code> ในโหมด <strong>เกมตอบคำถามถูก/ผิด</strong> · เก็บที่ <code>quizMapPanelTheme</code> ใน <code>quiz-settings.json</code> · <em>English:</em> Panel BG, border, text — same as carry panel theme pickers.</p>
<div class="quiz-carry-theme-grid">
<div class="quiz-carry-theme-row" data-quiz-tf-theme="bg">
<span class="quiz-carry-theme-label">พื้นหลังแผง</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-tf-theme-bg-swatch" value="#0c0e1c" title="เลือกสี" aria-label="สีพื้นหลังแผงคำถามถูกผิด" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-tf-theme-bg-alpha" min="0" max="100" value="88" step="1" aria-label="ความโปร่งใสพื้นหลัง" />
<output id="quiz-tf-theme-bg-alpha-out" class="quiz-carry-alpha-out" for="quiz-tf-theme-bg-alpha">88%</output>
</label>
<code id="quiz-tf-theme-bg-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-tf-theme-bg" value="" />
</div>
<div class="quiz-carry-theme-row" data-quiz-tf-theme="border">
<span class="quiz-carry-theme-label">สีขอบ</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-tf-theme-border-swatch" value="#ffd666" title="เลือกสีขอบ" aria-label="สีขอบแผง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-tf-theme-border-alpha" min="0" max="100" value="70" step="1" aria-label="ความโปร่งใสขอบ" />
<output id="quiz-tf-theme-border-alpha-out" class="quiz-carry-alpha-out" for="quiz-tf-theme-border-alpha">70%</output>
</label>
<code id="quiz-tf-theme-border-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-tf-theme-border" value="" />
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow">
<label class="admin-field quiz-carry-theme-label">ความหนาขอบ (px)
<input type="number" id="quiz-tf-theme-border-w" class="admin-inp-num" min="0" max="12" step="1" value="2" />
</label>
</div>
<div class="quiz-carry-theme-row" data-quiz-tf-theme="text">
<span class="quiz-carry-theme-label">สีข้อความ</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-tf-theme-text-swatch" value="#f1f5f9" title="เลือกสีข้อความ" aria-label="สีข้อความคำถาม" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-tf-theme-text-alpha" min="0" max="100" value="100" step="1" aria-label="ความโปร่งใสข้อความ" />
<output id="quiz-tf-theme-text-alpha-out" class="quiz-carry-alpha-out" for="quiz-tf-theme-text-alpha">100%</output>
</label>
<code id="quiz-tf-theme-text-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-tf-theme-text" value="" />
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow">
<label class="admin-field quiz-carry-theme-label">ขนาดตัวอักษรต่ำสุด (px)
<input type="number" id="quiz-tf-theme-qfont-min" class="admin-inp-num" min="10" max="40" step="1" value="10" />
</label>
<label class="admin-field quiz-carry-theme-label">สูงสุด (px)
<input type="number" id="quiz-tf-theme-qfont-max" class="admin-inp-num" min="14" max="56" step="1" value="24" />
</label>
</div>
</div>
</fieldset>
<h3 class="admin-subheading">รายการคำถาม</h3>
<p class="muted">แต่ละข้อ: ข้อความ + คำตอบที่<strong>ถูกต้อง</strong> (ถูก=จริง / ผิด=เท็จ)</p>
<div id="quiz-admin-questions-list" class="quiz-admin-questions-list"></div>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-ghost" id="btn-quiz-admin-add">+ เพิ่มคำถาม</button>
<button type="button" class="btn btn-primary" id="btn-quiz-admin-save">บันทึกทั้งหมด</button>
</div>
<p id="quiz-settings-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-quiz-carry" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-quiz-carry">
<h2>คำถามหลายตัวเลือก (หยิบมาวาง)</h2>
<p class="muted">ใช้กับฉากประเภท <strong>ตอบคำถาม — หยิบคำตอบมาวางกลาง</strong> ใน Editor — ผู้เล่นหยิบตัวเลือกไปวางโซนกลาง · ถ้ามีรายการที่นี่ เกมจะใช้<strong>เฉพาะชุดนี้</strong> (ไม่ผสมกับคำถามถูก/ผิดแท็บคำถามเกม) · เก็บที่ <code>/Game/data/quiz-settings.json</code> ฟิลด์ <code>carryQuestions</code></p>
<p class="muted" style="margin-top:-0.35rem">อย่างน้อย <strong>2 ตัวเลือก</strong>ต่อข้อ (สูงสุด 6) · <em>English:</em> Per-round timing below applies only to <strong>quiz carry</strong> (after 3-2-1 in embed preview: question first, then options).</p>
<div class="admin-form-row" style="margin:0.75rem 0 1rem;flex-wrap:wrap;gap:1rem">
<label class="admin-field">อ่านคำถามก่อนหยิบตัวเลือก (วินาที)
<input type="number" id="quiz-carry-read-sec" class="admin-inp-num" min="0" max="120" step="1" value="3" aria-describedby="quiz-carry-read-hint" />
<span id="quiz-carry-read-hint" class="muted" style="display:block;font-size:0.85rem;margin-top:0.2rem">0 = แสดงตัวเลือกทันทีหลังคำถามขึ้น · สูงสุด 120 วิ</span>
</label>
<label class="admin-field">เวลาตอบหลังตัวเลือกขึ้น (วินาที)
<input type="number" id="quiz-carry-answer-sec" class="admin-inp-num" min="1" max="300" step="1" value="5" aria-describedby="quiz-carry-answer-hint" />
<span id="quiz-carry-answer-hint" class="muted" style="display:block;font-size:0.85rem;margin-top:0.2rem">นับจากเมื่อหยิบได้ · สูงสุด 300 วิ</span>
</label>
<label class="admin-field">จำนวนข้อต่อเซสชันก่อนจบ (พรีวิว)
<input type="number" id="quiz-carry-session-len" class="admin-inp-num" min="0" max="500" step="1" value="0" aria-describedby="quiz-carry-session-hint" />
<span id="quiz-carry-session-hint" class="muted" style="display:block;font-size:0.85rem;margin-top:0.2rem">สุ่มคำถามจากชุดจนกว่าจะครบจำนวนนี้ (นับทุกข้อที่จบด้วยถูกหรือหมดเวลา) · 0 = เล่นต่อไม่จบอัตโนมัติ · <em>English:</em> Preview / editor embed only.</span>
</label>
</div>
<div class="admin-form-row" style="margin:0 0 1rem;flex-wrap:wrap;gap:1rem;align-items:flex-end">
<label class="admin-field">ความเร็วเดิน — รหัสฉาก (map id)
<input type="text" id="quiz-carry-walk-map-id" class="admin-inp-num" style="width:11rem;max-width:100%" maxlength="64" pattern="[a-zA-Z0-9_-]*" placeholder="เช่น mnorwqx1" autocomplete="off" spellcheck="false" aria-describedby="quiz-carry-walk-map-hint" />
<span id="quiz-carry-walk-map-hint" class="muted" style="display:block;font-size:0.85rem;margin-top:0.2rem">ใส่<strong>รหัสฉาก</strong>จาก Editor (หลังบันทึก) ให้ตรงกับแมปที่ต้องการ · ว่าง = ไม่ override · เก็บที่ <code>carryWalkSpeedMultForMapId</code> · <em>English:</em> Scene id must match the map you want to speed up.</span>
</label>
<label class="admin-field">คูณความเร็วเดิน (0.5–3)
<input type="number" id="quiz-carry-walk-mult" class="admin-inp-num" min="0.5" max="3" step="0.05" value="1.42" aria-describedby="quiz-carry-walk-mult-hint" />
<span id="quiz-carry-walk-mult-hint" class="muted" style="display:block;font-size:0.85rem;margin-top:0.2rem">ใช้เฉพาะเมื่อรหัสฉากตรงกับห้องเล่น/พรีวิว · ค่าเริ่มในเกมถ้าไม่ตั้ง = <strong>1.42</strong> · <code>carryWalkSpeedMult</code></span>
</label>
</div>
<fieldset class="quiz-carry-theme-fieldset" style="margin:0.75rem 0 1rem;padding:0.75rem 1rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated)">
<legend class="quiz-carry-theme-legend" style="font-size:0.92rem;font-weight:600;padding:0 0.35rem">แผงข้อความบนแผนที่ (คำถาม / ตัวเลือกที่ถือ)</legend>
<p class="muted" style="margin:0 0 0.65rem;font-size:0.82rem;line-height:1.45">ใช้กับ <code>#quiz-map-question-panel</code> ในโหมดหยิบมาวาง (โซนทองหรือโซนกลาง) · เก็บที่ <code>carryMapPanelTheme</code> ใน <code>quiz-settings.json</code> · <em>English:</em> Color pickers + alpha (0100%) — saved as <code>rgba()</code>.</p>
<p class="muted" style="margin:-0.35rem 0 0.65rem;padding:0.5rem 0.65rem;border-radius:8px;border:1px solid rgba(255,180,80,0.35);background:rgba(255,200,120,0.08);font-size:0.8rem;line-height:1.5"><strong>ทำไมไม่เห็นเปลี่ยน?</strong> แผงนี้แสดงเฉพาะในหน้า <strong>เล่นเกม</strong> (<code>/Game/play.html</code>) ขณะแมปเป็น <strong>quiz_carry</strong> และมีข้อความคำถาม — ไม่แสดงในหน้า Admin นี้ · ถ้า <strong>โปร่ง A = 0%</strong> กับพื้นหลังหรือขอบ = โปร่งใสทั้งหมด จะไม่เห็นสีนั้น · ความหนาขอบ <strong>0 px</strong> = ไม่มีเส้นขอบ · ลองโปร่ง 70–90% และขอบ 2 px แล้วเปิดพรีวิว/เล่น · <em>English:</em> This panel is only on <strong>play</strong> (not Admin). <strong>0% alpha</strong> = fully transparent color; <strong>0 px</strong> border = no stroke.</p>
<div class="quiz-carry-theme-grid">
<div class="quiz-carry-theme-row" data-quiz-carry-theme="bg">
<span class="quiz-carry-theme-label">พื้นหลังแผง</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-theme-bg-swatch" value="#0c0e1c" title="เลือกสี" aria-label="สีพื้นหลังแผง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-theme-bg-alpha" min="0" max="100" value="88" step="1" aria-label="ความโปร่งใสพื้นหลังแผง เปอร์เซ็นต์" />
<output id="quiz-carry-theme-bg-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-theme-bg-alpha">88%</output>
</label>
<code id="quiz-carry-theme-bg-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-theme-bg" value="" />
</div>
<div class="quiz-carry-theme-row" data-quiz-carry-theme="border">
<span class="quiz-carry-theme-label">สีขอบ</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-theme-border-swatch" value="#ffd666" title="เลือกสีขอบ" aria-label="สีขอบแผง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-theme-border-alpha" min="0" max="100" value="70" step="1" aria-label="ความโปร่งใสขอบ เปอร์เซ็นต์" />
<output id="quiz-carry-theme-border-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-theme-border-alpha">70%</output>
</label>
<code id="quiz-carry-theme-border-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-theme-border" value="" />
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow">
<label class="admin-field quiz-carry-theme-label">ความหนาขอบ (px)
<input type="number" id="quiz-carry-theme-border-w" class="admin-inp-num" min="0" max="12" step="1" value="2" />
</label>
</div>
<div class="quiz-carry-theme-row" data-quiz-carry-theme="text">
<span class="quiz-carry-theme-label">สีตัวอักษร</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-theme-text-swatch" value="#f1f5f9" title="เลือกสีตัวอักษร" aria-label="สีข้อความบนแผง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-theme-text-alpha" min="0" max="100" value="100" step="1" aria-label="ความโปร่งใสตัวอักษร เปอร์เซ็นต์" />
<output id="quiz-carry-theme-text-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-theme-text-alpha">100%</output>
</label>
<code id="quiz-carry-theme-text-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-theme-text" value="" />
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow" style="flex-wrap:wrap;gap:0.75rem 1.25rem;align-items:flex-end">
<label class="admin-field quiz-carry-theme-label">ขั้นต่ำ (px)
<input type="number" id="quiz-carry-theme-qfont-min" class="admin-inp-num" min="10" max="40" step="1" value="10" title="ขั้นต่ำ px หลังสูตรเดียวกับป้ายคำตอบ" aria-describedby="quiz-carry-theme-qfont-hint" />
</label>
<label class="admin-field quiz-carry-theme-label">เพดานขนาด (px)
<input type="number" id="quiz-carry-theme-qfont-max" class="admin-inp-num" min="14" max="56" step="1" value="24" title="เพดาน px (ค่าเริ่ม 24 = เหมือนป้ายคำตอบ)" />
</label>
</div>
<p id="quiz-carry-theme-qfont-hint" class="muted" style="margin:-0.25rem 0 0;font-size:0.78rem;line-height:1.45">ในเกมใช้<strong>สูตรเดียวกับป้ายคำตอบบนพื้น</strong>: <code>clamp(10, 24, tileSize×zoom×0.24×carryChoicePlaqueMapScale)</code> แล้ว clamp อีกชั้นด้วยค่าสองช่องนี้ · <em>English:</em> Same px formula as floor answer plaques; theme min/max clamp on top.</p>
</div>
</fieldset>
<fieldset class="quiz-carry-theme-fieldset" style="margin:0.75rem 0 1rem;padding:0.75rem 1rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated)">
<legend class="quiz-carry-theme-legend" style="font-size:0.92rem;font-weight:600;padding:0 0.35rem">ป้ายตัวเลือกบนแผนที่ (กล่องหยิบมาวาง) — ช่อง 1–16</legend>
<p class="muted" style="margin:0 0 0.65rem;font-size:0.82rem;line-height:1.45">แต่ละช่องตรงกับ <strong>โซนตัวเลือก 116</strong> บนแมป · เก็บที่ <code>carryChoicePlaqueThemes</code> · <strong>Neon</strong> = ขอบตามเลขช่อง · <strong>สีขอบคงที่</strong> = ใช้ picker ด้านล่าง · รูปช่องใช้เมื่อคำถามนั้นไม่มีรูปแยก · รูปคำถามทับช่อง · <strong>ขนาดป้ายบนแมป</strong> ปรับที่กล่องสีเขียวถัดลงนี้ (ไม่ใช่ในแต่ละช่อง) · <em>English:</em> Per-slot = colors/URL; map size = green panel below; Save quiz-carry.</p>
<div class="quiz-carry-plaque-map-scale-panel" role="region" aria-labelledby="quiz-carry-plaque-map-scale-title">
<div class="quiz-carry-plaque-map-scale-panel__title" id="quiz-carry-plaque-map-scale-title">ปรับขนาดป้าย + รูปบนแมป</div>
<p class="muted" style="margin:0.35rem 0 0.65rem;font-size:0.82rem;line-height:1.45">เลื่อนแถบหรือพิมพ์ตัวเลข (×) — ขยายทั้งกล่องป้ายและตัวอักษรบนแมป · จบด้วยปุ่ม <strong>บันทึกชุดหลายตัวเลือก</strong> ด้านล่าง · <em>English:</em> Slider or number, then Save.</p>
<div class="quiz-carry-plaque-map-scale-panel__row">
<label class="admin-field quiz-carry-plaque-map-scale-range-wrap"><span class="quiz-carry-plaque-map-scale-label">เลื่อนขยาย</span>
<input type="range" id="quiz-carry-plaque-map-scale-range" min="85" max="250" step="1" value="125" aria-label="ขยายป้ายบนแมป 0.85 ถึง 2.5 เท่า" />
</label>
<label class="admin-field"><span>ค่า ×</span>
<input type="number" id="quiz-carry-plaque-map-scale" class="admin-inp-num" min="0.85" max="2.5" step="0.05" value="1.25" title="คูณขนาดกล่องป้าย + ฟอนต์บนแผนที่" aria-describedby="quiz-carry-plaque-map-scale-hint" />
</label>
<span id="quiz-carry-plaque-map-scale-out" class="quiz-carry-plaque-map-scale-out" aria-live="polite">× 1.25</span>
</div>
<span id="quiz-carry-plaque-map-scale-hint" class="muted" style="font-size:0.76rem;display:block;margin-top:0.45rem"><code>carryChoicePlaqueMapScale</code> · 1 = ปกติ · 1.5–2.0 ถ้าต้องการใหญ่ชัด</span>
</div>
<div id="quiz-carry-plaque-slots-root" class="quiz-carry-plaque-slots-root" aria-label="ธีมป้ายตัวเลือกทีละช่อง"></div>
</fieldset>
<fieldset class="quiz-carry-theme-fieldset" style="margin:0.75rem 0 1rem;padding:0.75rem 1rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated)">
<legend class="quiz-carry-theme-legend" style="font-size:0.92rem;font-weight:600;padding:0 0.35rem">นับถอยหลัง 3-2-1 (พรีวิว embed)</legend>
<p class="muted" style="margin:0 0 0.65rem;font-size:0.82rem;line-height:1.45">ใช้กับ <code>#quiz-carry-embed-countdown</code> ตอนพรีวิวจากเอดิเตอร์ · เก็บที่ <code>carryEmbedCountdownTheme</code> ใน <code>quiz-settings.json</code> · <em>English:</em> Overlay, inner card, digit color, and size (map = grid box / screen = center).</p>
<div class="quiz-carry-theme-grid">
<div class="quiz-carry-theme-row" data-quiz-carry-ecd="overlay">
<span class="quiz-carry-theme-label">ม่านมืดเต็มจอ</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-ecd-overlay-swatch" value="#080a14" title="สีม่าน" aria-label="สีม่านพื้นหลัง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-ecd-overlay-alpha" min="0" max="100" value="42" step="1" aria-label="ความทึบม่าน" />
<output id="quiz-carry-ecd-overlay-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-ecd-overlay-alpha">42%</output>
</label>
<code id="quiz-carry-ecd-overlay-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-ecd-overlay-val" value="" />
</div>
<div class="quiz-carry-theme-row" data-quiz-carry-ecd="innerBg">
<span class="quiz-carry-theme-label">พื้นหลังกล่องตัวเลข</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-ecd-inner-bg-swatch" value="#0c0e1c" title="พื้นหลังกล่อง" aria-label="พื้นหลังกล่องนับถอยหลัง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-ecd-inner-bg-alpha" min="0" max="100" value="82" step="1" aria-label="โปร่งพื้นหลังกล่อง" />
<output id="quiz-carry-ecd-inner-bg-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-ecd-inner-bg-alpha">82%</output>
</label>
<code id="quiz-carry-ecd-inner-bg-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-ecd-inner-bg-val" value="" />
</div>
<div class="quiz-carry-theme-row" data-quiz-carry-ecd="innerBorder">
<span class="quiz-carry-theme-label">สีขอบกล่อง</span>
<div class="quiz-carry-color-controls">
<input type="color" id="quiz-carry-ecd-inner-border-swatch" value="#7aa2f7" title="สีขอบ" aria-label="สีขอบกล่อง" />
<label class="quiz-carry-alpha-label"><span>โปร่ง A</span>
<input type="range" id="quiz-carry-ecd-inner-border-alpha" min="0" max="100" value="45" step="1" aria-label="โปร่งขอบ" />
<output id="quiz-carry-ecd-inner-border-alpha-out" class="quiz-carry-alpha-out" for="quiz-carry-ecd-inner-border-alpha">45%</output>
</label>
<code id="quiz-carry-ecd-inner-border-preview" class="quiz-carry-theme-code" aria-hidden="true"></code>
</div>
<input type="hidden" id="quiz-carry-ecd-inner-border-val" value="" />
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow" style="flex-wrap:wrap;gap:0.75rem 1.25rem">
<label class="admin-field quiz-carry-theme-label">ความหนาขอบกล่อง (px)
<input type="number" id="quiz-carry-ecd-inner-border-w" class="admin-inp-num" min="0" max="12" step="1" value="1" />
</label>
<label class="admin-field quiz-carry-theme-label">มุมโค้งกล่อง (px)
<input type="number" id="quiz-carry-ecd-inner-radius" class="admin-inp-num" min="0" max="32" step="1" value="12" />
</label>
<label class="admin-field quiz-carry-theme-label">สีตัวเลข
<input type="color" id="quiz-carry-ecd-digit-swatch" value="#ffe066" title="สีตัวเลข 3-2-1" aria-label="สีตัวเลขนับถอยหลัง" />
</label>
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow" style="flex-wrap:wrap;gap:0.75rem 1.25rem">
<label class="admin-field quiz-carry-theme-label">ขนาดบนแมป — cqmin (% ของช่อง)
<input type="number" id="quiz-carry-ecd-map-cqmin" class="admin-inp-num" min="35" max="100" step="1" value="78" title="ยิ่งมากตัวเลขใหญ่ขึ้นในกรอบช่อง" />
</label>
<label class="admin-field quiz-carry-theme-label">cqh
<input type="number" id="quiz-carry-ecd-map-cqh" class="admin-inp-num" min="35" max="100" step="1" value="82" />
</label>
<label class="admin-field quiz-carry-theme-label">สูงสุด (px)
<input type="number" id="quiz-carry-ecd-map-max-px" class="admin-inp-num" min="48" max="400" step="1" value="200" />
</label>
</div>
<div class="quiz-carry-theme-row quiz-carry-theme-row--narrow" style="flex-wrap:wrap;gap:0.75rem 1.25rem">
<label class="admin-field quiz-carry-theme-label">กลางจอ — vw
<input type="number" id="quiz-carry-ecd-screen-vw" class="admin-inp-num" min="6" max="44" step="1" value="28" title="ความกว้างตัวเลขเทียบ viewport" />
</label>
<label class="admin-field quiz-carry-theme-label">สูงสุด (px)
<input type="number" id="quiz-carry-ecd-screen-max-px" class="admin-inp-num" min="48" max="220" step="1" value="132" />
</label>
</div>
</div>
</fieldset>
<h3 class="admin-subheading">รายการคำถาม</h3>
<p class="muted" style="margin:0.35rem 0 0.65rem;font-size:0.8rem;line-height:1.45"><strong>รูปป้ายแต่ละช่อง:</strong> เลือกไฟล์แล้วกด «บันทึก» ได้เลย — ระบบจะ<strong>รอให้อัปโหลดจบ</strong>แล้วค่อยบันทึก URL ลงเซิร์ฟ (หรือรอเห็น «อัปโหลดแล้ว» ก่อนก็ได้) · ปุ่มบันทึก<strong>ไม่</strong>ส่งไฟล์แทนการอัปโหลด · <em>English:</em> Pick plaque file then Save — we wait for upload to finish before writing URLs (Save does not replace the upload step).</p>
<div id="quiz-carry-admin-list" class="quiz-carry-admin-list"></div>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-ghost" id="btn-quiz-carry-add">+ เพิ่มคำถาม</button>
<button type="button" class="btn btn-primary" id="btn-quiz-carry-save">บันทึกชุดหลายตัวเลือก</button>
</div>
<p id="quiz-carry-settings-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-quiz-battle" class="tab-panel card tab-panel-quiz-battle" hidden role="tabpanel" aria-labelledby="tab-quiz-battle">
<h2>Quiz Battle — คำถาม 3 ตัวเลือก (สไตล์โดม)</h2>
<p class="muted">สร้างคำถามแบบ <strong>A / B / C</strong> แยกตาม<strong>หมวด</strong>ให้สอดคล้องธีม <a href="https://srv1361159.hstgr.cloud/Quiz-Battle/" target="_blank" rel="noopener noreferrer">Quiz Battle</a> · เก็บที่ <code>/Game/data/quiz-settings.json</code> ฟิลด์ <code>battleQuizMcq</code> · ตัวอย่าง UI ด้านขวา (โดม + ป๊อปอัป) จำลองจากรีเฟอเรนซ์ในเกม</p>
<p class="muted" style="margin-top:-0.35rem">เวลาอ่าน/ตอบใช้ร่วมกับแท็บ <strong>คำถามเกม</strong>ได้เมื่อนำไปผูกโหมดเล่นในอนาคต · <em>English:</em> MCQ with category; dome + modal preview for art direction.</p>
<div class="qb-admin-layout">
<div class="qb-admin-form-col">
<div class="qb-admin-toolbar">
<label class="qb-filter-label">กรองรายการตามหมวด
<select id="qb-battle-filter-cat" class="qb-battle-filter-cat" aria-label="กรองหมวด">
<option value="">ทุกหมวด</option>
</select>
</label>
</div>
<h3 class="admin-subheading">รายการคำถาม</h3>
<p class="muted" style="margin:-0.25rem 0 0.5rem;font-size:0.88rem">ก่อนกดบันทึก: ต้องมี<strong>ข้อความคำถาม</strong>และ<strong>ตัวเลือก A B C ครบทั้งสามช่อง</strong>ในแต่ละข้อที่ต้องการเก็บ · ถ้าช่องว่าง ระบบจะไม่บันทึกข้อนั้นและจะแจ้งเตือน</p>
<div id="qb-battle-admin-list" class="qb-battle-admin-list"></div>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-ghost" id="btn-qb-battle-add">+ เพิ่มคำถาม</button>
<button type="button" class="btn btn-primary" id="btn-qb-battle-save">บันทึกชุด Quiz Battle</button>
</div>
<p id="qb-battle-settings-msg" class="msg" role="status"></p>
</div>
<div class="qb-preview-col" aria-label="ตัวอย่าง UI โดมและป๊อปอัป">
<h3 class="admin-subheading qb-preview-heading">ตัวอย่าง (ตามรีเฟอเรนซ์)</h3>
<div class="qb-preview-toolbar">
<span class="qb-preview-toolbar-label">โหมดดูตัวอย่าง</span>
<div class="qb-preview-mode-btns" role="group" aria-label="โหมดตัวอย่าง">
<button type="button" class="btn btn-ghost btn-sm qb-preview-mode-btn is-active" data-qb-mode="question">คำถาม</button>
<button type="button" class="btn btn-ghost btn-sm qb-preview-mode-btn" data-qb-mode="revealed">หลังเลือกตอบ</button>
<button type="button" class="btn btn-ghost btn-sm qb-preview-mode-btn" data-qb-mode="summary">สรุปผล</button>
</div>
</div>
<div class="qb-preview-stage" id="qb-preview-stage">
<div class="qb-preview-hud">
<div class="qb-hud-left">
<span class="qb-hud-icon" aria-hidden="true">💻</span>
<span class="qb-hud-cat" id="qb-preview-hud-cat">อาชญากรรมออนไลน์</span>
</div>
<div class="qb-hud-center">PLAYERS : —</div>
<div class="qb-hud-right">SCORE : —</div>
</div>
<div class="qb-preview-floor">
<div class="qb-path"></div>
<div class="qb-dome-scene">
<div class="qb-dome-float">
<div class="qb-dome-qbadge" aria-hidden="true">?</div>
<div class="qb-dome-chevron" aria-hidden="true"></div>
</div>
<div class="qb-dome-glass">
<div class="qb-dome-scanlines" aria-hidden="true"></div>
<div class="qb-dome-pedestal"></div>
<div class="qb-dome-lock" aria-hidden="true">🔒</div>
</div>
</div>
</div>
<div class="qb-modal-layer" id="qb-modal-layer">
<div class="qb-cyber-modal qb-cyber-modal--question" id="qb-cyber-modal-question">
<div class="qb-cyber-bracket qb-cyber-bracket--tl" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--tr" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--bl" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--br" aria-hidden="true"></div>
<button type="button" class="qb-cyber-close" tabindex="-1" aria-hidden="true">×</button>
<div class="qb-cyber-title-tab">QUESTION #<span id="qb-preview-qnum">1</span></div>
<p class="qb-cyber-qtext" id="qb-preview-modal-q">การกระทำใดผิดกฎหมายไซเบอร์?</p>
<div class="qb-cyber-choices" id="qb-preview-choices">
<div class="qb-cyber-choice" data-slot="0"><span class="qb-cyber-choice-label">A</span><span class="qb-cyber-choice-txt" id="qb-preview-c0">ตัวอย่างตัวเลือก A</span><span class="qb-cyber-choice-mark" aria-hidden="true"></span></div>
<div class="qb-cyber-choice" data-slot="1"><span class="qb-cyber-choice-label">B</span><span class="qb-cyber-choice-txt" id="qb-preview-c1">ตัวอย่างตัวเลือก B</span><span class="qb-cyber-choice-mark" aria-hidden="true"></span></div>
<div class="qb-cyber-choice" data-slot="2"><span class="qb-cyber-choice-label">C</span><span class="qb-cyber-choice-txt" id="qb-preview-c2">ตัวอย่างตัวเลือก C</span><span class="qb-cyber-choice-mark" aria-hidden="true"></span></div>
</div>
</div>
<div class="qb-cyber-modal qb-cyber-modal--summary" id="qb-cyber-modal-summary" hidden>
<div class="qb-cyber-bracket qb-cyber-bracket--tl" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--tr" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--bl" aria-hidden="true"></div>
<div class="qb-cyber-bracket qb-cyber-bracket--br" aria-hidden="true"></div>
<button type="button" class="qb-cyber-close" tabindex="-1" aria-hidden="true">×</button>
<div class="qb-summary-header">Completed</div>
<p class="qb-summary-msg">เย่! คุณตอบคำถามครบทุกข้อแล้ว มาสรุปคะแนนกัน</p>
<div class="qb-summary-stats">
<div class="qb-summary-card">
<span class="qb-summary-card-icon" aria-hidden="true"></span>
<span class="qb-summary-card-label">Correct</span>
<span class="qb-summary-card-val qb-summary-card-val--green">10</span>
</div>
<div class="qb-summary-card">
<span class="qb-summary-card-icon" aria-hidden="true">🕐</span>
<span class="qb-summary-card-label">Time Taken</span>
<span class="qb-summary-card-val qb-summary-card-val--cyan">27 m 33 s</span>
</div>
<div class="qb-summary-card">
<span class="qb-summary-card-icon" aria-hidden="true">🏆</span>
<span class="qb-summary-card-label">My Rank</span>
<span class="qb-summary-card-val qb-summary-card-val--gold">11</span>
</div>
</div>
<div class="qb-summary-footer-btn">ดูรายละเอียดเพิ่มเติม</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="tab-panel-jump-survive" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-jump-survive">
<h2>กระโดดให้รอด — ตั้งค่าเกม</h2>
<p class="muted">คนละโหมดกับ <strong>พรมแดง (Gauntlet)</strong> · เก็บที่ <code>/Game/data/game-timing.json</code> ฟิลด์ <code>jumpSurviveJumpHeightMult</code> และ <code>jumpSurviveMissionTimeSec</code> · ผู้เล่นได้ค่าใหม่เมื่อโหลดหน้าเล่น / <code>GET /api/game-timing</code> · ถ้า API 404 ให้รีสตาร์ท Node ที่รัน <code>Game/server.js</code></p>
<fieldset class="quiz-timing-fieldset">
<legend>ความสูงกระโดด</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="1 = สูงเท่าความสูงตัวละครจากแมป · 1.5 = สูงกว่าตัวครึ่งหนึ่ง">ทวีคูณความสูงกระโดด × ความสูงตัว <input type="number" id="jump-survive-height-mult" min="0.5" max="4" step="0.05" value="1.5"></label>
</div>
</fieldset>
<p class="muted" style="margin-top:-0.25rem;margin-bottom:0.65rem">ใช้กับฉากประเภท <strong>กระโดดให้รอด</strong> เท่านั้น · ถ้าในแมปตั้ง <code>jumpSurviveJumpImpulse</code> &gt; 0 จะใช้ค่านั้นแทน (override) · <em>English:</em> Jump height multiplier vs character height; per-map impulse overrides.</p>
<fieldset class="quiz-timing-fieldset">
<legend>จำกัดเวลารอบ (มินิเกม / ภารกิจกระโดดขึ้นแท่น)</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="0 = ใช้ค่าเริ่ม 60 วินาทีในเกม · ตั้งอย่างน้อย 10 วินาทีถ้าต้องการกำหนดเอง">เวลารอบ (วินาที) <input type="number" id="jump-survive-mission-sec" min="0" max="7200" step="5" value="0"></label>
</div>
</fieldset>
<p class="muted" style="margin-top:-0.25rem;margin-bottom:0.65rem">นับจากเริ่มรอบจริง (หลังนับถอยหลัง) · ถ้าในแมปตั้ง <code>jumpSurviveTimeSec</code> &gt; 0 จะใช้ค่าบนแมปแทนทั้งหมด · ไม่ใช่ค่าเดียวกับ &quot;จำกัดเวลาเกม&quot; ของพรมแดง · <em>English:</em> Per-map <code>jumpSurviveTimeSec</code> overrides this global default.</p>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-primary" id="btn-jump-survive-save">บันทึก</button>
</div>
<p id="jump-survive-timing-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-space-shooter" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-space-shooter">
<h2>ยิงยานอวกาศ (Violent Crime / space_shooter) — เวลารอบ + รูปยานต่อช่อง</h2>
<p class="muted">เก็บที่ <code>/Game/data/game-timing.json</code><code>spaceShooterMissionTimeSec</code> · <code>spaceShooterShipImageUrls</code> · <code>spaceShooterShipDamageOverlayUrls</code> · <code>spaceShooterAsteroidIntervalMs</code> · <code>spaceShooterAsteroidSpriteUrls</code> + <code>spaceShooterAsteroidExplodeFrameMs</code> · ผู้เล่นได้ค่าใหม่เมื่อโหลดหน้าเล่น / <code>GET /api/game-timing</code> · อัปโหลด PNG ไปที่ <code>/Game/public/img/ViolentCrime/</code> (โฟลเดอร์<strong>ไม่มีช่องว่าง</strong>) · อัปโหลด overlay ผ่านคลัง Gauntlet (<code>/Game/img/gauntlet-assets/</code>)</p>
<fieldset class="quiz-timing-fieldset">
<legend>จำกัดเวลารอบ (ยิงอุกาบาต)</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="0 = ใช้ค่าเริ่ม 90 วินาทีในเกม · ตั้งอย่างน้อย 10 วินาทีถ้าต้องการกำหนดเอง">เวลารอบ (วินาที) <input type="number" id="space-shooter-mission-sec" min="0" max="7200" step="5" value="0"></label>
</div>
</fieldset>
<fieldset class="quiz-timing-fieldset">
<legend>อุกาบาต — อัตราการเกิด (ความถี่ร่วง)</legend>
<p class="muted" style="margin-top:0">ระยะห่างระหว่างเกิดดวงใหม่ (มิลลิวินาที) · <strong>ค่าน้อย = อุกาบาตถี่ขึ้น</strong> (เช่น 400 ถี่กว่า 2000) · 200–10000 ms · ทุกแมป space_shooter โหลดค่านี้จากเซิร์ฟเวอร์เมื่อเข้าเกม · ถ้าในแมปตั้ง <code>spaceShooterAsteroidIntervalMs</code> ≥ 200 จะใช้ค่าบนแมปแทนค่านี้ · <em>English:</em> Lower ms = denser rain. Map ≥200 overrides.</p>
<div class="form-grid form-inline quiz-timing-grid">
<label title="มิลลิวินาทีระหว่างเกิดดวงใหม่ — ค่าเริ่มในเกมเดิม 1040">ช่วงเวลาเกิด (ms) <input type="number" id="space-shooter-asteroid-interval-ms" min="200" max="10000" step="10" value="1040"></label>
</div>
</fieldset>
<fieldset class="quiz-timing-fieldset">
<legend>รูปยานต่อช่อง (spawn slot 16)</legend>
<p class="muted" style="margin-top:0">ช่องตรงกับ <code>shooterSpawnSlots</code> บนแมป (1–6) · ว่าง = ใช้ยานวาดเวกเตอร์เดิม · <em>English:</em> One image URL per map slot; empty keeps the default vector ship.</p>
<div class="space-shooter-ship-grid" role="group" aria-label="Space shooter ship images">
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">1</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship1.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-1" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-1">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">2</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship2.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-2" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-2">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">3</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship3.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-3" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-3">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">4</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-4" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship4.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-4" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-4">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">5</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-5" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship5.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-5" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-5">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">6</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ship-url-6" maxlength="500" spellcheck="false" placeholder="/Game/img/ViolentCrime/ship6.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="space-shooter-ship-prev-6" alt="" width="48" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ship-clear-6">ล้าง</button></div>
</div>
</fieldset>
<fieldset class="quiz-timing-fieldset">
<legend>รอยความเสียหายทับยาน (ชนอุกาบาต ครั้งที่ 1–3)</legend>
<p class="muted" style="margin-top:0">ใช้ PNG/WebP โปร่งพื้นหลัง · แสดงทับรูปยานตามจำนวนครั้งที่โดน (ก่อน OUT) · <em>English:</em> Three optional overlays for hit 1 / 2 / 3; scales with the ship footprint.</p>
<div class="space-shooter-ship-grid" role="group" aria-label="Space shooter damage overlays">
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot" title="โดนครั้งที่ 1">1</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-damage-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/gauntlet-assets/....png" autocomplete="off"></label><input type="file" id="space-shooter-damage-file-1" class="space-shooter-damage-file" accept="image/png,image/webp,image/jpeg,image/gif"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-upload-1">อัปโหลด</button><img class="space-shooter-ship-prev space-shooter-damage-prev" id="space-shooter-damage-prev-1" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-clear-1">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot" title="โดนครั้งที่ 2">2</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-damage-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/gauntlet-assets/....png" autocomplete="off"></label><input type="file" id="space-shooter-damage-file-2" class="space-shooter-damage-file" accept="image/png,image/webp,image/jpeg,image/gif"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-upload-2">อัปโหลด</button><img class="space-shooter-ship-prev space-shooter-damage-prev" id="space-shooter-damage-prev-2" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-clear-2">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot" title="โดนครั้งที่ 3">3</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-damage-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/gauntlet-assets/....png" autocomplete="off"></label><input type="file" id="space-shooter-damage-file-3" class="space-shooter-damage-file" accept="image/png,image/webp,image/jpeg,image/gif"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-upload-3">อัปโหลด</button><img class="space-shooter-ship-prev space-shooter-damage-prev" id="space-shooter-damage-prev-3" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-damage-clear-3">ล้าง</button></div>
</div>
</fieldset>
<fieldset class="quiz-timing-fieldset">
<legend>อุกาบาต — PNG sequence</legend>
<p class="muted" style="margin-top:0"><strong>แถวแรก</strong> = รูปตอนตก (loop ขณะล่วง) · <strong>แถวถัดไป</strong> = เฟรมแตกตามลำดับหลังโดนยิง/ชน · รวมได้สูงสุด 32 URL · อัปโหลดผ่านคลัง Gauntlet เหมือนรอยความเสียหาย · <em>English:</em> First row = falling sprite; add rows for explosion frames (max 31 extra).</p>
<div class="space-shooter-ship-grid" role="group" aria-label="อุกาบาต รูปตอนตก">
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot space-shooter-ship-slot--ast" title="รูปขณะอุกาบาตร่วง">ตก</span><label class="space-shooter-ship-url-label">URL <input type="text" id="space-shooter-ast-fall-url" maxlength="500" spellcheck="false" placeholder="/Game/img/gauntlet-assets/....png" autocomplete="off"></label><input type="file" id="space-shooter-ast-fall-file" class="space-shooter-damage-file" accept="image/png,image/webp,image/jpeg,image/gif"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ast-fall-upload">อัปโหลด</button><img class="space-shooter-ship-prev space-shooter-damage-prev" id="space-shooter-ast-fall-prev" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-space-shooter-ast-fall-clear">ล้าง</button></div>
</div>
<p class="muted" style="margin:0.75rem 0 0.35rem">เฟรมแตก (ลำดับหลังยิง/ชน)</p>
<div id="space-shooter-ast-explode-rows" class="space-shooter-ship-grid" role="group" aria-label="เฟรมแตกอุกาบาต"></div>
<div class="space-shooter-ast-explode-actions" style="margin-top:0.5rem;display:flex;flex-wrap:wrap;gap:0.5rem;align-items:center">
<button type="button" class="btn btn-ghost" id="btn-space-shooter-ast-add-explode">+ เพิ่มเฟรมแตก</button>
<span class="muted" style="font-size:0.85rem">สูงสุด 31 แถว · <em>English:</em> Up to 31 explosion rows.</span>
</div>
<div class="form-grid form-inline quiz-timing-grid" style="margin-top:0.65rem">
<label title="ความเร็วเฟรมแอนิเมชันแตก (มิลลิวินาทีต่อเฟรม)">เฟรมแตก (ms) <input type="number" id="space-shooter-asteroid-explode-ms" min="30" max="500" step="5" value="70"></label>
</div>
</fieldset>
<p class="muted" style="margin-top:-0.25rem;margin-bottom:0.65rem">นับจากเริ่มรอบในเกม · ถ้าในแมปตั้ง <code>spaceShooterTimeSec</code> &gt; 0 จะใช้ค่าบนแมปแทนทั้งหมด · ตั้ง <code>spaceShooterTimeSec</code> = 0 บนแมป = ไม่จับเวลา</p>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-primary" id="btn-space-shooter-save">บันทึก</button>
</div>
<p id="space-shooter-timing-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-mega-virus" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-mega-virus">
<h2>Minigame-7 — ยิง Mega Virus (balloon_boss)</h2>
<p class="muted">เก็บที่ <code>/Game/data/game-timing.json</code> · แมป <code>balloonBossTimeSec</code> ทับค่าด้านล่าง · แมป = 0 = ไม่จับเวลา · ถ้าแมปไม่ตั้งเวลา: ใช้ช่องล่าง (หรือ 120 วิ ถ้าใส่ 0) · <em>English:</em> Map time overrides; map 0 = unlimited; else global seconds below (or 120s if 0).</p>
<form id="form-mega-virus-timing" class="mega-virus-timing-form" action="#" method="post">
<fieldset class="quiz-timing-fieldset">
<legend>เวลา &amp; รูป</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="0 = ใช้ 120 วิเมื่อแมปไม่กำหนด · ตั้งอย่างน้อย 10 ถ้าต้องการกำหนดเอง">เวลารอบ (วินาที) <input type="number" id="mega-virus-mission-sec" min="0" max="7200" step="5" value="0"></label>
</div>
<div class="form-grid form-inline" style="margin-top:0.75rem;align-items:flex-end;gap:0.75rem;flex-wrap:wrap">
<label class="space-shooter-ship-url-label" style="flex:1;min-width:14rem">รูปบอส (URL) <input type="text" id="mega-virus-boss-url" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/boss.png" autocomplete="off"></label>
<img class="space-shooter-ship-prev" id="mega-virus-boss-prev" alt="" width="64" height="64" decoding="async">
<button type="button" class="btn btn-ghost" id="btn-mega-virus-boss-clear">ล้าง</button>
</div>
<p class="muted" style="margin-top:0.35rem">ลูกโป่งต่อที่นั่ง P1–P6 · ช่องว่างใช้ fallback · <em>English:</em> Per-seat balloon URLs; empty uses fallback.</p>
<div class="space-shooter-ship-grid" role="group" aria-label="Mega Virus player balloon images">
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P1</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-1.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-1" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-1">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P2</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-2.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-2" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-2">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P3</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-3.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-3" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-3">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P4</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-4" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-4.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-4" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-4">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P5</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-5" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-5.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-5" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-5">ล้าง</button></div>
<div class="space-shooter-ship-row"><span class="space-shooter-ship-slot">P6</span><label class="space-shooter-ship-url-label">URL <input type="text" id="mega-virus-balloon-url-6" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/balloon-6.png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="mega-virus-balloon-prev-6" alt="" width="40" height="48" decoding="async"><button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-clear-6">ล้าง</button></div>
</div>
<p class="muted" style="margin-top:0.5rem">Fallback ใช้เมื่อช่อง P ว่าง <strong>หรือ</strong> รูปต่อที่นั่งโหลดไม่สำเร็จ (404) · ใช้ URL แบบ <code>/Game/img/...</code> เท่านั้น — <strong>ไม่ใช่</strong> <code>/Game/public/img/...</code> (public ไม่ปรากฏใน URL) · <em>English:</em> Use <code>/Game/img/...</code> only, not <code>/Game/public/img/...</code>.</p>
<div class="form-grid form-inline" style="margin-top:0.75rem;align-items:flex-end;gap:0.75rem;flex-wrap:wrap">
<label class="space-shooter-ship-url-label" style="flex:1;min-width:14rem">รูปลูกโป่งร่วม / กรอบฟอง (fallback) <input type="text" id="mega-virus-balloon-fallback-url" maxlength="500" spellcheck="false" placeholder="/Game/img/MegaVirus/Artboard 9.png" autocomplete="off"></label>
<img class="space-shooter-ship-prev" id="mega-virus-balloon-fallback-prev" alt="" width="40" height="48" decoding="async">
<button type="button" class="btn btn-ghost" id="btn-mega-virus-balloon-fallback-clear">ล้าง</button>
</div>
</fieldset>
<div class="quiz-admin-actions" style="margin-top:0.75rem">
<button type="submit" class="btn btn-primary" id="btn-mega-virus-save">บันทึก</button>
</div>
</form>
<p id="mega-virus-timing-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-game-timing" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-game-timing">
<h2>เวลาเกม — พรมแดงสุดท้าย (Gauntlet)</h2>
<p class="muted">เก็บที่เซิร์ฟเวอร์เกม <code>/Game/data/game-timing.json</code> · หลังบันทึกห้องที่กำลังเล่นจะปรับความถี่ tick ทันที · ค่าจะส่งไปยังผู้เล่นผ่าน <code>gauntlet-sync</code> · <strong>จำกัดเวลา</strong>นับจากตอนโฮสต์เริ่มเกมพรมแดง — หมดเวลาแล้วเกมจบและส่งทุกคนกลับฉากล็อบบี้บนเซิร์ฟเวอร์ · ถ้าขึ้น Not Found ให้<strong>รีสตาร์ท Node</strong>ที่รัน <code>Game/server.js</code> (เช่น <code>pm2 restart</code>) หลัง deploy · <strong>เวลามินิเกมกระโดดขึ้นแท่น</strong>ตั้งที่แท็บ Minigame-5 · <strong>เวลายิงอุกาบาต</strong>ตั้งที่แท็บ Minigame-6</p>
<fieldset class="quiz-timing-fieldset">
<legend>พารามิเตอร์</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label>ความถี่ tick (มิลลิวินาที) <input type="number" id="game-timing-tick-ms" min="80" max="800" step="10" value="220"></label>
<label>ระยะกระโดด (tick) <input type="number" id="game-timing-jump-ticks" min="4" max="40" step="1" value="16"></label>
<label>จำกัดเวลาเกม (วินาที) <input type="number" id="game-timing-limit-sec" min="0" max="7200" step="10" value="0" title="0 = เล่นได้ไม่จำกัดเวลา"></label>
</div>
</fieldset>
<p class="muted" style="margin-top:0.35rem">ค่าเวลาเกม: ตั้ง <strong>0</strong> = ไม่จำกัด · ตั้งอย่างน้อย <strong>10</strong> วินาทีถ้าต้องการจับเวลา (สูงสุด 7200 = 2 ชม.) · Per-round time limit; <strong>0</strong> = no limit, else min <strong>10</strong>s, max <strong>7200</strong>s.</p>
<fieldset class="quiz-timing-fieldset gauntlet-timing-fieldset">
<legend>รูปอุปสรรค์ Gauntlet / Obstacle &amp; laser art</legend>
<div class="gauntlet-editor-shell">
<header class="gauntlet-editor-toolbar">
<h3 class="gauntlet-editor-toolbar-title">พรมแดงสุดท้าย — อุปสรรค์ &amp; เลเซอร์</h3>
<div class="gauntlet-editor-toolbar-actions">
<a class="gauntlet-editor-link" href="/Game/editor.html?id=mnn93hpi&amp;embed=1" target="_blank" rel="noopener noreferrer">Editor ฉาก (ฝัง)</a>
<a class="gauntlet-editor-link gauntlet-editor-link-secondary" href="/Game/editor.html?id=mnn93hpi" target="_blank" rel="noopener noreferrer">Editor เต็มหน้า</a>
</div>
</header>
<p class="gauntlet-editor-legend">เลือกรูปจากคลัง → กด <strong>Lane</strong> / <strong>Laser</strong> ตามชิ้นส่วน (หัวบน · เส้น · ท้ายล่าง) แบบในเกม · โครง UI อ้างอิง <a href="/Game/editor.html?id=mnn93hpi&amp;embed=1" target="_blank" rel="noopener noreferrer">Editor ฉาก</a></p>
<div class="gauntlet-editor-panels">
<div class="gauntlet-asset-library gauntlet-editor-panel">
<h4 class="gauntlet-editor-panel-title">คลังรูป · Asset library</h4>
<p class="muted gauntlet-asset-library-intro">ลากวางหรือคลิกโซนอัปโหลด (png / jpg / gif / webp · สูงสุด 4 MB) · กด <strong>Lane</strong> / <strong>Laser บน·ล่าง·เส้น</strong> เพื่อใส่ในชุดที่ส่งเข้าเกม</p>
<div id="gauntlet-asset-drop" class="gauntlet-asset-drop" role="button" tabindex="0" aria-label="วางรูปหรือคลิกเพื่อเลือกไฟล์">
<span class="gauntlet-asset-drop-text">ลากรูปมาวางที่นี่ หรือคลิกเลือกไฟล์<br><small class="muted">Drop images here or click to browse</small></span>
</div>
<input type="file" id="gauntlet-asset-file-input" accept="image/png,image/jpeg,image/jpg,image/gif,image/webp" multiple hidden>
<p id="gauntlet-asset-upload-msg" class="msg gauntlet-asset-upload-msg" role="status"></p>
<div id="gauntlet-asset-list" class="gauntlet-asset-list" aria-live="polite"></div>
</div>
<div class="game-timing-assigned-visuals gauntlet-editor-panel gauntlet-editor-panel--stage">
<div class="game-timing-visual-block game-timing-visual-block--lanes">
<h3 class="game-timing-visual-title">Lane — สิ่งกีดขวางบนพรมแดง</h3>
<p class="muted game-timing-visual-hint">กด <strong>Lane</strong> ที่การ์ดในคลังเพื่อเพิ่ม · เรียงลำดับด้วยลูกศร · ลบออกจากรายการที่นี่ (ไม่ลบไฟล์ในคลัง)</p>
<div id="game-timing-lane-visual-list" class="game-timing-visual-grid" role="list" aria-label="รูป lane ที่ใช้"></div>
<textarea id="game-timing-lane-urls" hidden aria-hidden="true" tabindex="-1" spellcheck="false"></textarea>
</div>
<div class="game-timing-visual-block game-timing-visual-block--laser">
<h3 class="game-timing-visual-title">Laser — คอลัมน์แนวตั้ง (หัว · เส้น · ท้าย)</h3>
<p class="muted game-timing-visual-hint">กด <strong>Laser บน / ล่าง / เส้น</strong> ที่คลัง · ดูหรือล้างช่องด้านล่าง · เส้นกลาง = tile ซ้ำแนวตั้งในเกม</p>
<div class="game-timing-laser-slots-grid">
<div class="game-timing-laser-slot">
<div class="game-timing-laser-slot-label">หัวบน · Top emitter</div>
<div id="game-timing-laser-top-preview" class="game-timing-laser-preview" aria-label="ตัวอย่าง laser บน"></div>
<input type="hidden" id="game-timing-laser-top-url" value="">
<div class="game-timing-laser-slot-actions">
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-top-view">ดู</button>
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-top-clear">ล้าง</button>
</div>
</div>
<div class="game-timing-laser-slot game-timing-laser-slot--beam">
<div class="game-timing-laser-slot-label">เส้นกลาง (tile) · Beam</div>
<div id="game-timing-laser-line-preview" class="game-timing-laser-preview game-timing-laser-preview--beam" aria-label="ตัวอย่างเส้น laser"></div>
<input type="hidden" id="game-timing-laser-line-url" value="">
<div class="game-timing-laser-slot-actions">
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-line-view">ดู</button>
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-line-clear">ล้าง</button>
</div>
</div>
<div class="game-timing-laser-slot">
<div class="game-timing-laser-slot-label">ท้ายล่าง · Bottom</div>
<div id="game-timing-laser-bottom-preview" class="game-timing-laser-preview" aria-label="ตัวอย่าง laser ล่าง"></div>
<input type="hidden" id="game-timing-laser-bottom-url" value="">
<div class="game-timing-laser-slot-actions">
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-bottom-view">ดู</button>
<button type="button" class="btn btn-ghost btn-sm" id="btn-game-timing-laser-bottom-clear">ล้าง</button>
</div>
</div>
</div>
</div>
<div class="form-grid form-inline quiz-timing-grid game-timing-laser-colors-row gauntlet-editor-laser-colors">
<label class="game-timing-laser-color-label">สีเติมคอลัมน์ laser (CSS)
<div class="game-timing-laser-color-wrap">
<div class="game-timing-laser-swatch-stack">
<span id="game-timing-laser-fill-swatch" class="game-timing-laser-swatch" role="img" aria-hidden="true"></span>
<input type="color" id="game-timing-laser-fill-picker" class="game-timing-laser-color-picker" value="#ef4444" title="เลือกสี (RGB) — ค่าโปร่งใสยังแก้ในช่องข้อความ" aria-label="เปิดตัวเลือกสีเติม laser">
</div>
<input type="text" id="game-timing-laser-fill" maxlength="100" placeholder="rgba(239,68,68,0.35)" spellcheck="false">
</div>
</label>
<label class="game-timing-laser-color-label">สีเส้นกรอบ laser
<div class="game-timing-laser-color-wrap">
<div class="game-timing-laser-swatch-stack">
<span id="game-timing-laser-stroke-swatch" class="game-timing-laser-swatch" role="img" aria-hidden="true"></span>
<input type="color" id="game-timing-laser-stroke-picker" class="game-timing-laser-color-picker" value="#fca5a5" title="เลือกสี (RGB) — ค่าโปร่งใสยังแก้ในช่องข้อความ" aria-label="เปิดตัวเลือกสีเส้นกรอบ laser">
</div>
<input type="text" id="game-timing-laser-stroke" maxlength="100" placeholder="rgba(252,165,165,0.95)" spellcheck="false">
</div>
</label>
<label>ความหนาเส้น (px, 0 = ไม่กรอบ) <input type="number" id="game-timing-laser-line-width" min="0" max="24" step="1" value="2"></label>
</div>
</div>
</div>
</div>
</fieldset>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-primary" id="btn-game-timing-save">บันทึก</button>
</div>
<p id="game-timing-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-stack-game" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-stack-game">
<h2>Minigame-3 — Tower block (Stack)</h2>
<p class="muted">ปรับความเร็วสวิง (crane) และ<strong>ความยาวแท่งบล็อก</strong>ในเกม <strong>Stack</strong> · เก็บที่ <code>/Game/data/game-timing.json</code> · ผู้เล่นได้ค่าใหม่เมื่อเข้าห้อง / รีเฟรชหน้าเล่น</p>
<fieldset class="quiz-timing-fieldset">
<legend>ความเร็วสวิง</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="cycles per second — น้อย = ช้า, มาก = เร็ว · ใช้ช่องข้อความเพื่อไม่ให้เบราว์เซอร์ตัดค่าที่ไม่ตรง step ของ type=number">รอบสวิงต่อวินาที (Hz)
<input type="text" id="stack-swing-hz" inputmode="decimal" autocomplete="off" spellcheck="false" value="" placeholder="เช่น 0.55" title="0.082.8 · ว่าง = 0.55">
</label>
</div>
</fieldset>
<p class="muted" style="margin-top:0.35rem">แนะนำสวิง <strong>0.350.9</strong> · สูงสุด ~2.8 · Lower Hz = slower pendulum.</p>
<fieldset class="quiz-timing-fieldset" style="margin-top:0.75rem">
<legend>ความยาวแท่งบล็อก</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="ความกว้างบล็อกเป็นหน่วย tile (1 tile = 1 ช่องบนแผนที่)">กว้างบล็อก (tile)
<input type="text" id="stack-block-width-tiles" inputmode="decimal" autocomplete="off" spellcheck="false" value="" placeholder="ว่าง = อัตโนมัติ" title="ตัวเลข 0.85–3.2 หรือปล่อยว่าง">
</label>
</div>
</fieldset>
<p class="muted" style="margin-top:0.35rem"><strong>ว่างช่อง</strong> = ใช้สูตรจากพื้นที่ลงบนแผนที่ (~48% ของความกว้างโซนลง) · ระหว่าง <strong>0.853.2</strong> tile · แท่งยาว = ยากขึ้น</p>
<fieldset class="quiz-timing-fieldset" style="margin-top:0.75rem">
<legend>ภารกิจ Tower block (ฉาก mnn93hpi)</legend>
<div class="form-grid form-inline quiz-timing-grid">
<label title="จำกัดเวลารอบถอดรหัส — 10–7200 วินาที">เวลารอบ (วินาที)
<input type="number" id="stack-tower-mission-sec" min="10" max="7200" step="1" value="90">
</label>
<label title="ทั้งทีมพลาดได้กี่ครั้งก่อนเกมจบ — 1–20">โอกาสพลาด (ครั้ง)
<input type="number" id="stack-team-misses-max" min="1" max="20" step="1" value="3">
</label>
<label title="ชั้นสำเร็จกี่ชั้น (ฐาน) รวมแล้วได้ Progress 100% — แต่ละชั้น +100÷N %; คอมโบ ×2 ของชั้นนั้น · ค่าเริ่ม 50 = เดิม 2%/ชั้น">Progress เต็ม 100% ที่กี่ชั้น (บล็อก)
<input type="number" id="stack-tower-progress-blocks" min="1" max="500" step="1" value="50">
</label>
</div>
</fieldset>
<p class="muted" style="margin-top:0.35rem">ใช้เฉพาะโหมด Stack + แมปภารกิจ Tower · คะแนน 10/ชั้น (คอมโบ ×2) · Progress ต่อชั้น = <strong>100% ÷ จำนวนชั้นด้านบน</strong> (คอมโบ ×2)</p>
<fieldset class="quiz-timing-fieldset" style="margin-top:0.75rem">
<legend>รูปบล็อกต่อผู้เล่น (ที่นั่ง 1–6)</legend>
<p class="muted" style="margin-top:0">ช่อง <strong>P1</strong> = ผู้เล่นคน (ตาที่ 1 ในโหมดพรีวิวบอท) · <strong>P2P6</strong> = บอท/ผู้เล่นอื่นในลำดับเดียวกับ HUD · แต่ละช่องมี <strong>ปกติ</strong> + <strong>ใหญ่</strong> (PNG/WebP โปร่งพื้นหลังแนะนำ) · เกมสุ่มใช้ “ใหญ่” ตามเปอร์เซ็นต์ด้านล่าง (ถ้าไม่ใส่ URL ใหญ่ = ใช้ปกติเสมอ) · <em>English:</em> Six seats; two URLs each (normal / heavy). Heavy roll uses percent; empty heavy falls back to normal art.</p>
<div class="form-grid form-inline quiz-timing-grid" style="margin-bottom:0.5rem">
<label title="0 = ไม่สุ่มใหญ่เลย · 100 = ทุกครั้งที่มีรูปใหญ่ช่องนั้น">โอกาสบล็อก “ใหญ่” (% ต่อการปล่อย)
<input type="number" id="stack-heavy-block-percent" min="0" max="100" step="1" value="35">
</label>
</div>
<div class="space-shooter-ship-grid" role="group" aria-label="Stack block images per seat">
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 1">P1</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-1" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-1">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-1" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-1">ล้าง</button></div>
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 2">P2</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-2" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-2">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-2" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-2">ล้าง</button></div>
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 3">P3</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-3" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-3">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-3" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-3">ล้าง</button></div>
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 4">P4</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-4" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-4" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-4">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-4" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-4" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-4">ล้าง</button></div>
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 5">P5</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-5" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-5" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-5">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-5" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-5" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-5">ล้าง</button></div>
<div class="space-shooter-ship-row stack-block-visual-row"><span class="space-shooter-ship-slot" title="ที่นั่ง 6">P6</span><label class="space-shooter-ship-url-label">ปกติ <input type="text" id="stack-block-normal-url-6" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-normal-prev-6" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-normal-clear-6">ล้าง</button><label class="space-shooter-ship-url-label">ใหญ่ <input type="text" id="stack-block-heavy-url-6" maxlength="500" spellcheck="false" placeholder="/Game/img/....png" autocomplete="off"></label><img class="space-shooter-ship-prev" id="stack-block-heavy-prev-6" alt="" width="56" height="28" decoding="async"><button type="button" class="btn btn-ghost" id="btn-stack-block-heavy-clear-6">ล้าง</button></div>
</div>
</fieldset>
<div class="quiz-admin-actions">
<button type="button" class="btn btn-primary" id="btn-stack-game-save">บันทึก</button>
</div>
<p id="stack-game-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-characters" class="tab-panel card admin-editor-panel" hidden role="tabpanel" aria-labelledby="tab-characters">
<div class="admin-editor-panel-head">
<div class="admin-editor-panel-intro">
<h2>ตัวละคร (4 ทิศ)</h2>
<p class="muted">อัปโหลดสกิน 4 ทิศหรือแบบเลเยอร์ และเลือกตัวที่ใช้ในล็อบบี้/เกม — เปิดแยกได้ที่ <a href="/Game/character.html" target="_blank" rel="noopener noreferrer">/Game/character.html</a></p>
</div>
<button type="button" class="btn btn-ghost btn-editor-fs" id="btn-character-fullscreen" aria-pressed="false" title="ขยายเต็มหน้าจอ (Esc ออก)">เต็มจอ</button>
</div>
<iframe class="admin-editor-iframe" id="admin-character-frame" title="เลือกตัวละคร" src="/Game/character.html?embed=1&amp;from=admin"></iframe>
</section>
<section id="tab-panel-accounts" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-accounts">
<h2>บัญชีผู้ใช้ (รายการภายในระบบ)</h2>
<p class="muted">ใช้บันทึก / บล็อกผู้เล่นหรือบัญชีที่เชื่อมกับ Guest / Facebook / Google — ผู้เล่น Guest ใน Main-Lobby จะถูกสร้างอัตโนมัติและมี COINS ตามที่เก็บที่นี่</p>
<p id="accounts-coins-summary" class="muted" aria-live="polite"></p>
<form id="form-account-add" class="form-grid form-inline">
<label>อีเมล <input type="email" name="email" placeholder="optional"></label>
<label>ชื่อแสดง <input type="text" name="displayName"></label>
<label>ประเภท
<select name="loginType">
<option value="guest">guest</option>
<option value="facebook">facebook</option>
<option value="google">google</option>
<option value="email">email</option>
</select>
</label>
<label>Provider User ID <input type="text" name="providerUserId" placeholder="id จาก Facebook/Google"></label>
<label>หมายเหตุ <input type="text" name="notes"></label>
<label>COINS เริ่มต้น <input type="number" name="coins" min="0" value="0" step="1"></label>
<label class="check"><input type="checkbox" name="blocked"> บล็อก</label>
<button type="submit" class="btn btn-primary">เพิ่มบัญชี</button>
</form>
<div class="table-wrap">
<table class="data-table" id="table-accounts">
<thead>
<tr>
<th>ชื่อ</th>
<th>อีเมล</th>
<th>ประเภท</th>
<th>Provider ID</th>
<th>COINS</th>
<th>สถานะ</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<p id="accounts-msg" class="msg" role="status"></p>
</section>
<section id="tab-panel-admins" class="tab-panel card" hidden role="tabpanel" aria-labelledby="tab-admins">
<h2>บัญชีแอดมิน</h2>
<p class="muted">เฉพาะ <strong>super admin</strong> เท่านั้นที่เพิ่ม/ลบแอดมินได้</p>
<form id="form-admin-add" class="form-grid form-inline">
<label>ชื่อผู้ใช้ <input type="text" name="username" required minlength="2" autocomplete="off"></label>
<label>รหัสผ่าน <input type="password" name="password" required minlength="8" autocomplete="new-password"></label>
<label>บทบาท
<select name="role">
<option value="admin">admin</option>
<option value="super">super</option>
</select>
</label>
<button type="submit" class="btn btn-primary">สร้างแอดมิน</button>
</form>
<h3 class="admin-subheading">ตั้งรหัสใหม่ให้แอดมินอื่น (super)</h3>
<p class="muted">ไม่ต้องรู้รหัสเดิม — ใช้เมื่อผู้ใช้ลืมรหัส</p>
<form id="form-admin-reset-other" class="form-grid form-inline">
<label>เลือกแอดมิน
<select name="targetId" id="select-admin-reset-target" required>
<option value="">— เลือก —</option>
</select>
</label>
<label>รหัสผ่านใหม่ <input type="password" name="newPassword" required minlength="8" autocomplete="new-password"></label>
<label>ยืนยันรหัส <input type="password" name="newPassword2" required minlength="8" autocomplete="new-password"></label>
<button type="submit" class="btn btn-primary">บันทึกรหัสใหม่</button>
</form>
<div class="table-wrap">
<table class="data-table" id="table-admins">
<thead>
<tr>
<th>ชื่อผู้ใช้</th>
<th>บทบาท</th>
<th>สร้างเมื่อ</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<p id="admins-msg" class="msg" role="status"></p>
</section>
</div>
</main>
</div>
<script src="admin.js?v=68"></script>
</body>
</html>