minigame 5 add more design 1.41
This commit is contained in:
@@ -2903,6 +2903,63 @@
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeJumpSurvivePlatformTileUrlInput(v) {
|
||||
var s = String(v || '').trim().slice(0, 500);
|
||||
if (!s) return '';
|
||||
if (!/^https?:\/\//i.test(s) && s.charAt(0) !== '/') s = '/' + s.replace(/^\/+/, '');
|
||||
return s;
|
||||
}
|
||||
|
||||
function updateJumpSurvivePlatformTilePreview(slot) {
|
||||
var img = el('jump-survive-platform-tile-prev-' + slot);
|
||||
var inp = el('jump-survive-platform-tile-url-' + slot);
|
||||
if (!img) return;
|
||||
var v = inp && inp.value ? normalizeJumpSurvivePlatformTileUrlInput(inp.value) : '';
|
||||
if (!v) {
|
||||
img.removeAttribute('src');
|
||||
img.alt = '';
|
||||
return;
|
||||
}
|
||||
img.alt = 'Jump platform tile ' + slot + ' preview';
|
||||
img.src = v;
|
||||
}
|
||||
|
||||
function readJumpSurvivePlatformTilesFromForm() {
|
||||
var out = [];
|
||||
for (var s = 1; s <= 3; s++) {
|
||||
var inpU = el('jump-survive-platform-tile-url-' + s);
|
||||
var url = inpU ? normalizeJumpSurvivePlatformTileUrlInput(inpU.value) : '';
|
||||
var inpW = el('jump-survive-platform-tile-w-' + s);
|
||||
var inpH = el('jump-survive-platform-tile-h-' + s);
|
||||
var w = inpW ? parseInt(String(inpW.value), 10) : 0;
|
||||
var h = inpH ? parseInt(String(inpH.value), 10) : 0;
|
||||
if (!Number.isFinite(w) || w < 0) w = 0;
|
||||
if (!Number.isFinite(h) || h < 0) h = 0;
|
||||
out.push({ url: url, w: w, h: h });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function bindJumpSurvivePlatformTileInputs() {
|
||||
for (var s = 1; s <= 3; s++) {
|
||||
(function (slot) {
|
||||
var inp = el('jump-survive-platform-tile-url-' + slot);
|
||||
var btn = el('btn-jump-survive-platform-tile-clear-' + slot);
|
||||
if (inp) {
|
||||
inp.addEventListener('change', function () { updateJumpSurvivePlatformTilePreview(slot); });
|
||||
inp.addEventListener('blur', function () { updateJumpSurvivePlatformTilePreview(slot); });
|
||||
}
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var i = el('jump-survive-platform-tile-url-' + slot);
|
||||
if (i) i.value = '';
|
||||
updateJumpSurvivePlatformTilePreview(slot);
|
||||
});
|
||||
}
|
||||
})(s);
|
||||
}
|
||||
}
|
||||
|
||||
function loadJumpSurviveTimingPanel() {
|
||||
gameTimingFetch('GET')
|
||||
.then(function (data) {
|
||||
@@ -2916,6 +2973,22 @@
|
||||
var jss = Number(data.jumpSurviveMissionTimeSec);
|
||||
inpT.value = String(Number.isFinite(jss) && jss > 0 ? Math.floor(jss) : 0);
|
||||
}
|
||||
var tiles = Array.isArray(data.jumpSurvivePlatformTiles) ? data.jumpSurvivePlatformTiles : [];
|
||||
var legacy = typeof data.jumpSurvivePlatformTileUrl === 'string' ? data.jumpSurvivePlatformTileUrl : '';
|
||||
for (var s = 1; s <= 3; s++) {
|
||||
var o = tiles[s - 1] && typeof tiles[s - 1] === 'object' ? tiles[s - 1] : {};
|
||||
var url = typeof o.url === 'string' ? o.url : '';
|
||||
if (s === 1 && !url && legacy) url = legacy;
|
||||
var inpU = el('jump-survive-platform-tile-url-' + s);
|
||||
if (inpU) inpU.value = url;
|
||||
var w = Number(o.w);
|
||||
var h = Number(o.h);
|
||||
var inpW = el('jump-survive-platform-tile-w-' + s);
|
||||
var inpH = el('jump-survive-platform-tile-h-' + s);
|
||||
if (inpW) inpW.value = String(Number.isFinite(w) && w > 0 ? Math.floor(w) : 0);
|
||||
if (inpH) inpH.value = String(Number.isFinite(h) && h > 0 ? Math.floor(h) : 0);
|
||||
updateJumpSurvivePlatformTilePreview(s);
|
||||
}
|
||||
setMsg('jump-survive-timing-msg', '', '');
|
||||
})
|
||||
.catch(function (e) {
|
||||
@@ -3577,10 +3650,13 @@
|
||||
var limJump = el('jump-survive-mission-sec') ? parseInt(String(el('jump-survive-mission-sec').value), 10) : 0;
|
||||
if (Number.isNaN(limJump) || limJump < 0) limJump = 0;
|
||||
limJump = limJump <= 0 ? 0 : Math.max(10, Math.min(7200, limJump));
|
||||
var platTiles = readJumpSurvivePlatformTilesFromForm();
|
||||
gameTimingFetch('GET')
|
||||
.then(function (data) {
|
||||
data.jumpSurviveJumpHeightMult = jumpSurvMult;
|
||||
data.jumpSurviveMissionTimeSec = limJump;
|
||||
delete data.jumpSurvivePlatformTileUrl;
|
||||
data.jumpSurvivePlatformTiles = platTiles;
|
||||
return gameTimingFetch('PUT', data);
|
||||
})
|
||||
.then(function () {
|
||||
@@ -3791,6 +3867,7 @@
|
||||
if (btnJumpSurviveSave) {
|
||||
btnJumpSurviveSave.addEventListener('click', saveJumpSurviveTimingPanel);
|
||||
}
|
||||
bindJumpSurvivePlatformTileInputs();
|
||||
var btnSpaceShooterSave = el('btn-space-shooter-save');
|
||||
if (btnSpaceShooterSave) {
|
||||
btnSpaceShooterSave.addEventListener('click', saveSpaceShooterTimingPanel);
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
|
||||
<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>
|
||||
<p class="muted">คนละโหมดกับ <strong>พรมแดง (Gauntlet)</strong> · เก็บที่ <code>/Game/data/game-timing.json</code> ฟิลด์ <code>jumpSurviveJumpHeightMult</code> · <code>jumpSurviveMissionTimeSec</code> · <code>jumpSurvivePlatformTiles</code> (3 ช่อง) · ผู้เล่นได้ค่าใหม่เมื่อโหลดหน้าเล่น / <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">
|
||||
@@ -531,6 +531,15 @@
|
||||
</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>
|
||||
<fieldset class="quiz-timing-fieldset">
|
||||
<legend>รูปแพลตฟอร์ม 1–3 (ช่องยืนได้ — เลือกโหมดวาด 1/2/3 ใน Map Editor)</legend>
|
||||
<p class="muted" style="margin-top:0">แต่ละช่อง: URL + ความกว้าง/สูงเป็น <strong>พิกเซลในโลกเกม</strong> (เทียบกับขนาดไทล์แมป) · กว้าง/สูง = <strong>0</strong> ให้ใช้ขนาดเท่าไทล์หนึ่งช่อง · ว่าง URL = กราฟิก cyan เดิม · อัปโหลดไปที่ <code>/Game/public/img/Jumper/</code> · <em>English:</em> Three platform sprites; W/H in world px (0 = one tile).</p>
|
||||
<div class="space-shooter-ship-grid" role="group" aria-label="Jump survive platform tiles 1–3">
|
||||
<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="jump-survive-platform-tile-url-1" maxlength="500" spellcheck="false" placeholder="/Game/img/Jumper/platform-1.png" autocomplete="off"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">W <input type="number" id="jump-survive-platform-tile-w-1" min="0" max="4096" step="1" value="0"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">H <input type="number" id="jump-survive-platform-tile-h-1" min="0" max="4096" step="1" value="0"></label><img class="space-shooter-ship-prev" id="jump-survive-platform-tile-prev-1" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-jump-survive-platform-tile-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="jump-survive-platform-tile-url-2" maxlength="500" spellcheck="false" placeholder="/Game/img/Jumper/platform-2.png" autocomplete="off"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">W <input type="number" id="jump-survive-platform-tile-w-2" min="0" max="4096" step="1" value="0"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">H <input type="number" id="jump-survive-platform-tile-h-2" min="0" max="4096" step="1" value="0"></label><img class="space-shooter-ship-prev" id="jump-survive-platform-tile-prev-2" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-jump-survive-platform-tile-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="jump-survive-platform-tile-url-3" maxlength="500" spellcheck="false" placeholder="/Game/img/Jumper/platform-3.png" autocomplete="off"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">W <input type="number" id="jump-survive-platform-tile-w-3" min="0" max="4096" step="1" value="0"></label><label class="space-shooter-ship-url-label" title="พิกเซลในโลกเกม · 0 = เท่าไทล์">H <input type="number" id="jump-survive-platform-tile-h-3" min="0" max="4096" step="1" value="0"></label><img class="space-shooter-ship-prev" id="jump-survive-platform-tile-prev-3" alt="" width="56" height="56" decoding="async"><button type="button" class="btn btn-ghost" id="btn-jump-survive-platform-tile-clear-3">ล้าง</button></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="quiz-admin-actions">
|
||||
<button type="button" class="btn btn-primary" id="btn-jump-survive-save">บันทึก</button>
|
||||
</div>
|
||||
@@ -895,6 +904,6 @@
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script src="admin.js?v=68"></script>
|
||||
<script src="admin.js?v=70"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -38,6 +38,23 @@
|
||||
"stackHeavyBlockPercent": 35,
|
||||
"jumpSurviveJumpHeightMult": 1.5,
|
||||
"jumpSurviveMissionTimeSec": 0,
|
||||
"jumpSurvivePlatformTiles": [
|
||||
{
|
||||
"url": "/Game/img/Jumper/platfrom-1.png",
|
||||
"w": 1392,
|
||||
"h": 136
|
||||
},
|
||||
{
|
||||
"url": "/Game/img/Jumper/platfrom-2.png",
|
||||
"w": 389,
|
||||
"h": 77
|
||||
},
|
||||
{
|
||||
"url": "/Game/img/Jumper/platfrom-3.png",
|
||||
"w": 212,
|
||||
"h": 77
|
||||
}
|
||||
],
|
||||
"spaceShooterMissionTimeSec": 0,
|
||||
"spaceShooterShipImageUrls": [
|
||||
"/Game/img/ViolentCrime/Rocket-1.png",
|
||||
|
||||
@@ -122,7 +122,9 @@
|
||||
<option value="quizBattleDome" id="draw-mode-option-quiz-battle-dome" hidden>โดมคำถาม (ช่องกด E ในเกม)</option>
|
||||
</optgroup>
|
||||
<optgroup label="กระโดดให้รอด">
|
||||
<option value="jumpSurvivePlatform" id="draw-mode-option-jump-platform" hidden>แพลตฟอร์ม (ยืนได้ — ลอยขึ้นตามกล้องในเกม)</option>
|
||||
<option value="jumpSurvivePlatform1" id="draw-mode-option-jump-platform-1" hidden>แพลตฟอร์ม 1 (ยืนได้ — รูปช่อง 1 จาก Admin)</option>
|
||||
<option value="jumpSurvivePlatform2" id="draw-mode-option-jump-platform-2" hidden>แพลตฟอร์ม 2 (ยืนได้ — รูปช่อง 2 จาก Admin)</option>
|
||||
<option value="jumpSurvivePlatform3" id="draw-mode-option-jump-platform-3" hidden>แพลตฟอร์ม 3 (ยืนได้ — รูปช่อง 3 จาก Admin)</option>
|
||||
<option value="jumpSurviveHazard" id="draw-mode-option-jump-hazard" hidden>โซนตาย (แดง — ตัวละครโดนกล่องชนแล้วตายทันที)</option>
|
||||
</optgroup>
|
||||
<optgroup label="ยิงยานอวกาศ">
|
||||
@@ -360,7 +362,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/version.js?v=0.0258"></script>
|
||||
<script src="js/editor.js?v=0.0316"></script>
|
||||
<script src="js/editor.js?v=0.0318"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -108,6 +108,16 @@
|
||||
let jumpSurvivePlatformArea = [];
|
||||
/** jump_survive: กริด 0/1 — ช่องที่ 1 = โซนตาย (ชน hitbox แล้วตายทันทีในเกม) */
|
||||
let jumpSurviveHazardArea = [];
|
||||
/** กริด 0 / 1–3 = รูปแพลตฟอร์มจาก jumpSurvivePlatformTiles ใน game-timing */
|
||||
let jumpSurvivePlatformVariantArea = [];
|
||||
/** จาก GET /api/game-timing — แสดงในแคนวาสเอดิเตอร์ตามช่อง variant */
|
||||
let editorJumpSurvivePlatformTilesCfg = [
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
];
|
||||
let editorJumpSurvivePlatformTileImgs = [null, null, null];
|
||||
let editorJumpSurvivePlatformTimingGen = 0;
|
||||
let cellColors = [];
|
||||
let gauntletPlayerSpawns = [];
|
||||
/** gauntlet: แถว y บน–ล่างของแถบเลเซอร์ (null = เต็มความสูงแมป) */
|
||||
@@ -1258,6 +1268,35 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureJumpSurvivePlatformVariantArea();
|
||||
}
|
||||
|
||||
function ensureJumpSurvivePlatformVariantArea() {
|
||||
if (!jumpSurvivePlatformVariantArea.length || jumpSurvivePlatformVariantArea.length !== height) {
|
||||
const existing = jumpSurvivePlatformVariantArea.slice().map((r) => r && r.slice());
|
||||
jumpSurvivePlatformVariantArea = [];
|
||||
for (let y = 0; y < height; y++) {
|
||||
const row = existing[y] && existing[y].length === width ? existing[y].slice() : Array(width).fill(0);
|
||||
jumpSurvivePlatformVariantArea.push(row);
|
||||
}
|
||||
} else {
|
||||
for (let y = 0; y < height; y++) {
|
||||
if (!jumpSurvivePlatformVariantArea[y] || jumpSurvivePlatformVariantArea[y].length !== width) {
|
||||
jumpSurvivePlatformVariantArea[y] = Array(width).fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
if (jumpSurvivePlatformArea[y] && jumpSurvivePlatformArea[y][x] === 1) {
|
||||
let v = jumpSurvivePlatformVariantArea[y][x];
|
||||
if (!Number.isFinite(v) || v < 1) jumpSurvivePlatformVariantArea[y][x] = 1;
|
||||
else if (v > 3) jumpSurvivePlatformVariantArea[y][x] = 3;
|
||||
} else {
|
||||
jumpSurvivePlatformVariantArea[y][x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureJumpSurviveHazardArea() {
|
||||
@@ -1277,6 +1316,68 @@
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEditorJumpSurvivePlatformTileUrl(v) {
|
||||
let s = String(v || '').trim().slice(0, 500);
|
||||
if (!s) return '';
|
||||
if (!/^https?:\/\//i.test(s) && s.charAt(0) !== '/') s = '/' + s.replace(/^\/+/, '');
|
||||
return s;
|
||||
}
|
||||
|
||||
function normalizeEditorJumpSurvivePlatformTilesFromTiming(data) {
|
||||
const legacy = normalizeEditorJumpSurvivePlatformTileUrl(data.jumpSurvivePlatformTileUrl);
|
||||
const arr = Array.isArray(data.jumpSurvivePlatformTiles) ? data.jumpSurvivePlatformTiles : [];
|
||||
const out = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const o = arr[i] && typeof arr[i] === 'object' ? arr[i] : {};
|
||||
let url = normalizeEditorJumpSurvivePlatformTileUrl(o.url);
|
||||
if (i === 0 && !url && legacy) url = legacy;
|
||||
let ww = Number(o.w);
|
||||
let hh = Number(o.h);
|
||||
if (!Number.isFinite(ww) || ww <= 0) ww = 0;
|
||||
else ww = Math.max(8, Math.min(4096, Math.round(ww)));
|
||||
if (!Number.isFinite(hh) || hh <= 0) hh = 0;
|
||||
else hh = Math.max(8, Math.min(4096, Math.round(hh)));
|
||||
out.push({ url, w: ww, h: hh });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function refreshEditorJumpSurvivePlatformTileFromTiming() {
|
||||
editorJumpSurvivePlatformTimingGen += 1;
|
||||
const gen = editorJumpSurvivePlatformTimingGen;
|
||||
editorJumpSurvivePlatformTileImgs = [null, null, null];
|
||||
fetch(BASE + '/api/game-timing?_=' + Date.now(), { cache: 'no-store' })
|
||||
.then((r) => (r.ok ? r.json() : null))
|
||||
.then((data) => {
|
||||
if (!data || gen !== editorJumpSurvivePlatformTimingGen) return;
|
||||
const gtNow = gameTypeEl ? gameTypeEl.value : gameType;
|
||||
if (gtNow !== 'jump_survive') return;
|
||||
editorJumpSurvivePlatformTilesCfg = normalizeEditorJumpSurvivePlatformTilesFromTiming(data);
|
||||
let pending = 0;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const u = editorJumpSurvivePlatformTilesCfg[i].url;
|
||||
if (!u) continue;
|
||||
pending += 1;
|
||||
const idx = i;
|
||||
const im = new Image();
|
||||
im.onload = function () {
|
||||
if (gen !== editorJumpSurvivePlatformTimingGen) return;
|
||||
editorJumpSurvivePlatformTileImgs[idx] = im;
|
||||
pending -= 1;
|
||||
if (pending <= 0) draw();
|
||||
};
|
||||
im.onerror = function () {
|
||||
if (gen !== editorJumpSurvivePlatformTimingGen) return;
|
||||
pending -= 1;
|
||||
if (pending <= 0) draw();
|
||||
};
|
||||
im.src = u;
|
||||
}
|
||||
if (pending === 0) draw();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function sanitizeGauntletPlayerSpawns() {
|
||||
gauntletPlayerSpawns = gauntletPlayerSpawns
|
||||
.filter((s) => s && Number.isFinite(s.x) && Number.isFinite(s.y) &&
|
||||
@@ -1733,6 +1834,7 @@
|
||||
const dqBattlePath = document.getElementById('draw-mode-option-quiz-battle-path');
|
||||
if (!froggerWrap) return;
|
||||
const gt = gameTypeEl ? gameTypeEl.value : 'zep';
|
||||
if (drawModeEl && drawModeEl.value === 'jumpSurvivePlatform') drawModeEl.value = 'jumpSurvivePlatform1';
|
||||
if (dqTrue) dqTrue.hidden = gt !== 'quiz';
|
||||
if (dqFalse) dqFalse.hidden = gt !== 'quiz';
|
||||
if (dqQ) dqQ.hidden = gt !== 'quiz' && gt !== 'quiz_carry';
|
||||
@@ -1827,6 +1929,7 @@
|
||||
if (editorFroggerAnimationId != null) { cancelAnimationFrame(editorFroggerAnimationId); editorFroggerAnimationId = null; }
|
||||
draw();
|
||||
} else if (gt === 'jump_survive') {
|
||||
refreshEditorJumpSurvivePlatformTileFromTiming();
|
||||
froggerWrap.style.display = 'none';
|
||||
if (hint) {
|
||||
hint.innerHTML = '<p class="editor-hint-lead"><strong>กระโดดให้รอด</strong> — กำแพง + แพลตฟอร์มเลื่อนขึ้นในกรอบจอ</p>' + editorHintBulletList([
|
||||
@@ -1966,12 +2069,15 @@
|
||||
if (drawModeEl && (drawModeEl.value === 'quizBattleDome' || drawModeEl.value === 'quizBattlePath') && gt !== 'quiz_battle') {
|
||||
drawModeEl.value = 'wall';
|
||||
}
|
||||
const jPlatOpt = document.getElementById('draw-mode-option-jump-platform');
|
||||
const jPlat1 = document.getElementById('draw-mode-option-jump-platform-1');
|
||||
const jPlat2 = document.getElementById('draw-mode-option-jump-platform-2');
|
||||
const jPlat3 = document.getElementById('draw-mode-option-jump-platform-3');
|
||||
const jHazOpt = document.getElementById('draw-mode-option-jump-hazard');
|
||||
if (jPlatOpt) {
|
||||
jPlatOpt.hidden = gt !== 'jump_survive';
|
||||
if (gt !== 'jump_survive' && drawModeEl && drawModeEl.value === 'jumpSurvivePlatform') drawModeEl.value = 'wall';
|
||||
}
|
||||
const showJumpPlat = gt === 'jump_survive';
|
||||
if (jPlat1) jPlat1.hidden = !showJumpPlat;
|
||||
if (jPlat2) jPlat2.hidden = !showJumpPlat;
|
||||
if (jPlat3) jPlat3.hidden = !showJumpPlat;
|
||||
if (!showJumpPlat && drawModeEl && /^jumpSurvivePlatform\d$/.test(drawModeEl.value)) drawModeEl.value = 'wall';
|
||||
if (jHazOpt) {
|
||||
jHazOpt.hidden = gt !== 'jump_survive';
|
||||
if (gt !== 'jump_survive' && drawModeEl && drawModeEl.value === 'jumpSurviveHazard') drawModeEl.value = 'wall';
|
||||
@@ -2007,6 +2113,7 @@
|
||||
stackReleaseArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
stackLandArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatformArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatformVariantArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurviveHazardArea = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
shooterSpawnSlots = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
balloonBossPlayerSlots = Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
@@ -2242,12 +2349,36 @@
|
||||
}
|
||||
}
|
||||
if (gtDraw === 'jump_survive' && jumpSurvivePlatformArea[y] && jumpSurvivePlatformArea[y][x] === 1) {
|
||||
ctx.fillStyle = 'rgba(125, 235, 200, 0.48)';
|
||||
ctx.fillRect(tx + 3, ty + 3, tileSize - 6, tileSize - 6);
|
||||
ctx.strokeStyle = 'rgba(160, 255, 220, 0.92)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(tx + 3, ty + 3, tileSize - 6, tileSize - 6);
|
||||
ctx.lineWidth = 1;
|
||||
let v = jumpSurvivePlatformVariantArea[y] ? Math.floor(Number(jumpSurvivePlatformVariantArea[y][x])) : 1;
|
||||
if (!Number.isFinite(v) || v < 1) v = 1;
|
||||
if (v > 3) v = 3;
|
||||
const cfg = editorJumpSurvivePlatformTilesCfg[v - 1] || { url: '', w: 0, h: 0 };
|
||||
const im = editorJumpSurvivePlatformTileImgs[v - 1];
|
||||
const px0 = tx + 3;
|
||||
const py0 = ty + 3;
|
||||
const pw = tileSize - 6;
|
||||
const ph = tileSize - 6;
|
||||
let dw = cfg.w > 0 ? cfg.w : pw;
|
||||
let dh = cfg.h > 0 ? cfg.h : ph;
|
||||
const maxDim = tileSize * 4;
|
||||
if (dw > maxDim) dw = maxDim;
|
||||
if (dh > maxDim) dh = maxDim;
|
||||
const dx = tx + (tileSize - dw) / 2;
|
||||
const dy = ty + tileSize - dh - 2;
|
||||
if (im && im.complete && im.naturalWidth > 0) {
|
||||
ctx.drawImage(im, dx, dy, dw, dh);
|
||||
ctx.strokeStyle = 'rgba(160, 255, 220, 0.92)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(dx, dy, dw, dh);
|
||||
ctx.lineWidth = 1;
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(125, 235, 200, 0.48)';
|
||||
ctx.fillRect(px0, py0, pw, ph);
|
||||
ctx.strokeStyle = 'rgba(160, 255, 220, 0.92)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(px0, py0, pw, ph);
|
||||
ctx.lineWidth = 1;
|
||||
}
|
||||
}
|
||||
if (gtDraw === 'jump_survive' && jumpSurviveHazardArea[y] && jumpSurviveHazardArea[y][x] === 1) {
|
||||
ctx.fillStyle = 'rgba(220, 40, 60, 0.52)';
|
||||
@@ -2776,10 +2907,17 @@
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'stack') return;
|
||||
ensureStackAreas();
|
||||
stackLandArea[y][x] = left ? 1 : 0;
|
||||
} else if (drawModeEl.value === 'jumpSurvivePlatform') {
|
||||
} else if (drawModeEl.value === 'jumpSurvivePlatform1' || drawModeEl.value === 'jumpSurvivePlatform2' || drawModeEl.value === 'jumpSurvivePlatform3') {
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'jump_survive') return;
|
||||
const slot = drawModeEl.value === 'jumpSurvivePlatform1' ? 1 : drawModeEl.value === 'jumpSurvivePlatform2' ? 2 : 3;
|
||||
ensureJumpSurvivePlatformArea();
|
||||
jumpSurvivePlatformArea[y][x] = left ? 1 : 0;
|
||||
if (left) {
|
||||
jumpSurvivePlatformArea[y][x] = 1;
|
||||
jumpSurvivePlatformVariantArea[y][x] = slot;
|
||||
} else {
|
||||
jumpSurvivePlatformArea[y][x] = 0;
|
||||
jumpSurvivePlatformVariantArea[y][x] = 0;
|
||||
}
|
||||
} else if (drawModeEl.value === 'jumpSurviveHazard') {
|
||||
if ((gameTypeEl ? gameTypeEl.value : gameType) !== 'jump_survive') return;
|
||||
ensureJumpSurviveHazardArea();
|
||||
@@ -3237,7 +3375,7 @@
|
||||
if (drawModeEl && drawModeEl.value === 'quizQuestion' && gameType !== 'quiz' && gameType !== 'quiz_carry') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && (drawModeEl.value === 'stackRelease' || drawModeEl.value === 'stackLand') && gameType !== 'stack') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && quizCarryEditorCarryModes().indexOf(drawModeEl.value) >= 0 && gameType !== 'quiz_carry') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'jumpSurvivePlatform' && gameType !== 'jump_survive') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && /^jumpSurvivePlatform\d$/.test(drawModeEl.value) && gameType !== 'jump_survive') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'gauntletPlayerSpawn' && gameType !== 'gauntlet') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'gauntletLaserStart' && gameType !== 'gauntlet') drawModeEl.value = 'wall';
|
||||
if (drawModeEl && drawModeEl.value === 'gauntletLaserEnd' && gameType !== 'gauntlet') drawModeEl.value = 'wall';
|
||||
@@ -3429,6 +3567,7 @@
|
||||
stackLandArea: gameType === 'stack' ? stackLandArea.map(r => r.slice()) : [],
|
||||
jumpSurvivePlatforms: gameType === 'jump_survive' ? jumpSurvivePlatforms.slice() : [],
|
||||
jumpSurvivePlatformArea: gameType === 'jump_survive' ? jumpSurvivePlatformArea.map((r) => r.slice()) : [],
|
||||
jumpSurvivePlatformVariantArea: gameType === 'jump_survive' ? jumpSurvivePlatformVariantArea.map((r) => r.slice()) : [],
|
||||
jumpSurviveHazardArea: gameType === 'jump_survive' ? jumpSurviveHazardArea.map((r) => r.slice()) : [],
|
||||
shooterSpawnSlots: (gameType === 'space_shooter' || gameType === 'jump_survive') ? shooterSpawnSlots.map((r) => r.slice()) : [],
|
||||
balloonBossPlayerSlots: gameType === 'balloon_boss' ? balloonBossPlayerSlots.map((r) => r.slice()) : [],
|
||||
@@ -3716,6 +3855,9 @@
|
||||
quizBattlePathArea = m.quizBattlePathArea && m.quizBattlePathArea.length ? m.quizBattlePathArea.map(r => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatforms = Array.isArray(m.jumpSurvivePlatforms) ? m.jumpSurvivePlatforms.map((p) => (p && typeof p === 'object' ? { ...p } : null)).filter(Boolean) : [];
|
||||
jumpSurvivePlatformArea = m.jumpSurvivePlatformArea && m.jumpSurvivePlatformArea.length ? m.jumpSurvivePlatformArea.map((r) => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
jumpSurvivePlatformVariantArea = m.jumpSurvivePlatformVariantArea && m.jumpSurvivePlatformVariantArea.length
|
||||
? m.jumpSurvivePlatformVariantArea.map((r) => r && r.slice())
|
||||
: Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
ensureJumpSurvivePlatformArea();
|
||||
jumpSurviveHazardArea = m.jumpSurviveHazardArea && m.jumpSurviveHazardArea.length ? m.jumpSurviveHazardArea.map((r) => r && r.slice()) : Array(height).fill(0).map(() => Array(width).fill(0));
|
||||
ensureJumpSurviveHazardArea();
|
||||
|
||||
+111
-24
@@ -1675,6 +1675,12 @@
|
||||
let playJumpSurviveJumpHeightMult = 1.5;
|
||||
/** จำกัดเวลารอบภารกิจกระโดดขึ้นแท่น (วินาที) — จาก GET /api/game-timing; 0 = ใช้ค่าเริ่ม 60 ในเกม */
|
||||
let playJumpSurviveMissionTimeSec = 0;
|
||||
/** รูปแพลตฟอร์ม jump_survive ช่อง 1–3 จาก game-timing — url ว่าง = วาด cyan; w/h 0 = ใช้ขนาดไทล์ */
|
||||
let playJumpSurvivePlatformTiles = [
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
];
|
||||
/** ยิงยานอวกาศ (space_shooter): เวลารอบจาก game-timing; 0 = ใช้ค่าเริ่ม 90 ในเกมถ้าแมปไม่ทับ */
|
||||
let playSpaceShooterMissionTimeSec = 0;
|
||||
/** รูปยานช่อง 1–6 จาก game-timing (spawn slot); ว่าง = วาดยานเวกเตอร์ */
|
||||
@@ -9196,6 +9202,28 @@
|
||||
playJumpSurviveMissionTimeSec = 0;
|
||||
}
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'jumpSurvivePlatformTiles')
|
||||
|| Object.prototype.hasOwnProperty.call(payload, 'jumpSurvivePlatformTileUrl')) {
|
||||
const legacy = Object.prototype.hasOwnProperty.call(payload, 'jumpSurvivePlatformTileUrl')
|
||||
? normalizeGauntletAssetUrlForPlay(typeof payload.jumpSurvivePlatformTileUrl === 'string' ? payload.jumpSurvivePlatformTileUrl : '')
|
||||
: '';
|
||||
const arr = Array.isArray(payload.jumpSurvivePlatformTiles) ? payload.jumpSurvivePlatformTiles : [];
|
||||
const next = [];
|
||||
for (let pi = 0; pi < 3; pi++) {
|
||||
const o = arr[pi] && typeof arr[pi] === 'object' ? arr[pi] : {};
|
||||
let url = normalizeGauntletAssetUrlForPlay(typeof o.url === 'string' ? o.url : '');
|
||||
if (pi === 0 && !url && legacy) url = legacy;
|
||||
let ww = Number(o.w);
|
||||
let hh = Number(o.h);
|
||||
if (!Number.isFinite(ww) || ww <= 0) ww = 0;
|
||||
else ww = Math.max(8, Math.min(4096, Math.round(ww)));
|
||||
if (!Number.isFinite(hh) || hh <= 0) hh = 0;
|
||||
else hh = Math.max(8, Math.min(4096, Math.round(hh)));
|
||||
next.push({ url, w: ww, h: hh });
|
||||
if (url) ensureGauntletAssetImage(url);
|
||||
}
|
||||
playJumpSurvivePlatformTiles = next;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'spaceShooterMissionTimeSec')) {
|
||||
const st = Number(payload.spaceShooterMissionTimeSec);
|
||||
if (Number.isFinite(st) && st > 0) {
|
||||
@@ -10527,6 +10555,37 @@
|
||||
md.jumpSurvivePlatformArea = rows;
|
||||
}
|
||||
|
||||
function normalizeJumpSurvivePlatformVariantAreaInPlay(md) {
|
||||
if (!md || md.gameType !== 'jump_survive') return;
|
||||
const w = md.width || 20, h = md.height || 15;
|
||||
const pa = md.jumpSurvivePlatformArea || [];
|
||||
const src = md.jumpSurvivePlatformVariantArea || [];
|
||||
const rows = [];
|
||||
for (let y = 0; y < h; y++) {
|
||||
const row = [];
|
||||
for (let x = 0; x < w; x++) {
|
||||
const has = pa[y] && pa[y][x] === 1;
|
||||
let v = has ? Math.floor(Number(src[y] && src[y][x])) : 0;
|
||||
if (has && (!Number.isFinite(v) || v < 1)) v = 1;
|
||||
if (v > 3) v = 3;
|
||||
if (!has) v = 0;
|
||||
row.push(v);
|
||||
}
|
||||
rows.push(row);
|
||||
}
|
||||
md.jumpSurvivePlatformVariantArea = rows;
|
||||
}
|
||||
|
||||
function jumpSurvivePlatformVariantIndexAtPlay(md, tx, ty) {
|
||||
const pa = md && md.jumpSurvivePlatformArea;
|
||||
if (!pa || !pa[ty] || pa[ty][tx] !== 1) return 0;
|
||||
const va = md.jumpSurvivePlatformVariantArea;
|
||||
let v = va && va[ty] ? Math.floor(Number(va[ty][tx])) : 1;
|
||||
if (!Number.isFinite(v) || v < 1) v = 1;
|
||||
if (v > 3) v = 3;
|
||||
return v;
|
||||
}
|
||||
|
||||
function normalizeJumpSurviveHazardAreaInPlay(md) {
|
||||
if (!md || md.gameType !== 'jump_survive') return;
|
||||
const w = md.width || 20, h = md.height || 15;
|
||||
@@ -13055,7 +13114,9 @@
|
||||
if (!Array.isArray(mapData.jumpSurvivePlatforms)) mapData.jumpSurvivePlatforms = [];
|
||||
if (!mapData.jumpSurvivePlatformArea) mapData.jumpSurvivePlatformArea = [];
|
||||
if (!mapData.jumpSurviveHazardArea) mapData.jumpSurviveHazardArea = [];
|
||||
if (!mapData.jumpSurvivePlatformVariantArea) mapData.jumpSurvivePlatformVariantArea = [];
|
||||
normalizeJumpSurvivePlatformAreaInPlay(mapData);
|
||||
normalizeJumpSurvivePlatformVariantAreaInPlay(mapData);
|
||||
normalizeJumpSurviveHazardAreaInPlay(mapData);
|
||||
normalizeShooterSpawnSlotsInPlay(mapData);
|
||||
jumpSurviveEliminated = false;
|
||||
@@ -13889,7 +13950,9 @@
|
||||
if (!Array.isArray(mapData.jumpSurvivePlatforms)) mapData.jumpSurvivePlatforms = [];
|
||||
if (!mapData.jumpSurvivePlatformArea) mapData.jumpSurvivePlatformArea = [];
|
||||
if (!mapData.jumpSurviveHazardArea) mapData.jumpSurviveHazardArea = [];
|
||||
if (!mapData.jumpSurvivePlatformVariantArea) mapData.jumpSurvivePlatformVariantArea = [];
|
||||
normalizeJumpSurvivePlatformAreaInPlay(mapData);
|
||||
normalizeJumpSurvivePlatformVariantAreaInPlay(mapData);
|
||||
normalizeJumpSurviveHazardAreaInPlay(mapData);
|
||||
normalizeShooterSpawnSlotsInPlay(mapData);
|
||||
jumpSurviveEliminated = false;
|
||||
@@ -16033,6 +16096,11 @@
|
||||
for (let py = 0; py < h; py++) {
|
||||
for (let px = 0; px < w; px++) {
|
||||
if (!pa[py] || pa[py][px] !== 1) continue;
|
||||
const vIdx = jumpSurvivePlatformVariantIndexAtPlay(mapData, px, py);
|
||||
const cfg = playJumpSurvivePlatformTiles[vIdx - 1] || { url: '', w: 0, h: 0 };
|
||||
const jumpPlatU = normalizeGauntletAssetUrlForPlay(cfg.url || '');
|
||||
const jumpPlatRec = jumpPlatU ? ensureGauntletAssetImage(jumpPlatU) : null;
|
||||
const jumpPlatImg = jumpPlatRec && jumpPlatRec.ready && jumpPlatRec.img && jumpPlatRec.img.naturalWidth > 0 ? jumpPlatRec.img : null;
|
||||
const tileWorldLeft = px * tsz;
|
||||
const tileWorldRight = tileWorldLeft + tsz;
|
||||
if (tileWorldRight <= worldMinX || tileWorldLeft >= worldMaxX) continue;
|
||||
@@ -16045,32 +16113,51 @@
|
||||
if (platTop + tsz <= visTop || platTop >= visBot) continue;
|
||||
const [psx, psy] = worldToScreen(tileWorldLeft, platTop);
|
||||
const psz = tsz * zDraw;
|
||||
ctx.fillStyle = 'rgba(0, 40, 52, 0.72)';
|
||||
ctx.fillRect(psx + 3, psy + 3, psz - 6, psz - 6);
|
||||
ctx.strokeStyle = 'rgba(0, 255, 240, 0.55)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(psx + 3, psy + 3, psz - 6, psz - 6);
|
||||
ctx.strokeStyle = 'rgba(120, 255, 255, 0.35)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(psx + 5, psy + 5, psz - 10, psz - 10);
|
||||
const grad = ctx.createLinearGradient(psx, psy, psx, psy + psz);
|
||||
grad.addColorStop(0, 'rgba(0, 255, 240, 0.12)');
|
||||
grad.addColorStop(0.5, 'rgba(0, 0, 0, 0)');
|
||||
grad.addColorStop(1, 'rgba(0, 180, 200, 0.08)');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(psx + 4, psy + 4, psz - 8, psz - 8);
|
||||
ctx.lineWidth = 1;
|
||||
if (zDraw >= 1.15 && psz > 22) {
|
||||
let dw = cfg.w > 0 ? cfg.w * zDraw : psz - 4;
|
||||
let dh = cfg.h > 0 ? cfg.h * zDraw : psz - 4;
|
||||
const maxDim = psz * 4;
|
||||
if (dw > maxDim) dw = maxDim;
|
||||
if (dh > maxDim) dh = maxDim;
|
||||
const marginBot = 2;
|
||||
const dx = psx + (psz - dw) / 2;
|
||||
const dy = psy + psz - dh - marginBot;
|
||||
if (jumpPlatImg) {
|
||||
ctx.save();
|
||||
ctx.font = `600 ${Math.max(7, Math.min(11, psz * 0.14))}px "Share Tech Mono", ui-monospace, monospace`;
|
||||
ctx.fillStyle = 'rgba(180, 255, 255, 0.5)';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('DATA', psx + psz * 0.5, psy + psz * 0.38);
|
||||
ctx.font = `500 ${Math.max(5, Math.min(8, psz * 0.1))}px "Share Tech Mono", ui-monospace, monospace`;
|
||||
ctx.fillStyle = 'rgba(120, 230, 255, 0.38)';
|
||||
ctx.fillText('/SEC/BLOCK', psx + psz * 0.5, psy + psz * 0.62);
|
||||
try { ctx.imageSmoothingEnabled = true; } catch (e) { /* ignore */ }
|
||||
ctx.drawImage(jumpPlatImg, dx, dy, dw, dh);
|
||||
ctx.restore();
|
||||
ctx.strokeStyle = 'rgba(0, 255, 240, 0.4)';
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.strokeRect(dx, dy, dw, dh);
|
||||
ctx.lineWidth = 1;
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(0, 40, 52, 0.72)';
|
||||
ctx.fillRect(psx + 3, psy + 3, psz - 6, psz - 6);
|
||||
ctx.strokeStyle = 'rgba(0, 255, 240, 0.55)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(psx + 3, psy + 3, psz - 6, psz - 6);
|
||||
ctx.strokeStyle = 'rgba(120, 255, 255, 0.35)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(psx + 5, psy + 5, psz - 10, psz - 10);
|
||||
const grad = ctx.createLinearGradient(psx, psy, psx, psy + psz);
|
||||
grad.addColorStop(0, 'rgba(0, 255, 240, 0.12)');
|
||||
grad.addColorStop(0.5, 'rgba(0, 0, 0, 0)');
|
||||
grad.addColorStop(1, 'rgba(0, 180, 200, 0.08)');
|
||||
ctx.fillStyle = grad;
|
||||
ctx.fillRect(psx + 4, psy + 4, psz - 8, psz - 8);
|
||||
ctx.lineWidth = 1;
|
||||
if (zDraw >= 1.15 && psz > 22) {
|
||||
ctx.save();
|
||||
ctx.font = `600 ${Math.max(7, Math.min(11, psz * 0.14))}px "Share Tech Mono", ui-monospace, monospace`;
|
||||
ctx.fillStyle = 'rgba(180, 255, 255, 0.5)';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('DATA', psx + psz * 0.5, psy + psz * 0.38);
|
||||
ctx.font = `500 ${Math.max(5, Math.min(8, psz * 0.1))}px "Share Tech Mono", ui-monospace, monospace`;
|
||||
ctx.fillStyle = 'rgba(120, 230, 255, 0.38)';
|
||||
ctx.fillText('/SEC/BLOCK', psx + psz * 0.5, psy + psz * 0.62);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// ทุกครั้งที่มีการเพิ่มหรือเปลี่ยน ให้เพิ่ม v +0.0001
|
||||
// หลังแก้ค่าแล้วต้อง copy ไป path ที่ Nginx ชี้ (หรือรัน copy-frogger-files-only.sh) ถึงจะเห็นบนเว็บ
|
||||
window.APP_VERSION = '0.0316';
|
||||
window.APP_VERSION = '0.0317';
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var t = document.querySelector('.version-tag');
|
||||
if (t) t.textContent = 'v ' + window.APP_VERSION;
|
||||
|
||||
@@ -3026,7 +3026,7 @@
|
||||
</div>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0306"></script>
|
||||
<script src="js/play.js?v=0.0316"></script>
|
||||
<script src="js/play.js?v=0.0318"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -167,6 +167,8 @@ const defaultMap = () => ({
|
||||
/** กระโดดให้รอด — แพลตฟอร์มในระบบ tile (ร่างสำหรับ side-view ภายหลัง) */
|
||||
jumpSurvivePlatforms: [],
|
||||
jumpSurvivePlatformArea: [],
|
||||
/** กริดช่องละ 0 หรือ 1–3 = รูปแพลตฟอร์มจาก jumpSurvivePlatformTiles[index-1] */
|
||||
jumpSurvivePlatformVariantArea: [],
|
||||
jumpSurviveHazardArea: [],
|
||||
jumpSurviveRisePxPerSec: 42,
|
||||
jumpSurviveGravity: 3200,
|
||||
@@ -601,6 +603,12 @@ function defaultGameTiming() {
|
||||
jumpSurviveJumpHeightMult: 1.5,
|
||||
/** จำกัดเวลารอบภารกิจ/มินิเกมกระโดดขึ้นแท่น (วินาที) — 0 = ไคลเอนต์ใช้ 60 วิ; ไม่ใช่ค่าเดียวกับพรมแดง */
|
||||
jumpSurviveMissionTimeSec: 0,
|
||||
/** รูปแพลตฟอร์ม jump_survive ช่อง 1–3 — ว่าง = ไคลเอนต์วาด cyan; w/h 0 = ใช้ขนาดไทล์ */
|
||||
jumpSurvivePlatformTiles: [
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
{ url: '', w: 0, h: 0 },
|
||||
],
|
||||
/** Stack ภารกิจ Tower (mnn93hpi): จำกัดเวลารอบ (วินาที) — ไคลเอนต์ใช้เมื่อเล่นภารกิจ */
|
||||
stackTowerMissionTimeSec: 90,
|
||||
/** Stack ภารกิจ Tower: ทีมพลาดได้กี่ครั้งก่อนจบ (ไม่รีเซ็ตหอ) */
|
||||
@@ -698,6 +706,36 @@ function clampJumpSurviveMissionTimeSec(n) {
|
||||
return Math.max(10, Math.min(7200, Math.floor(v)));
|
||||
}
|
||||
|
||||
/** กระโดดให้รอด: รูปแพลตฟอร์ม 3 ช่อง — url ว่าง = ไคลเอนต์วาด cyan; w/h ≤0 = ใช้ขนาดไทล์ตอนรัน */
|
||||
function normalizeJumpSurvivePlatformTilesFromTiming(val, legacyTileUrl) {
|
||||
let arr = val;
|
||||
if (typeof arr === 'string') {
|
||||
try {
|
||||
const p = JSON.parse(arr);
|
||||
if (Array.isArray(p)) arr = p;
|
||||
} catch (e) {
|
||||
arr = [];
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(arr)) arr = [];
|
||||
const legacy = typeof legacyTileUrl === 'string' ? sanitizeGauntletAssetUrl(legacyTileUrl) : '';
|
||||
const out = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const o = arr[i];
|
||||
const obj = o && typeof o === 'object' ? o : {};
|
||||
let url = sanitizeGauntletAssetUrl(typeof obj.url === 'string' ? obj.url : '');
|
||||
if (i === 0 && !url && legacy) url = legacy;
|
||||
let ww = Number(obj.w);
|
||||
let hh = Number(obj.h);
|
||||
if (!Number.isFinite(ww) || ww <= 0) ww = 0;
|
||||
else ww = Math.max(8, Math.min(4096, Math.round(ww)));
|
||||
if (!Number.isFinite(hh) || hh <= 0) hh = 0;
|
||||
else hh = Math.max(8, Math.min(4096, Math.round(hh)));
|
||||
out.push({ url, w: ww, h: hh });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function clampSpaceShooterMissionTimeSec(n) {
|
||||
const v = Number(n);
|
||||
if (Number.isNaN(v) || v <= 0) return 0;
|
||||
@@ -940,6 +978,10 @@ function loadGameTiming() {
|
||||
jumpSurviveMissionTimeSec: Object.prototype.hasOwnProperty.call(j, 'jumpSurviveMissionTimeSec')
|
||||
? clampJumpSurviveMissionTimeSec(j.jumpSurviveMissionTimeSec)
|
||||
: 0,
|
||||
jumpSurvivePlatformTiles: normalizeJumpSurvivePlatformTilesFromTiming(
|
||||
j.jumpSurvivePlatformTiles,
|
||||
j.jumpSurvivePlatformTileUrl,
|
||||
),
|
||||
spaceShooterMissionTimeSec: Object.prototype.hasOwnProperty.call(j, 'spaceShooterMissionTimeSec')
|
||||
? clampSpaceShooterMissionTimeSec(j.spaceShooterMissionTimeSec)
|
||||
: 0,
|
||||
@@ -1008,6 +1050,28 @@ function saveGameTimingToFile(d) {
|
||||
const jumpMissionSec = Object.prototype.hasOwnProperty.call(d, 'jumpSurviveMissionTimeSec')
|
||||
? clampJumpSurviveMissionTimeSec(d.jumpSurviveMissionTimeSec)
|
||||
: clampJumpSurviveMissionTimeSec(prevJumpMissionSec);
|
||||
const prevJumpTiles = normalizeJumpSurvivePlatformTilesFromTiming(
|
||||
prev.jumpSurvivePlatformTiles,
|
||||
prev.jumpSurvivePlatformTileUrl,
|
||||
);
|
||||
let jumpSurvivePlatformTiles;
|
||||
if (Object.prototype.hasOwnProperty.call(d, 'jumpSurvivePlatformTiles')) {
|
||||
jumpSurvivePlatformTiles = normalizeJumpSurvivePlatformTilesFromTiming(
|
||||
d.jumpSurvivePlatformTiles,
|
||||
d.jumpSurvivePlatformTileUrl,
|
||||
);
|
||||
} else if (Object.prototype.hasOwnProperty.call(d, 'jumpSurvivePlatformTileUrl')) {
|
||||
jumpSurvivePlatformTiles = normalizeJumpSurvivePlatformTilesFromTiming(
|
||||
[
|
||||
{ url: d.jumpSurvivePlatformTileUrl, w: 0, h: 0 },
|
||||
prevJumpTiles[1],
|
||||
prevJumpTiles[2],
|
||||
],
|
||||
'',
|
||||
);
|
||||
} else {
|
||||
jumpSurvivePlatformTiles = prevJumpTiles;
|
||||
}
|
||||
const prevSpaceShooterMissionSec = Object.prototype.hasOwnProperty.call(prev, 'spaceShooterMissionTimeSec')
|
||||
? prev.spaceShooterMissionTimeSec
|
||||
: 0;
|
||||
@@ -1119,6 +1183,7 @@ function saveGameTimingToFile(d) {
|
||||
stackHeavyBlockPercent,
|
||||
jumpSurviveJumpHeightMult: jumpMult,
|
||||
jumpSurviveMissionTimeSec: jumpMissionSec,
|
||||
jumpSurvivePlatformTiles,
|
||||
spaceShooterMissionTimeSec: spaceShooterMissionSec,
|
||||
spaceShooterShipImageUrls,
|
||||
spaceShooterAsteroidSpriteUrls,
|
||||
@@ -1865,6 +1930,27 @@ function normalizeJumpSurvivePlatformAreaOnMap(m) {
|
||||
m.jumpSurvivePlatformArea = rows;
|
||||
}
|
||||
|
||||
function normalizeJumpSurvivePlatformVariantAreaOnMap(m) {
|
||||
if (!m || m.gameType !== 'jump_survive') return;
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
const pa = m.jumpSurvivePlatformArea || [];
|
||||
const src = m.jumpSurvivePlatformVariantArea || [];
|
||||
const rows = [];
|
||||
for (let y = 0; y < h; y++) {
|
||||
const row = [];
|
||||
for (let x = 0; x < w; x++) {
|
||||
const has = pa[y] && pa[y][x] === 1;
|
||||
let v = has ? Math.floor(Number(src[y] && src[y][x])) : 0;
|
||||
if (has && (!Number.isFinite(v) || v < 1)) v = 1;
|
||||
if (v > 3) v = 3;
|
||||
if (!has) v = 0;
|
||||
row.push(v);
|
||||
}
|
||||
rows.push(row);
|
||||
}
|
||||
m.jumpSurvivePlatformVariantArea = rows;
|
||||
}
|
||||
|
||||
function normalizeJumpSurviveHazardAreaOnMap(m) {
|
||||
if (!m || m.gameType !== 'jump_survive') return;
|
||||
const w = m.width || 20, h = m.height || 15;
|
||||
@@ -1893,6 +1979,7 @@ function loadMaps() {
|
||||
const data = JSON.parse(fs.readFileSync(path.join(MAPS_DIR, f), 'utf8'));
|
||||
normalizeQuizCarryLayersOnMap(data);
|
||||
normalizeJumpSurvivePlatformAreaOnMap(data);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(data);
|
||||
normalizeJumpSurviveHazardAreaOnMap(data);
|
||||
normalizeQuizBattleDomeAreaOnMap(data);
|
||||
normalizeQuizBattlePathAreaOnMap(data);
|
||||
@@ -1974,9 +2061,11 @@ const server = http.createServer((req, res) => {
|
||||
if (!m.stackLandArea) m.stackLandArea = [];
|
||||
if (!Array.isArray(m.jumpSurvivePlatforms)) m.jumpSurvivePlatforms = [];
|
||||
if (!m.jumpSurvivePlatformArea) m.jumpSurvivePlatformArea = [];
|
||||
if (!m.jumpSurvivePlatformVariantArea) m.jumpSurvivePlatformVariantArea = [];
|
||||
if (!m.jumpSurviveHazardArea) m.jumpSurviveHazardArea = [];
|
||||
normalizeQuizCarryLayersOnMap(m);
|
||||
normalizeJumpSurvivePlatformAreaOnMap(m);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(m);
|
||||
normalizeJumpSurviveHazardAreaOnMap(m);
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
normalizeQuizBattlePathAreaOnMap(m);
|
||||
@@ -2016,9 +2105,11 @@ const server = http.createServer((req, res) => {
|
||||
if (!m.stackLandArea) m.stackLandArea = [];
|
||||
if (!Array.isArray(m.jumpSurvivePlatforms)) m.jumpSurvivePlatforms = [];
|
||||
if (!m.jumpSurvivePlatformArea) m.jumpSurvivePlatformArea = [];
|
||||
if (!m.jumpSurvivePlatformVariantArea) m.jumpSurvivePlatformVariantArea = [];
|
||||
if (!m.jumpSurviveHazardArea) m.jumpSurviveHazardArea = [];
|
||||
normalizeQuizCarryLayersOnMap(m);
|
||||
normalizeJumpSurvivePlatformAreaOnMap(m);
|
||||
normalizeJumpSurvivePlatformVariantAreaOnMap(m);
|
||||
normalizeJumpSurviveHazardAreaOnMap(m);
|
||||
normalizeQuizBattleDomeAreaOnMap(m);
|
||||
normalizeQuizBattlePathAreaOnMap(m);
|
||||
|
||||
Reference in New Issue
Block a user