update card
This commit is contained in:
@@ -38,9 +38,9 @@
|
||||
"providerUserId": "p_1775109142385_wq7wfy1p32j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 110,
|
||||
"coins": 180,
|
||||
"createdAt": "2026-04-02T05:52:21+00:00",
|
||||
"updatedAt": "2026-06-12T08:29:33+00:00",
|
||||
"updatedAt": "2026-06-14T14:25:05+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781197200000,
|
||||
"claimedDays": [
|
||||
@@ -52,13 +52,13 @@
|
||||
false,
|
||||
false
|
||||
],
|
||||
"lockUntilMs": 1781283600000
|
||||
"lockUntilMs": 0
|
||||
},
|
||||
"score": 100,
|
||||
"score": 170,
|
||||
"scoreByCase": {
|
||||
"1": 10,
|
||||
"10": 70,
|
||||
"8": 20
|
||||
"8": 90
|
||||
},
|
||||
"lbName": "Q"
|
||||
},
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -36,6 +36,8 @@
|
||||
const previewMode = params.get('preview') === '1';
|
||||
/* Card 3 Ban — ผู้ถูกแบนรอบนี้: เข้ามาดูเพื่อนเล่นได้ แต่ควบคุมตัวละคร/ทำแอ็กชันไม่ได้ (รอบหน้าเล่นปกติ) */
|
||||
const playBannedSpectator = params.get('banned') === '1';
|
||||
/* Card 3 Ban (กรณีโหวตแบน "บอท") — slot ของ __pv_bot_N ที่ถูกแบน → ไม่ spawn ลงเล่นรอบนี้ (-1 = ไม่มี) */
|
||||
const playBannedBotSlot = (() => { const v = parseInt(params.get('banBot'), 10); return Number.isFinite(v) && v >= 0 ? v : -1; })();
|
||||
/* shield โปร่งใสคลุมทั้งจอ (z 200) กิน pointer/touch ทุกอย่าง → ปุ่มเกม(jump z59/drop z60/joystick/shoot)+canvas กดไม่ได้
|
||||
แต่ต่ำกว่า modal (z 10000+) เลย special quiz / สรุปผล ยังกดได้ปกติ + ป้ายบอกสถานะด้านบน */
|
||||
function setupBannedSpectatorUi() {
|
||||
@@ -5003,7 +5005,12 @@
|
||||
function rebalancePreviewBots() {
|
||||
if (!playBotsEnabled() || !mapData) return;
|
||||
const human = countPlayHumans();
|
||||
const wantBots = Math.max(0, playBotTargetHeadcount() - human);
|
||||
let wantBots = Math.max(0, playBotTargetHeadcount() - human);
|
||||
/* Card 3 Ban — บอทที่ถูกแบนรอบนี้ (slot N) ไม่ลงเล่น: ลบทิ้ง + ลดเป้าจำนวนลง 1 + ข้าม slot นั้นตอนสร้าง */
|
||||
if (playBannedBotSlot >= 0) {
|
||||
others.delete(PREVIEW_BOT_PREFIX + playBannedBotSlot);
|
||||
wantBots = Math.max(0, wantBots - 1);
|
||||
}
|
||||
[...others.keys()].filter(isPreviewBotId).forEach((bid) => {
|
||||
const o = others.get(bid);
|
||||
if (!o) return;
|
||||
@@ -5043,7 +5050,7 @@
|
||||
while (botIds.length < wantBots) {
|
||||
/* หา slot ว่างเล็กสุด (0,1,2,…) — IDX ของ ID ต้องตรงกับ playLobbyBotThemes[idx] เพื่อให้สีตรง lobby */
|
||||
let nextSlot = 0;
|
||||
while (others.has(PREVIEW_BOT_PREFIX + nextSlot)) nextSlot++;
|
||||
while (others.has(PREVIEW_BOT_PREFIX + nextSlot) || nextSlot === playBannedBotSlot) nextSlot++;
|
||||
previewBotSeq = Math.max(previewBotSeq, nextSlot + 1);
|
||||
const id = PREVIEW_BOT_PREFIX + nextSlot;
|
||||
const joinIdx = countPlayHumans() + botIds.length;
|
||||
@@ -7454,6 +7461,8 @@
|
||||
const top = rows.slice(0, 5);
|
||||
const ranked = top.map(function (row, idx) {
|
||||
const pos = idx + 1;
|
||||
/* โบนัสอยู่รอดจนจบเกม = +20 (เฉพาะคนไม่ตาย) — รวมใน finalScore เหมือน rankBonus ของ MG2 */
|
||||
const surviveBonus = row.eliminated ? 0 : 20;
|
||||
return {
|
||||
id: row.id,
|
||||
nickname: row.nickname,
|
||||
@@ -7462,11 +7471,11 @@
|
||||
eliminated: !!row.eliminated,
|
||||
rank: pos,
|
||||
rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos),
|
||||
rankBonus: 0,
|
||||
finalScore: row.baseScore,
|
||||
rankBonus: surviveBonus,
|
||||
finalScore: row.baseScore + surviveBonus,
|
||||
};
|
||||
});
|
||||
const totalParts = ranked.map(function (r) { return r.baseScore; });
|
||||
const totalParts = ranked.map(function (r) { return r.finalScore; });
|
||||
const totalSum = totalParts.reduce(function (s, n) { return s + n; }, 0);
|
||||
const n = ranked.length || 1;
|
||||
const averageScore = Math.floor(totalSum / n);
|
||||
@@ -7597,9 +7606,13 @@
|
||||
return String(a.nickname).localeCompare(String(b.nickname), 'th') || String(a.id).localeCompare(String(b.id));
|
||||
});
|
||||
const top = baseRows.slice(0, 5);
|
||||
/* โบนัสจบเกม: ฆ่าบอส(+100)→ทุกคนที่ร่วมเล่น · รอดชีวิต(+10)→เฉพาะคนไม่ตาย (รวมใน finalScore, clamp ≥0) */
|
||||
const endRBonus = mission && mission.balloonBossEndReason;
|
||||
const killBonusAll = endRBonus === 'victory' ? 100 : 0;
|
||||
const ranked = top.map(function (row, idx) {
|
||||
const pos = idx + 1;
|
||||
const bs = Math.max(0, Number(row.baseScore) || 0);
|
||||
const bonus = killBonusAll + (row.eliminated ? 0 : 10);
|
||||
return {
|
||||
id: row.id,
|
||||
nickname: row.nickname,
|
||||
@@ -7608,11 +7621,11 @@
|
||||
eliminated: !!row.eliminated,
|
||||
rank: pos,
|
||||
rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos),
|
||||
rankBonus: 0,
|
||||
finalScore: bs,
|
||||
rankBonus: bonus,
|
||||
finalScore: Math.max(0, bs + bonus),
|
||||
};
|
||||
});
|
||||
const totalParts = ranked.map(function (r) { return r.baseScore; });
|
||||
const totalParts = ranked.map(function (r) { return r.finalScore; });
|
||||
const totalSum = totalParts.reduce(function (s, n) { return s + n; }, 0);
|
||||
const n2 = ranked.length || 1;
|
||||
const averageScore = Math.floor(totalSum / n2);
|
||||
@@ -7668,9 +7681,12 @@
|
||||
|| String(a.nickname).localeCompare(String(b.nickname), 'th')
|
||||
|| String(a.id).localeCompare(String(b.id)));
|
||||
const top = rows.slice(0, 5);
|
||||
/* โบนัสจบเกม: ฆ่าบอส(+100)→ทุกคนที่ร่วมเล่น · รอดชีวิต(+10)→เฉพาะคนไม่ตาย (รวมใน finalScore, clamp ≥0) */
|
||||
const killBonusAll = reason === 'victory' ? 100 : 0;
|
||||
const ranked = top.map((row, idx) => {
|
||||
const pos = idx + 1;
|
||||
const bs = Math.max(0, Number(row.baseScore) || 0);
|
||||
const bonus = killBonusAll + (row.eliminated ? 0 : 10);
|
||||
return {
|
||||
id: row.id,
|
||||
nickname: row.nickname,
|
||||
@@ -7679,11 +7695,11 @@
|
||||
eliminated: !!row.eliminated,
|
||||
rank: pos,
|
||||
rankLabel: pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos),
|
||||
rankBonus: 0,
|
||||
finalScore: bs,
|
||||
rankBonus: bonus,
|
||||
finalScore: Math.max(0, bs + bonus),
|
||||
};
|
||||
});
|
||||
const totalParts = ranked.map((r) => r.baseScore);
|
||||
const totalParts = ranked.map((r) => r.finalScore);
|
||||
const totalSum = totalParts.reduce((s, n) => s + n, 0);
|
||||
const n2 = ranked.length || 1;
|
||||
const averageScore = Math.floor(totalSum / n2);
|
||||
@@ -15154,7 +15170,7 @@
|
||||
if (Number.isFinite(t) && t > 0 && t < 7200) return Math.floor(t);
|
||||
const g = Number(playSpaceShooterMissionTimeSec);
|
||||
if (Number.isFinite(g) && g > 0 && g < 7200) return Math.floor(g);
|
||||
return 90;
|
||||
return 60; /* สเปก MG6: เล่นภายใน 60 วินาที (default เมื่อ map/game-timing ไม่ได้ตั้งค่า) */
|
||||
}
|
||||
|
||||
function spaceShooterRemainingSecPlay() {
|
||||
@@ -16001,6 +16017,8 @@
|
||||
vy: bvy,
|
||||
ownerId: bid,
|
||||
});
|
||||
/* สเปก MG7: บอทยิง 1 ครั้ง = −5 คะแนน เหมือนผู้เล่น (ต่ำสุด 0) */
|
||||
o.balloonBossScore = Math.max(0, (o.balloonBossScore | 0) - 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -16275,6 +16293,8 @@
|
||||
vy,
|
||||
ownerId: myId,
|
||||
});
|
||||
/* สเปก MG7: ยิง 1 ครั้ง = −5 คะแนน (ต่ำสุด 0 ไม่ติดลบ) */
|
||||
me.balloonBossScore = Math.max(0, (me.balloonBossScore | 0) - 5);
|
||||
}
|
||||
|
||||
stepBalloonBossPreviewBots(dt, mw, mh, bossC.cx, bossC.cy, ts);
|
||||
|
||||
@@ -6521,10 +6521,17 @@
|
||||
if (lobbyLevelStr) q += '&lobbyLevel=' + encodeURIComponent(lobbyLevelStr);
|
||||
if (caseIdStr) q += '&case=' + encodeURIComponent(caseIdStr);
|
||||
if (data && data.detectiveMinigame) q += '&detectiveReturn=1';
|
||||
/* Card 3 Ban — ผู้ที่ถูกแบนรอบนี้: เข้าไปดูเพื่อนเล่นได้ แต่ควบคุมตัวละครไม่ได้ (รอบหน้าเล่นปกติ) */
|
||||
if (data && data.bannedPlayerId && data.bannedPlayerId === socket.id) {
|
||||
q += '&banned=1';
|
||||
appendLobbySystemChat('— คุณถูกแบนรอบนี้ (Ban Card) · เข้าไปดูเพื่อนเล่นได้ แต่เล่นเองไม่ได้ · รอบหน้าเล่นได้ปกติ');
|
||||
/* Card 3 Ban — ผู้ที่ถูกแบนรอบนี้: เข้าไปดูเพื่อนเล่นได้ แต่ควบคุมตัวละครไม่ได้ (รอบหน้าเล่นปกติ)
|
||||
ถ้าถูกแบนเป็น "บอท" (__lobby_bot_N) → ส่ง banBot=N ให้ play.js กันบอทตัวนั้นไม่ให้ลงเล่น (ทุก client ที่จำลองบอท) */
|
||||
if (data && data.bannedPlayerId) {
|
||||
if (data.bannedPlayerId === socket.id) {
|
||||
q += '&banned=1';
|
||||
appendLobbySystemChat('— คุณถูกแบนรอบนี้ (Ban Card) · เข้าไปดูเพื่อนเล่นได้ แต่เล่นเองไม่ได้ · รอบหน้าเล่นได้ปกติ');
|
||||
} else if (String(data.bannedPlayerId).indexOf('__lobby_bot_') === 0) {
|
||||
var banBotSlot = parseInt(String(data.bannedPlayerId).slice('__lobby_bot_'.length), 10);
|
||||
if (!isNaN(banBotSlot) && banBotSlot >= 0) q += '&banBot=' + banBotSlot;
|
||||
appendLobbySystemChat('— บอทถูกแบนรอบนี้ (Ban Card) · ไม่ได้ลงเล่นมินิเกมรอบนี้');
|
||||
}
|
||||
}
|
||||
location.href = q;
|
||||
});
|
||||
|
||||
@@ -5180,7 +5180,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.006131817"></script>
|
||||
<script src="js/play.js?v=0.006131820"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
<style id="qc-howto-mg2-fix">
|
||||
/* HOW TO PLAY ของ quiz_carry (Minigame-4) -> ให้เหมือน Minigame-2 (gch-mg2-mock)
|
||||
|
||||
@@ -1613,7 +1613,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.0277"></script>
|
||||
<script src="js/room-lobby.js?v=0.0278"></script>
|
||||
<div class="version-tag">v —</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+51
-5
@@ -594,6 +594,47 @@ function specialQuizTileInGameZone(md, x, y) {
|
||||
function pickSpecialQuizSpawnTile(md) {
|
||||
const w = md.width || 20;
|
||||
const h = md.height || 15;
|
||||
/* quiz_carry (แบกคำตอบ): โต๊ะ/เคาน์เตอร์ถูกวาดใน "ภาพพื้นหลัง" แต่ไม่ได้มาร์คใน blockPlayer →
|
||||
ช่องว่างกลางระหว่าง hub กับโต๊ะ interactive ดูเหมือนเดินได้แต่จริง ๆ มีโต๊ะบัง เก็บไอคอนไม่ได้
|
||||
→ เกิดไอคอน "ใกล้จุดที่ผู้เล่นเกิด/เดิน" + เว้น buffer 2 ช่องรอบ hub/interactive/option (กันโต๊ะที่วาดเลยกริด) */
|
||||
if (md.gameType === 'quiz_carry') {
|
||||
const nearMarkedTable = (x, y) => {
|
||||
for (const g of [md.quizCarryHubArea, md.interactive, md.quizCarryOptionArea]) {
|
||||
if (!Array.isArray(g)) continue;
|
||||
for (let dy = -2; dy <= 2; dy++) {
|
||||
const gy = g[y + dy];
|
||||
if (!gy) continue;
|
||||
for (let dx = -2; dx <= 2; dx++) {
|
||||
if (gy[x + dx]) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const seeds = (Array.isArray(md.lobbyPlayerSpawns) && md.lobbyPlayerSpawns.length)
|
||||
? md.lobbyPlayerSpawns
|
||||
: [md.spawn || { x: Math.floor(w / 2), y: Math.floor(h / 2) }];
|
||||
const qcCands = [];
|
||||
seeds.forEach((s) => {
|
||||
const bx = Math.floor(Number(s.x));
|
||||
const by = Math.floor(Number(s.y));
|
||||
for (let dy = -5; dy <= 5; dy++) {
|
||||
for (let dx = -5; dx <= 5; dx++) {
|
||||
const dist = Math.abs(dx) + Math.abs(dy);
|
||||
if (dist < 2 || dist > 6) continue;
|
||||
const xx = bx + dx;
|
||||
const yy = by + dy;
|
||||
if (specialQuizTileWalkable(md, xx, yy) && !specialQuizTileInGameZone(md, xx, yy) && !nearMarkedTable(xx, yy)) {
|
||||
qcCands.push({ x: xx, y: yy });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (qcCands.length) {
|
||||
const p = qcCands[Math.floor(Math.random() * qcCands.length)];
|
||||
return { x: p.x + 0.5, y: p.y + 0.5 };
|
||||
}
|
||||
}
|
||||
/* gauntlet (วิ่งหลบ): ผู้เล่นวิ่งบน "แถวเลน" เท่านั้น → ไอคอนต้องอยู่บนแถวเดียวกับ gauntletPlayerSpawns
|
||||
(ที่ x กลางทาง) ผู้เล่นวิ่งผ่านเก็บได้/คลิกได้ — ไม่งั้นลอยออกนอกเลนเก็บไม่ได้ */
|
||||
if (md.gameType === 'gauntlet' && Array.isArray(md.gauntletPlayerSpawns) && md.gauntletPlayerSpawns.length) {
|
||||
@@ -1638,10 +1679,8 @@ function computeRunGradeForEvidence(space) {
|
||||
try {
|
||||
const md = (space.mapId && maps.get(space.mapId)) || space.mapData;
|
||||
const gameType = md && md.gameType;
|
||||
if (gameType === 'gauntlet' || gameType === 'jump_survive') {
|
||||
return buildGauntletCrownMissionPayload(space).grade || 'C';
|
||||
}
|
||||
/* เกมจริงหรือไม่ (quiz TF): เฉลี่ย % ของคะแนนเทียบเต็ม (จำนวนข้อ × แต้มต่อข้อ) */
|
||||
/* ทุกเกม: เอาคะแนนของผู้เล่นทุกคนมาเฉลี่ยเป็น % แล้วแปลงเป็นเกรด A(≥80)/B(≥60)/C
|
||||
quiz ใช้ % สัมบูรณ์ (ถูก/เต็ม), เกมอื่น normalize เทียบคนคะแนนสูงสุดในห้องแล้วเฉลี่ย */
|
||||
if (gameType === 'quiz' && space.quizSession && space.quizSession.players) {
|
||||
const total = (space.quizSession.questions || []).length * QUIZ_TF_POINTS_PER_CORRECT;
|
||||
const ids = Object.keys(space.quizSession.players);
|
||||
@@ -3261,6 +3300,7 @@ function beginDetectiveSuspectMinigame(sid, space, cardEntry, selectedIndex) {
|
||||
space.peers.forEach((p) => { p.reportedMiniScore = 0; p.reportedMiniSurvived = true; });
|
||||
/* Card 3 Ban — ผู้ที่ถูกแบนไม่ได้เล่นมินิเกมรอบนี้ (1 รอบ) */
|
||||
space.bannedThisRunPlayerId = space.bannedPlayerId || null;
|
||||
console.log('[ban-debug] startMinigame: bannedThisRunPlayerId=', space.bannedThisRunPlayerId);
|
||||
space.bannedPlayerId = null;
|
||||
space.detectiveMinigameCardIndex = cardEntry.cardIndex;
|
||||
// จำว่ากำลังสืบผู้ต้องสงสัยคนไหน เพื่อเติมหลักฐาน (ติกถูก) ตอนเล่นจบ
|
||||
@@ -7256,16 +7296,22 @@ io.on('connection', (socket) => {
|
||||
/* Card 3 Ban: ผู้เล่นกลับเข้า LobbyB หลังเกมจบ + มีการ์ด Ban ค้าง → เริ่มโหวตแบนทันที
|
||||
(คน rejoin คนแรกเป็นคนเปิดโหวต; คนที่ rejoin ทีหลังขณะโหวตเปิดอยู่ → ส่งสถานะให้เห็นด้วย) */
|
||||
if (serverMapIsPostCaseLobbyB(space)) {
|
||||
console.log('[ban-debug] join-space rejoin: postLobbyB=true pendingBan=', !!space.pendingBanVoteCard, 'pickVote=', !!space.pickVote, 'by', socket.id);
|
||||
if (space.pendingBanVoteCard && !space.pickVote) {
|
||||
const banCard = space.pendingBanVoteCard;
|
||||
space.pendingBanVoteCard = null;
|
||||
/* หน่วงให้ room-lobby ฝั่ง client โหลด/เซ็ตอัป UI เสร็จก่อน แล้วค่อยเปิดโหวต (ไม่งั้น overlay โดน reset/ทับตอนหน้ายังไม่พร้อม) */
|
||||
setTimeout(() => {
|
||||
const sp = spaces.get(spaceId);
|
||||
if (!sp || sp.pickVote || !serverMapIsPostCaseLobbyB(sp)) return;
|
||||
if (!sp || sp.pickVote || !serverMapIsPostCaseLobbyB(sp)) {
|
||||
console.log('[ban-debug] setTimeout BAIL: sp=', !!sp, 'pickVote=', !!(sp && sp.pickVote), 'postLobbyB=', !!(sp && serverMapIsPostCaseLobbyB(sp)));
|
||||
return;
|
||||
}
|
||||
console.log('[ban-debug] OPEN ban vote (peers=', sp.peers.size, ')');
|
||||
io.to(spaceId).emit('special-card-applied', { card: specialCardClientPayload(banCard), context: 'ban' });
|
||||
startPlayerPickVote(spaceId, sp, 'ban', (targetId) => {
|
||||
const sp2 = spaces.get(spaceId);
|
||||
console.log('[ban-debug] ban vote RESOLVED target=', targetId);
|
||||
if (sp2) sp2.bannedPlayerId = targetId || null;
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
Reference in New Issue
Block a user