40 lines
1.8 KiB
TypeScript
40 lines
1.8 KiB
TypeScript
import catalog from "../game/dungeonRaceBotCatalog.json";
|
|
|
|
export type RaceBotTier = "novice" | "regular" | "veteran" | "elite";
|
|
export type RaceBotSource = "wow" | "rom";
|
|
export interface RaceBot {
|
|
readonly type: "bot";
|
|
readonly name: string;
|
|
readonly tier: RaceBotTier;
|
|
readonly source: RaceBotSource;
|
|
readonly roster: readonly { readonly name: string; readonly classId: string; readonly role: string }[];
|
|
readonly finishMs: number;
|
|
readonly milestones: readonly { readonly atMs: number; readonly progress: number; readonly label: string }[];
|
|
}
|
|
|
|
export function hashRaceSeed(value: string): number {
|
|
let hash = 2166136261;
|
|
for (const char of value) { hash ^= char.charCodeAt(0); hash = Math.imul(hash, 16777619); }
|
|
return hash >>> 0;
|
|
}
|
|
|
|
export function createRaceBot(seed: string, tierId: RaceBotTier, source: RaceBotSource): RaceBot {
|
|
const tier = catalog.tiers.find((entry) => entry.id === tierId) ?? catalog.tiers[1];
|
|
const variance = 0.92 + (hashRaceSeed(`${seed}:pace`) % 161) / 1000;
|
|
return { type: "bot", name: `${tier.label} ${source === "rom" ? "RuneWaker" : "Azeroth"} Team`,
|
|
tier: tier.id as RaceBotTier, source, roster: catalog.teams[source],
|
|
finishMs: Math.round(catalog.baselineMs * tier.multiplier * variance),
|
|
milestones: catalog.milestones.map((entry) => ({ ...entry,
|
|
atMs: Math.round(catalog.baselineMs * tier.multiplier * variance * entry.at) })) };
|
|
}
|
|
|
|
export function raceBotProgress(elapsedMs: number, bot: RaceBot): number {
|
|
if (elapsedMs <= 0) return 0;
|
|
const previous = [{ atMs: 0, progress: 0 }, ...bot.milestones]
|
|
.filter((entry) => entry.atMs <= elapsedMs).at(-1)!;
|
|
const next = bot.milestones.find((entry) => entry.atMs > elapsedMs);
|
|
if (!next) return 100;
|
|
return Math.floor(previous.progress + (next.progress - previous.progress)
|
|
* (elapsedMs - previous.atMs) / (next.atMs - previous.atMs));
|
|
}
|