diff --git a/www/html/Game/public/js/play.js b/www/html/Game/public/js/play.js index 64dc4e7..3ef44c3 100644 --- a/www/html/Game/public/js/play.js +++ b/www/html/Game/public/js/play.js @@ -5382,6 +5382,106 @@ return g !== 'F'; } + function gauntletCrownRankBonusLocal(rankOrdinal) { + if (rankOrdinal === 1) return 100; + if (rankOrdinal === 2) return 80; + if (rankOrdinal === 3) return 60; + return 0; + } + + function gauntletCrownRollRewardCardLocal(grade) { + const r = Math.random(); + if (grade === 'A') { + if (r < 0.3) return { kind: 'culprit', th: 'การ์ดชี้คนร้าย', en: 'Culprit card' }; + if (r < 0.8) return { kind: 'rare', th: 'การ์ด Rare', en: 'Rare card' }; + return { kind: 'common', th: 'การ์ดธรรมดา', en: 'Common card' }; + } + if (grade === 'B') { + if (r < 0.2) return { kind: 'culprit', th: 'การ์ดชี้คนร้าย', en: 'Culprit card' }; + if (r < 0.5) return { kind: 'rare', th: 'การ์ด Rare', en: 'Rare card' }; + return { kind: 'common', th: 'การ์ดธรรมดา', en: 'Common card' }; + } + if (r < 0.2) return { kind: 'rare', th: 'การ์ด Rare', en: 'Rare card' }; + return { kind: 'common', th: 'การ์ดธรรมดา', en: 'Common card' }; + } + + /** + * Editor embed + preview bots: รวมแถวอันดับกับบอท (สูงสุด 5 ช่อง) และคำนวณรวม/เฉลี่ย/เกรดให้สอดคล้องกับแถวที่โชว์ + * — อันดับในช่องใช้ลำดับ 1..5 หลังเรียงคะแนน (เหมือนแนว quiz_carry mission row) + */ + function gauntletCrownBuildDisplayMission(mission) { + if (!mission || !Array.isArray(mission.ranked)) return mission; + if (!(previewFillBots && isGauntletCrownHeistMapPlay() && others && typeof others.forEach === 'function')) return mission; + + const seen = new Set(); + const baseRows = []; + mission.ranked.forEach((r) => { + if (!r) return; + const sid = String(r.id); + if (seen.has(sid)) return; + seen.add(sid); + baseRows.push({ + id: r.id, + nickname: (r.nickname && String(r.nickname).trim()) ? String(r.nickname).trim() : 'ผู้เล่น', + characterId: r.characterId ?? null, + baseScore: r.eliminated ? 0 : Math.max(0, Number(r.baseScore) || 0), + eliminated: !!r.eliminated, + }); + }); + others.forEach((o, id) => { + if (!o || !isPreviewBotId(id)) return; + const sid = String(id); + if (seen.has(sid)) return; + seen.add(sid); + baseRows.push({ + id: id, + nickname: (o.nickname && String(o.nickname).trim()) ? String(o.nickname).trim() : sid, + characterId: o.characterId ?? null, + baseScore: o.gauntletEliminated ? 0 : Math.max(0, Number(o.gauntletScore) | 0), + eliminated: !!o.gauntletEliminated, + }); + }); + + baseRows.sort((a, b) => { + if (b.baseScore !== a.baseScore) return b.baseScore - a.baseScore; + const nick = String(a.nickname).localeCompare(String(b.nickname), 'th'); + if (nick !== 0) return nick; + return String(a.id).localeCompare(String(b.id)); + }); + const top = baseRows.slice(0, 5); + const ranked = top.map((row, idx) => { + const pos = idx + 1; + const rankBonus = gauntletCrownRankBonusLocal(pos); + const rankLabel = pos === 1 ? '1st' : pos === 2 ? '2nd' : pos === 3 ? '3rd' : String(pos); + const finalScore = row.baseScore + rankBonus; + return { + id: row.id, + nickname: row.nickname, + characterId: row.characterId, + baseScore: row.baseScore, + eliminated: row.eliminated, + rank: pos, + rankLabel: rankLabel, + rankBonus: rankBonus, + finalScore: finalScore, + }; + }); + + const totalSum = ranked.reduce(function (s, r) { return s + r.finalScore; }, 0); + const n = ranked.length || 1; + const averageScore = Math.floor(totalSum / n); + const grade = averageScore >= 80 ? 'A' : averageScore >= 60 ? 'B' : 'C'; + const rewardCard = gauntletCrownRollRewardCardLocal(grade); + return { + ranked: ranked, + totalSum: totalSum, + averageScore: averageScore, + grade: grade, + rewardCard: rewardCard, + totalParts: ranked.map(function (r) { return r.finalScore; }), + }; + } + function showGauntletCrownMissionOverlay(mission) { const ov = document.getElementById('gauntlet-crown-mission-overlay'); const rowEl = document.getElementById('gcm-rank-row'); @@ -5390,8 +5490,10 @@ const bonusEl = document.getElementById('gcm-bonus'); const btn = document.getElementById('btn-gcm-done'); if (!ov || !rowEl || !mission || !Array.isArray(mission.ranked)) return; + const disp = gauntletCrownBuildDisplayMission(mission); + if (!disp || !Array.isArray(disp.ranked)) return; rowEl.innerHTML = ''; - mission.ranked.forEach((r) => { + disp.ranked.forEach((r) => { const cell = document.createElement('div'); cell.className = 'gcm-cell'; const tag = document.createElement('div'); @@ -5436,7 +5538,10 @@ nick.textContent = r.nickname || '—'; const sc = document.createElement('div'); sc.className = 'gcm-sc'; - sc.textContent = String(r.baseScore || 0) + '(+' + String(r.rankBonus || 0) + ')'; + const botScorePlain = previewFillBots && isGauntletCrownHeistMapPlay() && isPreviewBotId(r.id); + sc.textContent = botScorePlain + ? String(Math.max(0, Number(r.baseScore) || 0)) + : String(r.baseScore || 0) + '(+' + String(r.rankBonus || 0) + ')'; cell.appendChild(tag); cell.appendChild(frame); cell.appendChild(nick); @@ -5444,13 +5549,13 @@ rowEl.appendChild(cell); }); if (totalEl) { - const parts = (mission.totalParts || []).map((n) => String(Math.max(0, Number(n) || 0))); - const sum = Math.max(0, Number(mission.totalSum) || 0); - const avg = Math.max(0, Math.floor(Number(mission.averageScore) || 0)); + const parts = (disp.totalParts || []).map((n) => String(Math.max(0, Number(n) || 0))); + const sum = Math.max(0, Number(disp.totalSum) || 0); + const avg = Math.max(0, Math.floor(Number(disp.averageScore) || 0)); totalEl.innerHTML = 'คะแนนรวม (' + parts.join('+') + ') = ' + sum + ' · เฉลี่ย ' + avg + ' → เกรด'; } if (gradeEl) { - const g = String(mission.grade || 'C').toUpperCase().charAt(0); + const g = String(disp.grade || 'C').toUpperCase().charAt(0); gradeEl.textContent = g; gradeEl.className = 'gcm-grade gcm-grade--' + g.toLowerCase(); } @@ -5472,7 +5577,7 @@ row.appendChild(chk); row.appendChild(sp); bonusEl.appendChild(row); - const rc = mission.rewardCard; + const rc = disp.rewardCard; if (rc && rc.th) { const pl = document.createElement('div'); pl.className = 'gcm-plaque'; @@ -5489,7 +5594,7 @@ btn.onclick = function () { if (previewMode && editorEmbedReturn) { ov.classList.add('is-hidden'); - showQuizCarryTimeupOnDeskLayer(function () { return gauntletCrownEmbedMissionAnyOk(mission); }); + showQuizCarryTimeupOnDeskLayer(function () { return gauntletCrownEmbedMissionAnyOk(disp); }); } else { goLobby(); } diff --git a/www/html/Game/public/js/version.js b/www/html/Game/public/js/version.js index 0d40f06..2917ad9 100644 --- a/www/html/Game/public/js/version.js +++ b/www/html/Game/public/js/version.js @@ -1,6 +1,6 @@ // ทุกครั้งที่มีการเพิ่มหรือเปลี่ยน ให้เพิ่ม v +0.0001 // หลังแก้ค่าแล้วต้อง copy ไป path ที่ Nginx ชี้ (หรือรัน copy-frogger-files-only.sh) ถึงจะเห็นบนเว็บ -window.APP_VERSION = '0.0174'; +window.APP_VERSION = '0.0175'; document.addEventListener('DOMContentLoaded', function () { var t = document.querySelector('.version-tag'); if (t) t.textContent = 'v ' + window.APP_VERSION; diff --git a/www/html/Game/public/play.html b/www/html/Game/public/play.html index d538aba..2fb1d91 100644 --- a/www/html/Game/public/play.html +++ b/www/html/Game/public/play.html @@ -1858,8 +1858,8 @@ - - + +