https://srv1361159.hstgr.cloud/Create%20Room/ more than 4 people no bot
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
|
||||
function botCountForSlot(n) {
|
||||
var humans = Math.min(LOBBY_TOTAL_SLOTS, Math.max(1, parseInt(n, 10) || 1));
|
||||
if (humans >= 4) return 0; /* เลือก ≥ 4 คน → ไม่มีบอท (รอคนจริง) — ต้องตรงกับ server effectiveBotSlotCount */
|
||||
return Math.max(0, LOBBY_TOTAL_SLOTS - humans);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (typeof ensureLocalDevLogin === 'function') ensureLocalDevLogin();
|
||||
|
||||
function checkLoginStatus() {
|
||||
var isLoggedIn = localStorage.getItem('isLoggedIn');
|
||||
if (!isLoggedIn || isLoggedIn !== 'true') {
|
||||
window.location.href = typeof appPath === 'function' ? appPath('/Login/') : '/Login/';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!checkLoginStatus()) return;
|
||||
|
||||
var BASE = typeof appPath === 'function' ? appPath('/Game') : '/Game';
|
||||
|
||||
var PROD_GAME_API = 'https://srv1361159.hstgr.cloud/Game';
|
||||
|
||||
function resolveCreateRoomApiBase() {
|
||||
if (typeof resolveGameApiBase === 'function') {
|
||||
return resolveGameApiBase().replace(/\/$/, '');
|
||||
}
|
||||
if (typeof window.__JD_GAME_API_BASE__ === 'string' && window.__JD_GAME_API_BASE__) {
|
||||
return window.__JD_GAME_API_BASE__.replace(/\/$/, '');
|
||||
}
|
||||
var pageGame = BASE.replace(/\/$/, '');
|
||||
try {
|
||||
var h = (location.hostname || '').toLowerCase();
|
||||
if (h === 'localhost' || h === '127.0.0.1') {
|
||||
try {
|
||||
if (localStorage.getItem('jdUseLocalGameApi') === '1') {
|
||||
var port = localStorage.getItem('jdGameNodePort') || '13010';
|
||||
return 'http://127.0.0.1:' + String(port).replace(/\D/g, '') + '/Game';
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
return PROD_GAME_API;
|
||||
}
|
||||
} catch (e2) { /* ignore */ }
|
||||
return pageGame;
|
||||
}
|
||||
|
||||
var SERVER = resolveCreateRoomApiBase();
|
||||
var LOBBY_A_MAP_ID = 'mlsbbxfe';
|
||||
var LOBBY_TOTAL_SLOTS = 6;
|
||||
|
||||
var selectedSlot = 3;
|
||||
var isPrivate = false;
|
||||
|
||||
function getNick() {
|
||||
return (localStorage.getItem('playerName') || '').trim() || 'ผู้เล่น';
|
||||
}
|
||||
|
||||
function botCountForSlot(n) {
|
||||
var humans = Math.min(LOBBY_TOTAL_SLOTS, Math.max(1, parseInt(n, 10) || 1));
|
||||
return Math.max(0, LOBBY_TOTAL_SLOTS - humans);
|
||||
}
|
||||
|
||||
function maxPlayersForSlot(n) {
|
||||
var humans = Math.min(LOBBY_TOTAL_SLOTS, Math.max(1, parseInt(n, 10) || 3));
|
||||
return Math.min(10, Math.max(1, humans));
|
||||
}
|
||||
|
||||
var btnBack = document.getElementById('btn-back');
|
||||
var tabPublic = document.getElementById('tab-public');
|
||||
var tabPrivate = document.getElementById('tab-private');
|
||||
var privateBlock = document.getElementById('create-private-block');
|
||||
var inputRoomName = document.getElementById('create-room-name');
|
||||
var inputPrivatePin = document.getElementById('create-private-pin');
|
||||
var playerBtnsWrap = document.getElementById('create-player-btns');
|
||||
var btnConfirm = document.getElementById('btn-confirm');
|
||||
var statusEl = document.getElementById('create-status');
|
||||
var summaryEl = document.getElementById('create-summary');
|
||||
|
||||
function updateSummary() {
|
||||
if (!summaryEl) return;
|
||||
var bots = botCountForSlot(selectedSlot);
|
||||
summaryEl.textContent = 'สรุปจำนวน : ' + selectedSlot + ' ผู้เล่น + ' + bots + ' Bot';
|
||||
}
|
||||
|
||||
btnBack?.addEventListener('click', function () {
|
||||
window.location.href = typeof appPath === 'function' ? appPath('/Main-Menu/') : '/Main-Menu/';
|
||||
});
|
||||
|
||||
inputPrivatePin?.addEventListener('input', function (e) {
|
||||
e.target.value = e.target.value.replace(/[^0-9]/g, '').slice(0, 4);
|
||||
updateConfirmButtonEnabled();
|
||||
});
|
||||
|
||||
function updateModeImages() {
|
||||
[tabPublic, tabPrivate].forEach(function (btn) {
|
||||
if (!btn) return;
|
||||
var img = btn.querySelector('.create-mode-btn-img');
|
||||
if (!img) return;
|
||||
var isActive = btn.classList.contains('create-mode-btn--active');
|
||||
img.src = isActive ? btn.getAttribute('data-img-h') : btn.getAttribute('data-img');
|
||||
});
|
||||
}
|
||||
|
||||
function setTab(privateMode) {
|
||||
isPrivate = privateMode;
|
||||
tabPublic?.classList.toggle('create-mode-btn--active', !privateMode);
|
||||
tabPrivate?.classList.toggle('create-mode-btn--active', privateMode);
|
||||
tabPublic?.setAttribute('aria-selected', privateMode ? 'false' : 'true');
|
||||
tabPrivate?.setAttribute('aria-selected', privateMode ? 'true' : 'false');
|
||||
if (privateBlock) privateBlock.classList.toggle('create-view--hidden', !privateMode);
|
||||
updateModeImages();
|
||||
updateConfirmButtonEnabled();
|
||||
}
|
||||
|
||||
tabPublic?.addEventListener('click', function () { setTab(false); });
|
||||
tabPrivate?.addEventListener('click', function () { setTab(true); });
|
||||
|
||||
function updatePlayerImages() {
|
||||
if (!playerBtnsWrap) return;
|
||||
var buttons = playerBtnsWrap.querySelectorAll('.create-num-btn');
|
||||
buttons.forEach(function (btn) {
|
||||
var v = parseInt(btn.getAttribute('data-slot'), 10);
|
||||
var on = v === selectedSlot;
|
||||
var img = btn.querySelector('.create-num-btn-img');
|
||||
if (img) {
|
||||
img.src = on ? btn.getAttribute('data-img-h') : btn.getAttribute('data-img');
|
||||
}
|
||||
btn.setAttribute('aria-pressed', on ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
function selectSlot(n) {
|
||||
selectedSlot = Math.min(6, Math.max(1, parseInt(n, 10) || 3));
|
||||
updatePlayerImages();
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
function buildPlayerButtons() {
|
||||
if (!playerBtnsWrap) return;
|
||||
playerBtnsWrap.innerHTML = '';
|
||||
for (var i = 1; i <= 6; i++) {
|
||||
var b = document.createElement('button');
|
||||
b.type = 'button';
|
||||
b.className = 'create-num-btn';
|
||||
b.setAttribute('data-slot', String(i));
|
||||
b.setAttribute('data-img', 'IMAGE/player-' + i + '.png');
|
||||
b.setAttribute('data-img-h', 'IMAGE/player-' + i + '-h.png');
|
||||
b.setAttribute('aria-label', 'เลือก ' + i + ' ผู้เล่น รวม ' + LOBBY_TOTAL_SLOTS + ' ที่นั่ง (Bot ' + botCountForSlot(i) + ' ตัว)');
|
||||
b.setAttribute('aria-pressed', i === selectedSlot ? 'true' : 'false');
|
||||
|
||||
var img = document.createElement('img');
|
||||
img.className = 'create-num-btn-img';
|
||||
img.alt = String(i);
|
||||
img.decoding = 'async';
|
||||
img.src = i === selectedSlot ? 'IMAGE/player-' + i + '-h.png' : 'IMAGE/player-' + i + '.png';
|
||||
b.appendChild(img);
|
||||
|
||||
b.addEventListener('click', function () {
|
||||
selectSlot(this.getAttribute('data-slot'));
|
||||
});
|
||||
playerBtnsWrap.appendChild(b);
|
||||
}
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
function getRoomName() {
|
||||
return (inputRoomName && inputRoomName.value || '').trim();
|
||||
}
|
||||
|
||||
function getPrivatePinDigits() {
|
||||
if (!inputPrivatePin) return '';
|
||||
return String(inputPrivatePin.value || '').replace(/\D/g, '').slice(0, 4);
|
||||
}
|
||||
|
||||
function isPrivatePinComplete() {
|
||||
return /^\d{4}$/.test(getPrivatePinDigits());
|
||||
}
|
||||
|
||||
function updateConfirmButtonEnabled() {
|
||||
if (!btnConfirm) return;
|
||||
if (btnConfirm.getAttribute('data-create-loading') === '1') return;
|
||||
btnConfirm.disabled = isPrivate && !isPrivatePinComplete();
|
||||
}
|
||||
|
||||
btnConfirm?.addEventListener('click', function () {
|
||||
if (statusEl) statusEl.textContent = '';
|
||||
var mapId = LOBBY_A_MAP_ID;
|
||||
var name = getRoomName();
|
||||
if (!name) {
|
||||
if (statusEl) statusEl.textContent = 'กรุณาใส่ชื่อห้อง';
|
||||
return;
|
||||
}
|
||||
if (isPrivate && !isPrivatePinComplete()) {
|
||||
if (statusEl) statusEl.textContent = 'กรุณาใส่รหัสผ่าน 4 หลัก (ตัวเลข) สำหรับห้องส่วนตัว';
|
||||
inputPrivatePin?.focus();
|
||||
return;
|
||||
}
|
||||
btnConfirm.setAttribute('data-create-loading', '1');
|
||||
btnConfirm.disabled = true;
|
||||
var body = {
|
||||
mapId: mapId,
|
||||
name: name,
|
||||
isPrivate: isPrivate,
|
||||
maxPlayers: maxPlayersForSlot(selectedSlot),
|
||||
password: isPrivate ? getPrivatePinDigits() : '',
|
||||
};
|
||||
fetch(SERVER + '/api/spaces', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, j: j }; }); })
|
||||
.then(function (res) {
|
||||
if (!res.j || !res.j.ok) {
|
||||
if (statusEl) statusEl.textContent = (res.j && res.j.error) || 'สร้างห้องไม่สำเร็จ';
|
||||
btnConfirm.removeAttribute('data-create-loading');
|
||||
updateConfirmButtonEnabled();
|
||||
return;
|
||||
}
|
||||
var sid = res.j.spaceId;
|
||||
if (!sid) {
|
||||
if (statusEl) statusEl.textContent = 'ไม่ได้รับรหัสห้อง';
|
||||
btnConfirm.removeAttribute('data-create-loading');
|
||||
updateConfirmButtonEnabled();
|
||||
return;
|
||||
}
|
||||
if (isPrivate) {
|
||||
try { localStorage.setItem('roomPinHint_' + sid, getPrivatePinDigits()); } catch (e) {}
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('lastCreatedSpaceId', sid);
|
||||
localStorage.setItem('lastCreatedSpaceName', name);
|
||||
} catch (e) {}
|
||||
var lobbyUrl = BASE + '/room-lobby.html?space=' + encodeURIComponent(sid) + '&nick=' + encodeURIComponent(getNick());
|
||||
lobbyUrl += '&displayRoom=' + encodeURIComponent(name);
|
||||
if (isPrivate) lobbyUrl += '&pass=' + encodeURIComponent(getPrivatePinDigits()); /* host เข้าห้องตัวเองผ่านด่านรหัส */
|
||||
try {
|
||||
var caseId = localStorage.getItem('caseId');
|
||||
var prevSpaceName = localStorage.getItem('prevSpaceName');
|
||||
if (caseId) lobbyUrl += '&caseId=' + encodeURIComponent(caseId);
|
||||
if (prevSpaceName) lobbyUrl += '&prevSpaceName=' + encodeURIComponent(prevSpaceName);
|
||||
} catch (e) {}
|
||||
window.location.replace((typeof appPath === 'function' ? appPath('/Loading/') : '/Loading/') + '?redirect=' + encodeURIComponent(lobbyUrl));
|
||||
})
|
||||
.catch(function () {
|
||||
if (statusEl) statusEl.textContent = 'เชื่อมต่อเซิร์ฟเวอร์ไม่ได้';
|
||||
btnConfirm.removeAttribute('data-create-loading');
|
||||
updateConfirmButtonEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
inputRoomName?.addEventListener('input', function () {
|
||||
updateConfirmButtonEnabled();
|
||||
});
|
||||
|
||||
buildPlayerButtons();
|
||||
setTab(false);
|
||||
})();
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
|
||||
<script src="../app-base.js"></script>
|
||||
<script src="create-room.js?v=20260623"></script>
|
||||
<script src="create-room.js?v=20260701"></script>
|
||||
<script>
|
||||
(function(){
|
||||
var content = document.querySelector('.create-content');
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="th">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>สร้างห้องเกม — 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@500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css?v=1.007">
|
||||
</head>
|
||||
<body>
|
||||
<div class="create-bg" aria-hidden="true">
|
||||
<div class="create-bg-grid"></div>
|
||||
<img src="../Main-Menu/bg.png" alt="" class="create-bg-img">
|
||||
</div>
|
||||
<div class="create-char-layer" aria-hidden="true">
|
||||
<img src="../Main-Menu/char-main.png" alt="" class="create-char-img">
|
||||
</div>
|
||||
<div class="create-vignette" aria-hidden="true"></div>
|
||||
|
||||
<div class="create-ui">
|
||||
<button type="button" class="menu-btn-back" id="btn-back" aria-label="ย้อนกลับ">
|
||||
<img src="../Main-Menu/IMAGE/btn-back.png" alt="ย้อนกลับ" class="menu-btn-back-img" decoding="async">
|
||||
</button>
|
||||
|
||||
<main class="create-content">
|
||||
<img src="IMAGE/txt-createroom.png" alt="สร้างห้องเกม" class="create-title-img" decoding="async">
|
||||
<img src="IMAGE/line.png" alt="" class="create-title-line" aria-hidden="true" decoding="async">
|
||||
|
||||
<section class="create-panel" aria-label="ฟอร์มสร้างห้อง">
|
||||
|
||||
<!-- ชื่อห้อง -->
|
||||
<div class="create-field create-field--inline create-field--room">
|
||||
<img src="IMAGE/txt-roomname.png" alt="ชื่อห้อง :" class="create-label-img" decoding="async">
|
||||
<div class="create-input-shell">
|
||||
<input type="text" id="create-room-name" class="create-input" maxlength="48" autocomplete="off" spellcheck="false" placeholder="ตั้งชื่อห้อง" aria-label="ชื่อห้อง">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="IMAGE/line.png" alt="" class="create-line" aria-hidden="true" decoding="async">
|
||||
|
||||
<!-- โหมด -->
|
||||
<div class="create-field create-field--inline create-field--mode">
|
||||
<img src="IMAGE/txt-mode.png" alt="โหมด :" class="create-label-img" decoding="async">
|
||||
<div class="create-mode-row" role="tablist" aria-label="โหมดห้อง">
|
||||
<button type="button" class="create-mode-btn create-mode-btn--active" id="tab-public" role="tab" aria-selected="true" data-img="IMAGE/btn-publish.png" data-img-h="IMAGE/btn-publish-h.png">
|
||||
<img src="IMAGE/btn-publish-h.png" alt="ห้องสาธารณะ" class="create-mode-btn-img" decoding="async">
|
||||
</button>
|
||||
<button type="button" class="create-mode-btn" id="tab-private" role="tab" aria-selected="false" data-img="IMAGE/btn-private.png" data-img-h="IMAGE/btn-private-h.png">
|
||||
<img src="IMAGE/btn-private.png" alt="ห้องส่วนตัว" class="create-mode-btn-img" decoding="async">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PASSWORD (ซ่อน default) -->
|
||||
<div id="create-private-block" class="create-private-block create-view--hidden" role="tabpanel">
|
||||
<img src="IMAGE/txt-password.png" alt="PASSWORD : (รหัสผ่าน 4 ตัว)" class="create-label-img create-label-img--password" decoding="async">
|
||||
<div class="create-input-shell create-input-shell--pin">
|
||||
<input type="text" id="create-private-pin" class="create-input create-input--pin" placeholder="0000" maxlength="4" inputmode="numeric" autocomplete="off" aria-label="รหัสผ่าน 4 หลัก">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="IMAGE/line.png" alt="" class="create-line" aria-hidden="true" decoding="async">
|
||||
|
||||
<!-- จำนวนผู้เล่น -->
|
||||
<div class="create-field create-field--inline create-field--players">
|
||||
<img src="IMAGE/txt-players.png" alt="จำนวนผู้เล่น :" class="create-label-img" decoding="async">
|
||||
<div class="create-num-grid" id="create-player-btns" role="group" aria-label="จำนวนผู้เล่น"></div>
|
||||
</div>
|
||||
|
||||
<img src="IMAGE/line.png" alt="" class="create-line" aria-hidden="true" decoding="async">
|
||||
|
||||
<!-- สรุป -->
|
||||
<div class="create-summary-wrap">
|
||||
<div class="create-summary" id="create-summary" aria-live="polite">สรุปจำนวน : 3 ผู้เล่น + 3 Bot</div>
|
||||
</div>
|
||||
|
||||
<!-- ยืนยัน -->
|
||||
<button type="button" class="create-confirm" id="btn-confirm" aria-label="ยืนยันสร้างห้อง">
|
||||
<img src="IMAGE/btn-confirm.png" alt="ยืนยัน" class="create-confirm-img" decoding="async">
|
||||
</button>
|
||||
|
||||
<p class="create-status" id="create-status" role="status"></p>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="../app-base.js"></script>
|
||||
<script src="create-room.js?v=20260623"></script>
|
||||
<script>
|
||||
(function(){
|
||||
var content = document.querySelector('.create-content');
|
||||
if(!content) return;
|
||||
var tid = 0;
|
||||
function autoFit(){
|
||||
clearTimeout(tid);
|
||||
tid = setTimeout(function(){
|
||||
var isLand = window.matchMedia('(orientation:landscape) and (max-width:1200px)').matches
|
||||
|| window.matchMedia('(orientation:landscape) and (max-height:620px)').matches;
|
||||
if(!isLand){ content.style.zoom = ''; return; }
|
||||
content.style.zoom = '1';
|
||||
requestAnimationFrame(function(){
|
||||
var vpH = window.innerHeight;
|
||||
var rect = content.getBoundingClientRect();
|
||||
var need = rect.top + rect.height;
|
||||
if(need > vpH){
|
||||
var s = Math.max(0.4, (vpH * 0.97) / need);
|
||||
content.style.zoom = String(Math.floor(s * 1000) / 1000);
|
||||
} else {
|
||||
content.style.zoom = '1';
|
||||
}
|
||||
});
|
||||
}, 30);
|
||||
}
|
||||
autoFit();
|
||||
window.addEventListener('resize', autoFit);
|
||||
window.addEventListener('orientationchange', function(){ setTimeout(autoFit, 300); });
|
||||
var panel = document.querySelector('.create-panel');
|
||||
if(panel){
|
||||
new MutationObserver(function(){ setTimeout(autoFit, 80); }).observe(panel, {childList:true, subtree:true, attributes:true, attributeFilter:['class']});
|
||||
}
|
||||
/* summary เด้ง (bump) ทุกครั้งที่ตัวเลขสรุปเปลี่ยน */
|
||||
var sumEl = document.getElementById('create-summary');
|
||||
if(sumEl){
|
||||
new MutationObserver(function(){
|
||||
sumEl.classList.remove('is-bump');
|
||||
void sumEl.offsetWidth; /* reflow → restart animation */
|
||||
sumEl.classList.add('is-bump');
|
||||
}).observe(sumEl, {childList:true, characterData:true, subtree:true});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -215,6 +215,8 @@ function effectiveBotSlotCount(space) {
|
||||
let n = parseInt(space && space.botSlotCount, 10);
|
||||
if (isNaN(n) || n < 0) n = 0;
|
||||
const maxP = space && space.maxPlayers ? parseInt(space.maxPlayers, 10) : 0;
|
||||
/* เลือกผู้เล่น ≥ 4 คนตอนสร้างห้อง → ไม่เติมบอท (รอคนจริง) — เว้นแต่ host ตั้งบอทเองภายหลัง (n>0) */
|
||||
if (n === 0 && maxP >= 4) return 0;
|
||||
if (n === 0 && maxP > 0 && maxP < LOBBY_SLOT_TOTAL) {
|
||||
n = LOBBY_SLOT_TOTAL - maxP;
|
||||
if (space) space.botSlotCount = n;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user