901 lines
105 KiB
HTML
901 lines
105 KiB
HTML
<!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">บัญชี & 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>กดแท็บ "รหัสผ่าน" อีกครั้งเพื่อปิด</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 & 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 (1–50, default 10).</p>
|
||
<label class="quiz-round-q-label" title="1–50 ข้อต่อรอบ">สุ่มสูงสุดกี่ข้อต่อรอบ <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 (0–100%) — 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>โซนตัวเลือก 1–16</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> > 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> > 0 จะใช้ค่าบนแมปแทนทั้งหมด · ไม่ใช่ค่าเดียวกับ "จำกัดเวลาเกม" ของพรมแดง · <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 1–6)</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> > 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>เวลา & รูป</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 & laser art</legend>
|
||
<div class="gauntlet-editor-shell">
|
||
<header class="gauntlet-editor-toolbar">
|
||
<h3 class="gauntlet-editor-toolbar-title">พรมแดงสุดท้าย — อุปสรรค์ & เลเซอร์</h3>
|
||
<div class="gauntlet-editor-toolbar-actions">
|
||
<a class="gauntlet-editor-link" href="/Game/editor.html?id=mnn93hpi&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&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.08–2.8 · ว่าง = 0.55">
|
||
</label>
|
||
</div>
|
||
</fieldset>
|
||
<p class="muted" style="margin-top:0.35rem">แนะนำสวิง <strong>0.35–0.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.85–3.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>P2–P6</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&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>
|