update Endflow
This commit is contained in:
@@ -19,15 +19,44 @@ if ($limit > 200) $limit = 200;
|
||||
|
||||
$caseId = preg_replace('/[^0-9]/', '', (string) ($_GET['caseId'] ?? ''));
|
||||
|
||||
/* ชื่อคดี (caseId -> name) จาก Game/data/case-media.json */
|
||||
function lb_case_names(): array
|
||||
{
|
||||
$path = __DIR__ . '/../../Game/data/case-media.json';
|
||||
if (!is_file($path)) return [];
|
||||
$j = json_decode((string) @file_get_contents($path), true);
|
||||
$cases = (is_array($j) && isset($j['cases']) && is_array($j['cases'])) ? $j['cases'] : [];
|
||||
$out = [];
|
||||
foreach ($cases as $cid => $c) {
|
||||
$nm = is_array($c) ? trim((string) ($c['name'] ?? '')) : '';
|
||||
if ($nm !== '') $out[(string) $cid] = $nm;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
$caseNames = lb_case_names();
|
||||
|
||||
$store = read_store();
|
||||
$accounts = (isset($store['accounts']) && is_array($store['accounts'])) ? $store['accounts'] : [];
|
||||
|
||||
$rows = [];
|
||||
foreach ($accounts as $a) {
|
||||
$bestCaseId = '';
|
||||
if ($caseId !== '') {
|
||||
/* เจาะจงคดี */
|
||||
$score = max(0, (int) (($a['scoreByCase'][$caseId] ?? 0)));
|
||||
$bestCaseId = $caseId;
|
||||
} else {
|
||||
$score = max(0, (int) ($a['score'] ?? 0));
|
||||
/* กระดานรวม — คะแนนสูงสุดของ user จากคดีไหนก็ได้ */
|
||||
$score = 0;
|
||||
$sbc = (isset($a['scoreByCase']) && is_array($a['scoreByCase'])) ? $a['scoreByCase'] : [];
|
||||
foreach ($sbc as $cid => $sc) {
|
||||
$sc = max(0, (int) $sc);
|
||||
if ($sc > $score) { $score = $sc; $bestCaseId = (string) $cid; }
|
||||
}
|
||||
if ($score <= 0) { /* legacy: ไม่มีคะแนนรายคดี ใช้คะแนนรวม (ไม่มีชื่อคดี) */
|
||||
$score = max(0, (int) ($a['score'] ?? 0));
|
||||
$bestCaseId = '';
|
||||
}
|
||||
}
|
||||
if ($score <= 0) {
|
||||
continue;
|
||||
@@ -39,6 +68,8 @@ foreach ($accounts as $a) {
|
||||
'name' => $name,
|
||||
'score' => $score,
|
||||
'coins' => max(0, (int) ($a['coins'] ?? 0)),
|
||||
'caseId' => $bestCaseId,
|
||||
'caseName' => ($bestCaseId !== '' && isset($caseNames[$bestCaseId])) ? $caseNames[$bestCaseId] : '',
|
||||
'key' => (string) ($a['providerUserId'] ?? ''),
|
||||
];
|
||||
}
|
||||
@@ -57,7 +88,7 @@ $myKey = trim((string) ($_GET['playerKey'] ?? ''));
|
||||
if ($myKey !== '') {
|
||||
for ($i = 0; $i < $total; $i++) {
|
||||
if ($rows[$i]['key'] === $myKey) {
|
||||
$me = ['rank' => $i + 1, 'name' => $rows[$i]['name'], 'score' => $rows[$i]['score'], 'coins' => $rows[$i]['coins']];
|
||||
$me = ['rank' => $i + 1, 'name' => $rows[$i]['name'], 'score' => $rows[$i]['score'], 'coins' => $rows[$i]['coins'], 'caseId' => $rows[$i]['caseId'], 'caseName' => $rows[$i]['caseName']];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +102,8 @@ for ($i = 0; $i < $n; $i++) {
|
||||
'name' => $rows[$i]['name'],
|
||||
'score' => $rows[$i]['score'],
|
||||
'coins' => $rows[$i]['coins'],
|
||||
'caseId' => $rows[$i]['caseId'],
|
||||
'caseName' => $rows[$i]['caseName'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
"providerUserId": "p_1775109142385_wq7wfy1p32j",
|
||||
"notes": "auto: player-coins",
|
||||
"blocked": false,
|
||||
"coins": 545,
|
||||
"coins": 565,
|
||||
"createdAt": "2026-04-02T05:52:21+00:00",
|
||||
"updatedAt": "2026-06-17T07:52:36+00:00",
|
||||
"updatedAt": "2026-06-17T08:31:26+00:00",
|
||||
"daily": {
|
||||
"anchorMs": 1781197200000,
|
||||
"claimedDays": [
|
||||
@@ -54,7 +54,7 @@
|
||||
],
|
||||
"lockUntilMs": 1781715600000
|
||||
},
|
||||
"score": 470,
|
||||
"score": 490,
|
||||
"scoreByCase": {
|
||||
"1": 10,
|
||||
"10": 120,
|
||||
@@ -63,11 +63,14 @@
|
||||
"13": 20,
|
||||
"14": 20,
|
||||
"9": 40,
|
||||
"12": 40,
|
||||
"12": 60,
|
||||
"15": 20,
|
||||
"4": 10
|
||||
},
|
||||
"lbName": "Q"
|
||||
"lbName": "Q",
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "1d64c56fadb64a93eae68a1d",
|
||||
@@ -173,6 +176,21 @@
|
||||
"coins": 0,
|
||||
"createdAt": "2026-06-16T05:42:47+00:00",
|
||||
"updatedAt": "2026-06-16T05:42:47+00:00"
|
||||
},
|
||||
{
|
||||
"id": "0b016b9f790d0b90d99d7ba6",
|
||||
"email": "",
|
||||
"displayName": "Guest",
|
||||
"loginType": "guest",
|
||||
"providerUserId": "achvtest_12345678",
|
||||
"notes": "auto: achievements",
|
||||
"blocked": false,
|
||||
"coins": 0,
|
||||
"achievements": {
|
||||
"d1_minigame_solver": 3
|
||||
},
|
||||
"createdAt": "2026-06-17T08:22:51+00:00",
|
||||
"updatedAt": "2026-06-17T08:22:51+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1760,6 +1760,7 @@ function computeRunGradeForEvidence(space) {
|
||||
|
||||
/* ===== แจกเหรียญตามอันดับ + คะแนนสะสม (high score) แบบ server-authoritative ===== */
|
||||
const GAME_AWARD_URL = process.env.GAME_AWARD_URL || 'https://srv1361159.hstgr.cloud/Admin/api/game-award.php';
|
||||
const ACHIEVEMENTS_URL = process.env.ACHIEVEMENTS_URL || GAME_AWARD_URL.replace('game-award.php', 'achievements.php');
|
||||
const GAME_AWARD_SECRET_PATH = path.join(__dirname, '..', 'Admin', 'private', 'game-award-secret.txt');
|
||||
let __gameAwardSecret = null;
|
||||
function gameAwardSecret() {
|
||||
@@ -1807,6 +1808,25 @@ function submitGameAwards(awards) {
|
||||
} catch (e) { console.error('[game-award] throw', e && e.message); }
|
||||
}
|
||||
|
||||
/* บวก progress achievement (server-authoritative ผ่าน achievements.php — secret เดียวกับ game-award)
|
||||
items: [{ playerKey, id, inc }] — หนึ่ง POST ต่อรายการ (achievements.php รับทีละ id) */
|
||||
function submitAchievementProgress(items) {
|
||||
const secret = gameAwardSecret();
|
||||
if (!secret || !Array.isArray(items) || !items.length) return;
|
||||
items.forEach((it) => {
|
||||
if (!it || !it.playerKey || !it.id) return;
|
||||
try {
|
||||
fetch(ACHIEVEMENTS_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action: 'progress', secret, playerKey: it.playerKey, id: it.id, inc: it.inc || 1 }),
|
||||
}).then((r) => r.json()).then((d) => {
|
||||
if (!d || !d.ok) console.error('[achv] failed', it.id, d);
|
||||
}).catch((e) => console.error('[achv] error', e && e.message));
|
||||
} catch (e) { console.error('[achv] throw', e && e.message); }
|
||||
});
|
||||
}
|
||||
|
||||
/* แจกเหรียญตามอันดับคะแนน (เฉพาะผู้รอดชีวิต ผู้ตาย = 0) + คะแนนสะสมเข้า leaderboard — ครั้งเดียวต่อรอบ */
|
||||
function awardMinigameRankCoins(sid, space) {
|
||||
if (!space || space.coinAwardDoneForRun) return;
|
||||
@@ -1829,6 +1849,9 @@ function awardMinigameRankCoins(sid, space) {
|
||||
const awards = rows.filter((r) => r.coins > 0 && r.playerKey).map((r) => ({ playerKey: r.playerKey, nickname: r.nickname, coins: r.coins, caseId: caseId }));
|
||||
submitGameAwards(awards);
|
||||
|
||||
/* Achievement: d1 Minigame Solver — รอดมินิเกมสำเร็จ (เฉพาะผู้รอดชีวิตที่เป็นผู้เล่นจริง) */
|
||||
submitAchievementProgress(survivors.filter((r) => r.playerKey).map((r) => ({ playerKey: r.playerKey, id: 'd1_minigame_solver', inc: 1 })));
|
||||
|
||||
io.to(sid).emit('minigame-coins', {
|
||||
gameType,
|
||||
coinsTable: MINIGAME_RANK_COINS,
|
||||
@@ -3636,6 +3659,34 @@ function computeAndEmitTrialResult(sid, space) {
|
||||
disruptorId,
|
||||
hasDisruptor: !!disruptorId,
|
||||
});
|
||||
|
||||
/* ===== Achievement auto-track ===== */
|
||||
try {
|
||||
const achv = [];
|
||||
/* a1 First Deduction (target 1) + a5 Truth Hunter (target 20) — ผู้โหวตถูก (ผู้เล่นจริงเท่านั้น) */
|
||||
winners.forEach((id) => {
|
||||
const p = space.peers.get(id);
|
||||
if (p && p.playerKey) {
|
||||
achv.push({ playerKey: p.playerKey, id: 'a1_first_deduction', inc: 1 });
|
||||
achv.push({ playerKey: p.playerKey, id: 'a5_truth_hunter', inc: 1 });
|
||||
}
|
||||
});
|
||||
/* ตัวป่วน — นับเฉพาะผู้เล่นจริง (มี playerKey) */
|
||||
if (disruptorId && space.disruptorPlayerKey) {
|
||||
const dKey = space.disruptorPlayerKey;
|
||||
achv.push({ playerKey: dKey, id: 'e2_the_impostor', inc: 1 }); /* target 1 */
|
||||
achv.push({ playerKey: dKey, id: 'e4_agent_of_chaos', inc: 1 }); /* target 10 */
|
||||
/* ตัวป่วนชนะ = ทีมโหวตผิดคน (ไม่จับคนร้ายตัวจริง) */
|
||||
let mv = 0; for (let k = 1; k <= 2; k++) { if (counts[k] > counts[mv]) mv = k; }
|
||||
const anyV = (counts[0] + counts[1] + counts[2]) > 0;
|
||||
if (anyV && mv !== culprit) {
|
||||
achv.push({ playerKey: dKey, id: 'e3_master_of_doubt', inc: 1 }); /* target 1 */
|
||||
achv.push({ playerKey: dKey, id: 'e5_slippery_eel', inc: 1 }); /* target 5 */
|
||||
}
|
||||
}
|
||||
submitAchievementProgress(achv);
|
||||
} catch (e) { console.error('[achv] trial-result', e && e.message); }
|
||||
|
||||
space.silencedPlayerId = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,6 @@
|
||||
<script src="../Game/js/customize-popup.js?v=31" data-customize-triggers="#btn-cloth" data-customize-asset-base="/Game/img/03-5-Customize"></script>
|
||||
<script src="../Game/js/achievements.js?v=0.002" data-asset-base="/Game/img/03-6-Profile"></script>
|
||||
<script src="../Game/js/profile-popup.js?v=7" data-profile-trigger="#btn-myprofile" data-profile-asset-base="/Game/img/03-6-Profile"></script>
|
||||
<script src="lobby.js?v=0.0191"></script>
|
||||
<script src="lobby.js?v=0.0192"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -268,14 +268,15 @@
|
||||
var rank = parseInt(r.rank, 10) || (i + 1);
|
||||
var name = lbEscapeHtml(r.name || 'ผู้เล่น');
|
||||
var score = Math.max(0, parseInt(r.score, 10) || 0);
|
||||
var coins = Math.max(0, parseInt(r.coins, 10) || 0);
|
||||
var caseName = lbEscapeHtml((r.caseName || '').toString());
|
||||
var caseLabel = caseName ? ('(คดี : ' + caseName + ')') : '';
|
||||
var head = rank <= 3
|
||||
? '<img src="IMAGE/leaderboard-' + rank + '.png" alt="" class="lobby-rank-icon" role="presentation" decoding="async">'
|
||||
: '<span class="lobby-rank-num">' + rank + '</span>';
|
||||
html += '<li>' + head +
|
||||
'<div class="lobby-rank-info">' +
|
||||
'<span class="lobby-rank-name">' + name + '</span>' +
|
||||
'<span class="lobby-rank-case">(เหรียญ : ' + coins + ')</span>' +
|
||||
'<span class="lobby-rank-case">' + caseLabel + '</span>' +
|
||||
'</div>' +
|
||||
'<span class="lobby-rank-score">' + score + '</span>' +
|
||||
'</li>';
|
||||
|
||||
Reference in New Issue
Block a user