update flow design 1.33
This commit is contained in:
@@ -2708,31 +2708,49 @@
|
||||
quizWrongGhostImg = img;
|
||||
return img;
|
||||
}
|
||||
/** วาด "ผี" คุมทับตัวผู้เล่นในแมป mng8a80o เมื่อตอบผิด — ครอบทั้งตัวละครเลย */
|
||||
function drawQuizWrongGhostOverlayPlay(ax, ay, entId) {
|
||||
if (!isQuizQuestionMissionUiMapPlay()) return;
|
||||
/** ตรวจว่า peer/me ตอบผิดในแมป mng8a80o แล้ว → คุมผีแทน avatar */
|
||||
function isQuizWrongGhostActiveForEnt(entId) {
|
||||
if (!isQuizQuestionMissionUiMapPlay()) return false;
|
||||
const sid = String(entId == null ? '' : entId);
|
||||
const isMeEnt = (myId != null && sid === String(myId));
|
||||
const wrong = isMeEnt
|
||||
return isMeEnt
|
||||
? !!(playQuizPlayerLocal && (playQuizPlayerLocal.cannotTrue || playQuizPlayerLocal.cannotFalse))
|
||||
: !!playQuizEverWrong[sid];
|
||||
if (!wrong) return;
|
||||
}
|
||||
|
||||
/** วาด "ผี" แทน avatar ทั้งตัวในแมป mng8a80o เมื่อตอบผิด — คุมผีเลย */
|
||||
function drawQuizWrongGhostOverlayPlay(ax, ay, entId, nameLabel) {
|
||||
const img = getQuizWrongGhostImg();
|
||||
if (!img || !img.complete || !img.naturalWidth) return;
|
||||
/* ผีครอบทับ avatar ทั้งตัว — กว้าง ~2.4 tile, สูงตามสัดส่วน, ลอยนิดๆ */
|
||||
const tile = (typeof tileSize === 'number' && tileSize > 0) ? tileSize : 32;
|
||||
const wPx = tile * 2.4;
|
||||
/* ผีขนาดเท่าตัวละครเดิม: avatar ปกติ ~1 tile กว้าง, ~1.6 tile สูง → ผีกว้าง ~1.4, สูงตามสัดส่วน */
|
||||
const wPx = tile * 1.4;
|
||||
const ratio = img.naturalHeight / Math.max(1, img.naturalWidth);
|
||||
const hPx = wPx * ratio;
|
||||
const tBob = Math.sin(performance.now() / 320) * (tile * 0.05);
|
||||
/* ทับตรงกลางตัว avatar — avatar กว้าง 1 tile, สูง ~1.6 tile (วาดจากมุมบนซ้าย) */
|
||||
const avatarCenterY = ay + tile * 0.55;
|
||||
const sid = String(entId == null ? '' : entId);
|
||||
const tBob = Math.sin(performance.now() / 360 + (sid ? sid.charCodeAt(0) || 0 : 0)) * (tile * 0.06);
|
||||
/* วาดให้ "เท้าผี" ตรงกับเท้า avatar (avatar สูง 1.6 tile จากจุดบนซ้าย ay) */
|
||||
const groundY = ay + tile * 1.6;
|
||||
const drawX = ax + (tile - wPx) / 2;
|
||||
const drawY = avatarCenterY - hPx / 2 + tBob;
|
||||
const drawY = groundY - hPx + tBob;
|
||||
ctx.save();
|
||||
ctx.globalAlpha = 0.85;
|
||||
ctx.globalAlpha = 0.98;
|
||||
ctx.drawImage(img, drawX, drawY, wPx, hPx);
|
||||
ctx.restore();
|
||||
/* ชื่อใต้ผี (ถ้ามี — เพื่อระบุว่าเป็นใคร) */
|
||||
if (nameLabel) {
|
||||
ctx.save();
|
||||
ctx.font = '600 12px system-ui, "Segoe UI", "Kanit", sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.95)';
|
||||
ctx.strokeStyle = 'rgba(0,0,0,0.85)';
|
||||
ctx.lineWidth = 3;
|
||||
const nx = ax + tile / 2;
|
||||
const ny = groundY + 14;
|
||||
ctx.strokeText(nameLabel, nx, ny);
|
||||
ctx.fillText(nameLabel, nx, ny);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
/** เกมถูก/ผิด — ตรงกับ server QUIZ_TF_POINTS_PER_CORRECT */
|
||||
const QUIZ_TF_POINTS_PER_CORRECT = 10;
|
||||
@@ -18471,22 +18489,32 @@
|
||||
if (shouldGauntletCrownHeistSkipAvatarDrawPlay(o, false, off.ax, off.ay)) return;
|
||||
const axO = safeX(o.x) + off.ax;
|
||||
const ayO = safeY(o.y) + off.ay;
|
||||
if (playQuizAvatarHideWhileOverlappingAnswerZone(o, id, axO, ayO)) return;
|
||||
/* ผีต้องโชว์เสมอ แม้ทับโซนคำตอบ (เดินไปไหนก็ได้) */
|
||||
const ghostActiveO = isQuizWrongGhostActiveForEnt(id);
|
||||
if (!ghostActiveO && playQuizAvatarHideWhileOverlappingAnswerZone(o, id, axO, ayO)) return;
|
||||
if (botOut) ctx.save();
|
||||
if (botOut) ctx.globalAlpha = 0.4;
|
||||
drawAvatar(axO, ayO, false, peerName, o.characterId, faceDirOther, otherWalk, ot, (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks) || 0, o.gauntletScore || 0, quizCarrySignForEntity(o), isGauntletCrownHeistMapPlay() ? (o.gauntletCrownPenaltyFxUntil || 0) : 0);
|
||||
drawQuizWrongGhostOverlayPlay(axO, ayO, id);
|
||||
/* ตอบผิดในแมป mng8a80o → แทน avatar ด้วยผีเลย */
|
||||
if (ghostActiveO) {
|
||||
drawQuizWrongGhostOverlayPlay(axO, ayO, id, peerName);
|
||||
} else {
|
||||
drawAvatar(axO, ayO, false, peerName, o.characterId, faceDirOther, otherWalk, ot, (o.gauntletJumpVis != null ? o.gauntletJumpVis : o.gauntletJumpTicks) || 0, o.gauntletScore || 0, quizCarrySignForEntity(o), isGauntletCrownHeistMapPlay() ? (o.gauntletCrownPenaltyFxUntil || 0) : 0);
|
||||
}
|
||||
if (botOut) ctx.restore();
|
||||
} else {
|
||||
if (shouldGauntletCrownHeistSkipAvatarDrawPlay(me, true, 0, 0)) return;
|
||||
const axMe = safeX(me.x);
|
||||
const ayMe = safeY(me.y);
|
||||
if (playQuizAvatarHideWhileOverlappingAnswerZone(me, myId, axMe, ayMe)) return;
|
||||
const ghostActiveMe = isQuizWrongGhostActiveForEnt(myId);
|
||||
if (!ghostActiveMe && playQuizAvatarHideWhileOverlappingAnswerZone(me, myId, axMe, ayMe)) return;
|
||||
if (isJumpSurvive() && jumpSurviveEliminated) ctx.save();
|
||||
if (isJumpSurvive() && jumpSurviveEliminated) ctx.globalAlpha = 0.4;
|
||||
const faceDirMe = isGauntletFaceRightMapMno9kb07() ? 'right' : me.direction;
|
||||
drawAvatar(axMe, ayMe, true, me.nickname + meTag, me.characterId, faceDirMe, meWalking, mt, meGauntletJumpVis, me.gauntletScore || 0, quizCarrySignForEntity(me), isGauntletCrownHeistMapPlay() ? (me.gauntletCrownPenaltyFxUntil || 0) : 0);
|
||||
drawQuizWrongGhostOverlayPlay(axMe, ayMe, myId);
|
||||
if (ghostActiveMe) {
|
||||
drawQuizWrongGhostOverlayPlay(axMe, ayMe, myId, me.nickname + meTag);
|
||||
} else {
|
||||
drawAvatar(axMe, ayMe, true, me.nickname + meTag, me.characterId, faceDirMe, meWalking, mt, meGauntletJumpVis, me.gauntletScore || 0, quizCarrySignForEntity(me), isGauntletCrownHeistMapPlay() ? (me.gauntletCrownPenaltyFxUntil || 0) : 0);
|
||||
}
|
||||
if (isJumpSurvive() && jumpSurviveEliminated) ctx.restore();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2750,7 +2750,22 @@
|
||||
socket.on('peer-display-name', applyPeerDisplayNameUpdate);
|
||||
|
||||
socket.on('connect', () => {
|
||||
socket.emit('join-space', { spaceId, nickname: getProfileDisplayName(), characterId: getStoredCharacterId() }, (res) => {
|
||||
/* ส่งค่าสีที่เลือกใน Main-Lobby ติดไปด้วย — server จะใช้ค่านี้ถ้ายังว่าง (กันโชว์สีแดง default ตอนเข้าใหม่) */
|
||||
var savedThemeIdx = null;
|
||||
var savedSkinIdx = null;
|
||||
try {
|
||||
var t = parseInt(localStorage.getItem('lobbyThemeColor'), 10);
|
||||
var s = parseInt(localStorage.getItem('lobbySkinTone'), 10);
|
||||
if (t >= 1 && t <= 8) savedThemeIdx = t;
|
||||
if (s >= 1 && s <= 3) savedSkinIdx = s;
|
||||
} catch (e) { /* ignore */ }
|
||||
socket.emit('join-space', {
|
||||
spaceId,
|
||||
nickname: getProfileDisplayName(),
|
||||
characterId: getStoredCharacterId(),
|
||||
desiredLobbyColorThemeIndex: savedThemeIdx,
|
||||
desiredLobbySkinToneIndex: savedSkinIdx,
|
||||
}, (res) => {
|
||||
if (!res || !res.ok) {
|
||||
var joinErr = (res && res.error) || 'เข้าไม่ได้';
|
||||
if (/เริ่มคดี|ไม่รับผู้เล่น/.test(joinErr)) {
|
||||
|
||||
@@ -3214,7 +3214,7 @@
|
||||
<script src="/app-base.js?v=2"></script>
|
||||
<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.0469"></script>
|
||||
<script src="js/play.js?v=0.0472"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1576,7 +1576,7 @@
|
||||
<script src="js/display-name.js?v=2"></script>
|
||||
<script src="js/version.js?v=0.0122"></script>
|
||||
<script src="js/customize-popup.js?v=31" data-customize-triggers="" data-customize-asset-base="img/03-5-Customize"></script>
|
||||
<script src="js/room-lobby.js?v=0.0247"></script>
|
||||
<script src="js/room-lobby.js?v=0.0248"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-3
@@ -4639,7 +4639,7 @@ function spaceAllowsQuizCarryLobbyRelaxed(space) {
|
||||
}
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
socket.on('join-space', ({ spaceId, nickname, characterId, playMapId }, cb) => {
|
||||
socket.on('join-space', ({ spaceId, nickname, characterId, playMapId, desiredLobbyColorThemeIndex, desiredLobbySkinToneIndex }, cb) => {
|
||||
const space = spaces.get(spaceId);
|
||||
if (!space || !space.mapData) return cb && cb({ ok: false, error: 'ไม่พบห้อง' });
|
||||
const maxPlayers = space.maxPlayers || 10;
|
||||
@@ -4674,11 +4674,20 @@ io.on('connection', (socket) => {
|
||||
const spawnJoinOrder = space.peers.size;
|
||||
const spawnPt = pickSpawnForJoin(mdJoin, spawnJoinOrder);
|
||||
const bbStartBalloons = Math.max(1, Math.min(12, Math.floor(Number(mdJoin.balloonBossBalloonsPerPlayer)) || 3));
|
||||
/* ใช้สีที่ client เลือกไว้ใน Main-Lobby ถ้าผ่านการตรวจสอบ + ยังไม่ถูกใช้ในห้องนี้ */
|
||||
let chosenThemeIdx = parseInt(desiredLobbyColorThemeIndex, 10);
|
||||
if (!(chosenThemeIdx >= 1 && chosenThemeIdx <= LOBBY_THEME_COUNT) || getTakenLobbyThemeIndices(space).has(chosenThemeIdx)) {
|
||||
chosenThemeIdx = pickFreeLobbyThemeIndex(space);
|
||||
}
|
||||
let chosenSkinIdx = parseInt(desiredLobbySkinToneIndex, 10);
|
||||
if (!(chosenSkinIdx >= 1 && chosenSkinIdx <= LOBBY_SKIN_COUNT)) {
|
||||
chosenSkinIdx = pickLobbySkinIndexForSlot(spawnJoinOrder);
|
||||
}
|
||||
const peer = {
|
||||
id: socket.id, x: +spawnPt.x, y: +spawnPt.y, direction: 'down', nickname: nickname || 'ผู้เล่น', ready: false, characterId: characterId || null, voiceMicOn: true,
|
||||
spawnJoinOrder,
|
||||
lobbyColorThemeIndex: pickFreeLobbyThemeIndex(space),
|
||||
lobbySkinToneIndex: pickLobbySkinIndexForSlot(spawnJoinOrder),
|
||||
lobbyColorThemeIndex: chosenThemeIdx,
|
||||
lobbySkinToneIndex: chosenSkinIdx,
|
||||
gauntletJumpTicks: 0, gauntletScore: 0, gauntletJumpPending: false, gauntletEliminated: false, spaceShooterScore: 0,
|
||||
balloonBossScore: 0, balloonBossBossDmg: 0, balloonBossBalloons: mdJoin.gameType === 'balloon_boss' ? bbStartBalloons : 5, balloonBossEliminated: false,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user