mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 17:16:38 +00:00
6e892839e8
**Add approved & assigned issue number here:** Resolves #4349 ## Description: The infra related PR is linked to this one and would need to be pushed first (376) Two changes for organized/tournament matches: 1. **Final standings.** `setWinner` snapshots each player's tiles owned at game end into `PlayerStats` (`finalTiles`). It's a deterministic integer captured in the sim, so it's replay-safe and rides into the existing game record. This lets standings be derived directly (winner, then surviving players by territory, then eliminated players by when they died) without re-simulating, which matters because a domination win ends with many players still alive. 2. **Per-kill log**. Records, per player, which humans they eliminated and at what tick (kills on PlayerStats). This lets standings attribute each kill to the victim's final placement, and gives a deterministic kill graph for integrity review. Hooked once in conquerPlayer (the single elimination funnel), humans only. Additive optional field that rides the existing game record, no archive or wire changes. These are off by default with no effect on normal play. ## Please complete the following: - [x] I have added screenshots for all UI updates (no UI changes in this PR) - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file (no new user-facing text) - [x] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: zixer._
125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
import { z } from "zod";
|
|
import { UnitType } from "./game/Game";
|
|
|
|
export const bombUnits = ["abomb", "hbomb", "mirv", "mirvw"] as const;
|
|
export const BombUnitSchema = z.enum(bombUnits);
|
|
export type BombUnit = z.infer<typeof BombUnitSchema>;
|
|
export type NukeType =
|
|
| UnitType.AtomBomb
|
|
| UnitType.HydrogenBomb
|
|
| UnitType.MIRV
|
|
| UnitType.MIRVWarhead;
|
|
|
|
export const unitTypeToBombUnit = {
|
|
[UnitType.AtomBomb]: "abomb",
|
|
[UnitType.HydrogenBomb]: "hbomb",
|
|
[UnitType.MIRV]: "mirv",
|
|
[UnitType.MIRVWarhead]: "mirvw",
|
|
} as const satisfies Record<NukeType, BombUnit>;
|
|
|
|
export const boatUnits = ["trade", "trans"] as const;
|
|
export const BoatUnitSchema = z.enum(boatUnits);
|
|
export type BoatUnit = z.infer<typeof BoatUnitSchema>;
|
|
export type BoatUnitType = UnitType.TradeShip | UnitType.TransportShip;
|
|
|
|
// export const unitTypeToBoatUnit = {
|
|
// [UnitType.TradeShip]: "trade",
|
|
// [UnitType.TransportShip]: "trans",
|
|
// } as const satisfies Record<BoatUnitType, BoatUnit>;
|
|
|
|
export const otherUnits = [
|
|
"city",
|
|
"defp",
|
|
"port",
|
|
"wshp",
|
|
"silo",
|
|
"saml",
|
|
"fact",
|
|
] as const;
|
|
export const OtherUnitSchema = z.enum(otherUnits);
|
|
export type OtherUnit = z.infer<typeof OtherUnitSchema>;
|
|
export type OtherUnitType =
|
|
| UnitType.City
|
|
| UnitType.DefensePost
|
|
| UnitType.MissileSilo
|
|
| UnitType.Port
|
|
| UnitType.SAMLauncher
|
|
| UnitType.Warship
|
|
| UnitType.Factory;
|
|
|
|
export const unitTypeToOtherUnit = {
|
|
[UnitType.City]: "city",
|
|
[UnitType.DefensePost]: "defp",
|
|
[UnitType.MissileSilo]: "silo",
|
|
[UnitType.Port]: "port",
|
|
[UnitType.SAMLauncher]: "saml",
|
|
[UnitType.Warship]: "wshp",
|
|
[UnitType.Factory]: "fact",
|
|
} as const satisfies Record<OtherUnitType, OtherUnit>;
|
|
|
|
// Attacks
|
|
export const ATTACK_INDEX_SENT = 0; // Outgoing attack troops
|
|
export const ATTACK_INDEX_RECV = 1; // Incmoing attack troops
|
|
export const ATTACK_INDEX_CANCEL = 2; // Cancelled attack troops
|
|
|
|
// Player types
|
|
export const PLAYER_INDEX_HUMAN = 0;
|
|
export const PLAYER_INDEX_NATION = 1;
|
|
export const PLAYER_INDEX_BOT = 2;
|
|
|
|
// Boats
|
|
export const BOAT_INDEX_SENT = 0; // Boats launched
|
|
export const BOAT_INDEX_ARRIVE = 1; // Boats arrived
|
|
export const BOAT_INDEX_CAPTURE = 2; // Boats captured
|
|
export const BOAT_INDEX_DESTROY = 3; // Boats destroyed
|
|
|
|
// Bombs
|
|
export const BOMB_INDEX_LAUNCH = 0; // Bombs launched
|
|
export const BOMB_INDEX_LAND = 1; // Bombs landed
|
|
export const BOMB_INDEX_INTERCEPT = 2; // Bombs intercepted
|
|
|
|
// Gold
|
|
export const GOLD_INDEX_WORK = 0; // Gold earned by workers
|
|
export const GOLD_INDEX_WAR = 1; // Gold earned by conquering players
|
|
export const GOLD_INDEX_TRADE = 2; // Gold earned by trade ships
|
|
export const GOLD_INDEX_STEAL = 3; // Gold earned by capturing trade ships
|
|
export const GOLD_INDEX_TRAIN_SELF = 4; // Gold earned by own trains
|
|
export const GOLD_INDEX_TRAIN_OTHER = 5; // Gold earned by other players trains
|
|
|
|
// Other Units
|
|
export const OTHER_INDEX_BUILT = 0; // Structures and warships built
|
|
export const OTHER_INDEX_DESTROY = 1; // Structures and warships destroyed
|
|
export const OTHER_INDEX_CAPTURE = 2; // Structures captured
|
|
export const OTHER_INDEX_LOST = 3; // Structures/warships destroyed/captured by others
|
|
export const OTHER_INDEX_UPGRADE = 4; // Structures upgraded
|
|
|
|
export const BigIntStringSchema = z.preprocess((val) => {
|
|
if (val === null) return 0n;
|
|
if (typeof val === "string" && /^-?\d+$/.test(val)) return BigInt(val);
|
|
if (typeof val === "bigint") return val;
|
|
return val;
|
|
}, z.bigint());
|
|
|
|
const AtLeastOneNumberSchema = BigIntStringSchema.array().min(1);
|
|
export type AtLeastOneNumber = z.infer<typeof AtLeastOneNumberSchema>;
|
|
|
|
export const PlayerStatsSchema = z
|
|
.object({
|
|
attacks: AtLeastOneNumberSchema.optional(),
|
|
betrayals: BigIntStringSchema.optional(),
|
|
killedAt: BigIntStringSchema.optional(),
|
|
// Tiles owned at game end, for OFM standings (set on setWinner).
|
|
finalTiles: BigIntStringSchema.optional(),
|
|
// Humans this player eliminated (victim clientID + tick), for OFM kill scoring.
|
|
kills: z
|
|
.array(z.object({ victim: z.string(), tick: BigIntStringSchema }))
|
|
.optional(),
|
|
conquests: AtLeastOneNumberSchema.optional(),
|
|
boats: z.partialRecord(BoatUnitSchema, AtLeastOneNumberSchema).optional(),
|
|
bombs: z.partialRecord(BombUnitSchema, AtLeastOneNumberSchema).optional(),
|
|
gold: AtLeastOneNumberSchema.optional(),
|
|
units: z.partialRecord(OtherUnitSchema, AtLeastOneNumberSchema).optional(),
|
|
})
|
|
.optional();
|
|
export type PlayerStats = z.infer<typeof PlayerStatsSchema>;
|