submit({ character, borrowedFrom, pts, k, d, atk, def, heal, slot, submittedByOfficer }: { character: Character; borrowedFrom?: UserKey; pts: number; k?: number; d?: number; atk?: number; def?: number; heal?: number; slot: SlotHour; submittedByOfficer?: boolean; }): void { const date = new Date().toISOString().slice(0, 10); const historyKey = `${date}-${slot}` as HistoryKey; const history = loadHistory(historyKey); // Snapshot W.Rank before recording score const existingEntry = WRank.entry(character.name, character.nation); const wRankAtSubmission = existingEntry ? { rank: existingEntry.currentRank, delta: existingEntry.currentRank - (existingEntry.previousRank ?? existingEntry.currentRank), } : undefined; const score: TGScore = { userKey: character.ownerKey, playedBy: borrowedFrom, characterName: character.name, class: character.class.key, nation: character.nation, pts, k, d, atk, def, heal, submittedAt: new Date().toISOString(), slot, date, submittedByOfficer: submittedByOfficer ?? false, wRankAtSubmission, }; // Upsert — replace existing score for same character/slot history.scores = history.scores.filter( (s) => !(s.userKey === character.ownerKey && s.characterName === character.name && s.slot === slot && s.date === date) ); history.scores.push(score); saveHistory(historyKey, history); // Record in W.Rank WRank.recordScore( character.ownerKey, character.name, character.class.key, character.nation, pts, historyKey ); },