Game: default character = first uploaded (API order by mtime); defer join until loaded
This commit is contained in:
@@ -146,7 +146,7 @@
|
||||
</div>
|
||||
<div class="editor-card__row editor-card__row--actions">
|
||||
<button type="button" id="btn-save" class="editor-btn-primary">บันทึกฉาก</button>
|
||||
<button type="button" id="btn-play-test" title="บันทึกฉากแล้วเท่านั้น — เปิดแท็บใหม่เล่นทดสอบด้วยตัวละครเริ่มต้น">ทดลองเล่น</button>
|
||||
<button type="button" id="btn-play-test" title="บันทึกฉากแล้วเท่านั้น — เปิดแท็บใหม่เล่นทดสอบด้วยตัวละครตัวแรกที่สร้าง (จาก /Game/character.html)">ทดลองเล่น</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,21 @@
|
||||
const previewMode = params.get('preview') === '1';
|
||||
const forceDefaultCharacter = params.get('defaultChar') === '1';
|
||||
const editorEmbedReturn = params.get('editorEmbed') === '1';
|
||||
/** จนกว่าโหลด /api/characters — placeholder ชั่วคราวก่อน join */
|
||||
const LEGACY_PLACEHOLDER_CHARACTER_ID = 'Chatest';
|
||||
let firstCharacterDefaultResolved = null;
|
||||
const firstCharacterDefaultPromise = fetch(BASE + '/api/characters')
|
||||
.then((r) => r.json())
|
||||
.then((list) => {
|
||||
if (Array.isArray(list) && list.length > 0 && list[0] && list[0].id) {
|
||||
firstCharacterDefaultResolved = String(list[0].id);
|
||||
} else {
|
||||
firstCharacterDefaultResolved = '';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
firstCharacterDefaultResolved = '';
|
||||
});
|
||||
const lobbyLevelParam = params.get('lobbyLevel');
|
||||
const lobbyCaseParam = params.get('case');
|
||||
if (lobbyLevelParam || lobbyCaseParam) {
|
||||
@@ -443,12 +458,19 @@
|
||||
if (img) return img;
|
||||
return defaultAvatarImg;
|
||||
}
|
||||
const DEFAULT_CHARACTER_ID = 'Chatest';
|
||||
function getStoredCharacterId() {
|
||||
try { return localStorage.getItem('gameCharacterId') || DEFAULT_CHARACTER_ID; } catch (e) { return DEFAULT_CHARACTER_ID; }
|
||||
try {
|
||||
const v = localStorage.getItem('gameCharacterId');
|
||||
if (v) return v;
|
||||
} catch (e) { /* ignore */ }
|
||||
if (firstCharacterDefaultResolved === null) return LEGACY_PLACEHOLDER_CHARACTER_ID;
|
||||
return firstCharacterDefaultResolved || '';
|
||||
}
|
||||
function getPlayCharacterId() {
|
||||
if (forceDefaultCharacter) return DEFAULT_CHARACTER_ID;
|
||||
if (forceDefaultCharacter) {
|
||||
if (firstCharacterDefaultResolved === null) return LEGACY_PLACEHOLDER_CHARACTER_ID;
|
||||
return firstCharacterDefaultResolved || '';
|
||||
}
|
||||
return getStoredCharacterId();
|
||||
}
|
||||
const MOVE_SPEED = 0.15;
|
||||
@@ -4973,43 +4995,45 @@
|
||||
}
|
||||
|
||||
socket.on('connect', () => {
|
||||
socket.emit('join-space', { spaceId, nickname: nick, characterId: getPlayCharacterId() }, (res) => {
|
||||
if (!res || !res.ok) {
|
||||
const errMsg = (res && res.error) || 'เข้าร่วมไม่ได้';
|
||||
alert(errMsg);
|
||||
const caseLocked = /เริ่มคดี|ไม่รับผู้เล่น/.test(errMsg);
|
||||
if (caseLocked) {
|
||||
window.location.replace('room-lobby.html?space=' + encodeURIComponent(spaceId) + '&nick=' + encodeURIComponent(nick));
|
||||
} else {
|
||||
window.location.replace(BASE + '/lobby.html');
|
||||
firstCharacterDefaultPromise.then(() => {
|
||||
socket.emit('join-space', { spaceId, nickname: nick, characterId: getPlayCharacterId() }, (res) => {
|
||||
if (!res || !res.ok) {
|
||||
const errMsg = (res && res.error) || 'เข้าร่วมไม่ได้';
|
||||
alert(errMsg);
|
||||
const caseLocked = /เริ่มคดี|ไม่รับผู้เล่น/.test(errMsg);
|
||||
if (caseLocked) {
|
||||
window.location.replace('room-lobby.html?space=' + encodeURIComponent(spaceId) + '&nick=' + encodeURIComponent(nick));
|
||||
} else {
|
||||
window.location.replace(BASE + '/lobby.html');
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
myId = socket.id;
|
||||
if (previewMode && editorEmbedReturn && res.mapData && res.mapData.gameType === 'lobby') {
|
||||
const q = [];
|
||||
q.push('space=' + encodeURIComponent(spaceId));
|
||||
q.push('nick=' + encodeURIComponent(nick));
|
||||
const mid = params.get('map');
|
||||
if (mid) q.push('map=' + encodeURIComponent(mid));
|
||||
q.push('preview=1');
|
||||
q.push('editorEmbed=1');
|
||||
if (params.get('defaultChar') != null) q.push('defaultChar=' + encodeURIComponent(params.get('defaultChar')));
|
||||
window.location.replace(BASE + '/room-lobby.html?' + q.join('&'));
|
||||
return;
|
||||
}
|
||||
const playMapId = params.get('map');
|
||||
if (playMapId) {
|
||||
fetch(BASE + '/api/maps/' + encodeURIComponent(playMapId))
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => {
|
||||
if (data) applyMapAndStart(data, res);
|
||||
else applyMapAndStart(res.mapData, res);
|
||||
})
|
||||
.catch(() => applyMapAndStart(res.mapData, res));
|
||||
} else {
|
||||
applyMapAndStart(res.mapData, res);
|
||||
}
|
||||
myId = socket.id;
|
||||
if (previewMode && editorEmbedReturn && res.mapData && res.mapData.gameType === 'lobby') {
|
||||
const q = [];
|
||||
q.push('space=' + encodeURIComponent(spaceId));
|
||||
q.push('nick=' + encodeURIComponent(nick));
|
||||
const mid = params.get('map');
|
||||
if (mid) q.push('map=' + encodeURIComponent(mid));
|
||||
q.push('preview=1');
|
||||
q.push('editorEmbed=1');
|
||||
if (params.get('defaultChar') != null) q.push('defaultChar=' + encodeURIComponent(params.get('defaultChar')));
|
||||
window.location.replace(BASE + '/room-lobby.html?' + q.join('&'));
|
||||
return;
|
||||
}
|
||||
const playMapId = params.get('map');
|
||||
if (playMapId) {
|
||||
fetch(BASE + '/api/maps/' + encodeURIComponent(playMapId))
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => {
|
||||
if (data) applyMapAndStart(data, res);
|
||||
else applyMapAndStart(res.mapData, res);
|
||||
})
|
||||
.catch(() => applyMapAndStart(res.mapData, res));
|
||||
} else {
|
||||
applyMapAndStart(res.mapData, res);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6418,9 +6442,13 @@
|
||||
const av = document.createElement('img');
|
||||
av.className = 'play-cyber-score-av';
|
||||
av.alt = '';
|
||||
const cid = row.characterId || DEFAULT_CHARACTER_ID;
|
||||
const im = getCharacterImg(cid, 'down');
|
||||
if (im && im.src) av.src = im.src;
|
||||
const cid = row.characterId ? String(row.characterId) : '';
|
||||
if (!cid) {
|
||||
av.src = defaultAvatarImg.src;
|
||||
} else {
|
||||
const im = getCharacterImg(cid, 'down');
|
||||
if (im && im.src) av.src = im.src;
|
||||
}
|
||||
const mid = document.createElement('div');
|
||||
mid.className = 'play-cyber-score-mid';
|
||||
const name = document.createElement('span');
|
||||
|
||||
@@ -7,6 +7,21 @@
|
||||
const previewMode = params.get('preview') === '1';
|
||||
const editorEmbedReturn = params.get('editorEmbed') === '1';
|
||||
const forceDefaultCharacter = params.get('defaultChar') === '1';
|
||||
/** จนกว่าโหลด /api/characters — ใช้ชั่วคราวก่อน join (หลังโหลดใช้ตัวแรกตามเวลาไฟล์บนเซิร์ฟเวอร์) */
|
||||
const LEGACY_PLACEHOLDER_CHARACTER_ID = 'Chatest';
|
||||
let firstCharacterDefaultResolved = null;
|
||||
const firstCharacterDefaultPromise = fetch(BASE + '/api/characters')
|
||||
.then((r) => r.json())
|
||||
.then((list) => {
|
||||
if (Array.isArray(list) && list.length > 0 && list[0] && list[0].id) {
|
||||
firstCharacterDefaultResolved = String(list[0].id);
|
||||
} else {
|
||||
firstCharacterDefaultResolved = '';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
firstCharacterDefaultResolved = '';
|
||||
});
|
||||
if (!spaceId) { location.href = BASE + '/lobby.html'; return; }
|
||||
|
||||
const CREATE_ROOM_URL = '/Create%20Room/';
|
||||
@@ -1147,10 +1162,17 @@
|
||||
if (quizModeActive) renderQuizScoreboard(lastQuizScores);
|
||||
}
|
||||
|
||||
const DEFAULT_CHARACTER_ID = 'Chatest';
|
||||
function getStoredCharacterId() {
|
||||
if (forceDefaultCharacter) return DEFAULT_CHARACTER_ID;
|
||||
try { return localStorage.getItem('gameCharacterId') || DEFAULT_CHARACTER_ID; } catch (e) { return DEFAULT_CHARACTER_ID; }
|
||||
if (forceDefaultCharacter) {
|
||||
if (firstCharacterDefaultResolved === null) return LEGACY_PLACEHOLDER_CHARACTER_ID;
|
||||
return firstCharacterDefaultResolved || '';
|
||||
}
|
||||
try {
|
||||
const v = localStorage.getItem('gameCharacterId');
|
||||
if (v) return v;
|
||||
} catch (e) { /* ignore */ }
|
||||
if (firstCharacterDefaultResolved === null) return LEGACY_PLACEHOLDER_CHARACTER_ID;
|
||||
return firstCharacterDefaultResolved || '';
|
||||
}
|
||||
|
||||
function updatePreviewClaimHostUi() {
|
||||
@@ -1168,7 +1190,11 @@
|
||||
const img = document.getElementById('room-lobby-profile-avatar');
|
||||
if (!img) return;
|
||||
const id = getStoredCharacterId();
|
||||
img.src = BASE + '/img/characters/' + encodeURIComponent(id) + '_down.png?ch=' + encodeURIComponent(id);
|
||||
if (!id) {
|
||||
img.src = defaultAvatarImg.src;
|
||||
} else {
|
||||
img.src = BASE + '/img/characters/' + encodeURIComponent(id) + '_down.png?ch=' + encodeURIComponent(id);
|
||||
}
|
||||
img.alt = (nick || 'ผู้เล่น') + ' — ตัวละคร';
|
||||
}
|
||||
|
||||
@@ -1178,7 +1204,8 @@
|
||||
}
|
||||
|
||||
socket.on('connect', () => {
|
||||
socket.emit('join-space', { spaceId, nickname: nick, characterId: getStoredCharacterId() }, (res) => {
|
||||
firstCharacterDefaultPromise.then(() => {
|
||||
socket.emit('join-space', { spaceId, nickname: nick, characterId: getStoredCharacterId() }, (res) => {
|
||||
if (!res || !res.ok) {
|
||||
var joinErr = (res && res.error) || 'เข้าไม่ได้';
|
||||
if (/เริ่มคดี|ไม่รับผู้เล่น/.test(joinErr)) {
|
||||
@@ -1269,6 +1296,7 @@
|
||||
}).catch(() => {});
|
||||
}, 600);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const LERP = 0.2;
|
||||
|
||||
@@ -669,7 +669,7 @@
|
||||
</div>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0166"></script>
|
||||
<script src="js/play.js?v=0.078"></script>
|
||||
<script src="js/play.js?v=0.079"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1015,7 +1015,7 @@
|
||||
</div>
|
||||
<script src="/Game/socket.io/socket.io.js"></script>
|
||||
<script src="js/version.js?v=0.0122"></script>
|
||||
<script src="js/room-lobby.js?v=0.0174"></script>
|
||||
<script src="js/room-lobby.js?v=0.0175"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+26
-3
@@ -1337,14 +1337,37 @@ const server = http.createServer((req, res) => {
|
||||
m = f.match(/^(.+)_(up|down|left|right)(?:_\d+)?_layer_[a-zA-Z]+\.(png|jpg|jpeg|gif|webp)$/i);
|
||||
if (m) ids.add(m[1]);
|
||||
});
|
||||
const list = [...ids].map((id) => {
|
||||
const idArr = [...ids];
|
||||
const oldestMsById = {};
|
||||
idArr.forEach((id) => {
|
||||
let oldest = Infinity;
|
||||
const prefix = id + '_';
|
||||
files.forEach((f) => {
|
||||
if (!f.startsWith(prefix) || !ext.test(f)) return;
|
||||
try {
|
||||
const st = fs.statSync(path.join(CHARACTERS_DIR, f));
|
||||
const t = st.mtimeMs != null ? st.mtimeMs : st.mtime.getTime();
|
||||
if (t < oldest) oldest = t;
|
||||
} catch (e) { /* ignore */ }
|
||||
});
|
||||
oldestMsById[id] = oldest === Infinity ? 0 : oldest;
|
||||
});
|
||||
idArr.sort((a, b) => {
|
||||
const ta = oldestMsById[a] || 0;
|
||||
const tb = oldestMsById[b] || 0;
|
||||
if (ta !== tb) return ta - tb;
|
||||
return String(a).localeCompare(String(b));
|
||||
});
|
||||
const list = idArr.map((id) => {
|
||||
const prefix = id + '_';
|
||||
const hasLayerFiles = files.some((f) => f.startsWith(prefix) && f.includes('_layer_') && ext.test(f));
|
||||
return { id, name: id, hasLayerFiles };
|
||||
});
|
||||
return res.writeHead(200, { 'Content-Type': 'application/json' }), res.end(JSON.stringify(list));
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
return res.end(JSON.stringify(list));
|
||||
} catch (e) {
|
||||
return res.writeHead(200, { 'Content-Type': 'application/json' }), res.end(JSON.stringify([]));
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
return res.end(JSON.stringify([]));
|
||||
}
|
||||
}
|
||||
const urlPath = url.split('?')[0];
|
||||
|
||||
Reference in New Issue
Block a user