- Day-Care and breeding: deposits, withdrawal pricing and growth, compatibility, egg species, DVs and moves, DayCareStep, egg collection and the Odd Egg, compared with the cartridge's routines. - CountStep: walking happiness, DoEggStep, DayCareStep and DoBikeStep in the original's order; HatchEggs with the ROM's hatch animation, compared frame by frame with the original. - MAIL: all ten stationery types and the Portrait MAIL picture, compared with the cartridge's tilemaps, graphics and palettes. - Printing: Bill's PC boxes, Pokedex PRNT, MAIL and the Photo Studio take the original's absent-printer path. - Hall of Fame records (saved apart from the League flag, ready for the League to call) and the Hall of Fame PC, compared with the cartridge's _HallOfFamePC on 255 pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
import { NUM_POKEMON } from "../rom/profile";
|
|
import { NICKNAME_PATTERN, PARTY_LENGTH, type PartyMon } from "./pokemon";
|
|
|
|
/** NUM_HOF_TEAMS: the teams sHallOfFame keeps; HOF_MASTER_COUNT: where
|
|
* wHallOfFameCount stops (constants/misc_constants.asm). */
|
|
export const NUM_HOF_TEAMS = 30;
|
|
export const HOF_MASTER_COUNT = 200;
|
|
|
|
/** HOF_MON_LENGTH: only the fields GetHallOfFameParty copies. */
|
|
export interface HallOfFameMon {
|
|
species: number;
|
|
otId: number;
|
|
dvs: number;
|
|
level: number;
|
|
nickname: string;
|
|
}
|
|
/** One sHallOfFame entry (HOF_LENGTH): the win it records and the team
|
|
* that won, without its Eggs. */
|
|
export interface HallOfFameTeam {
|
|
/** wHallOfFameCount when the team entered, capped at HOF_MASTER_COUNT. */
|
|
win: number;
|
|
mons: HallOfFameMon[];
|
|
}
|
|
export interface HallOfFameRecords {
|
|
/** wHallOfFameCount: the League wins, up to HOF_MASTER_COUNT. */
|
|
hallOfFameCount: number;
|
|
/** sHallOfFame, newest first. Separate from STATUSFLAGS_HALL_OF_FAME_F. */
|
|
hallOfFameTeams: HallOfFameTeam[];
|
|
}
|
|
|
|
/** GetHallOfFameParty: skip Eggs, copy the six recorded fields, and keep
|
|
* party order. An all-Egg party produces an empty team. */
|
|
export function getHallOfFameParty(
|
|
party: readonly PartyMon[],
|
|
win: number,
|
|
): HallOfFameTeam {
|
|
return {
|
|
win,
|
|
mons: party
|
|
.filter((mon) => !mon.egg)
|
|
.map(({ species, otId, dvs, level, nickname }) => ({
|
|
species,
|
|
otId,
|
|
dvs,
|
|
level,
|
|
nickname,
|
|
})),
|
|
};
|
|
}
|
|
|
|
/** HallOfFame's capped count increment, then AddHallOfFameEntry: the
|
|
* newest team goes first and the oldest of a full sHallOfFame drops off.
|
|
* The League's ceremony is not ported, so nothing calls this yet; it
|
|
* leaves STATUSFLAGS_HALL_OF_FAME_F (`hallOfFame`) to its caller. */
|
|
export function addHallOfFameEntry(
|
|
records: HallOfFameRecords,
|
|
party: readonly PartyMon[],
|
|
) {
|
|
records.hallOfFameCount = Math.min(
|
|
HOF_MASTER_COUNT,
|
|
records.hallOfFameCount + 1,
|
|
);
|
|
records.hallOfFameTeams.unshift(
|
|
getHallOfFameParty(party, records.hallOfFameCount),
|
|
);
|
|
records.hallOfFameTeams.length = Math.min(
|
|
NUM_HOF_TEAMS,
|
|
records.hallOfFameTeams.length,
|
|
);
|
|
}
|
|
|
|
/** Structural records validation; no stats, party ownership or current
|
|
* species are required of a historical entry. */
|
|
export function validHallOfFame(
|
|
count: unknown,
|
|
value: unknown,
|
|
): value is HallOfFameTeam[] {
|
|
const integer = (n: unknown, min: number, max: number): n is number =>
|
|
Number.isInteger(n) && Number(n) >= min && Number(n) <= max;
|
|
if (
|
|
!integer(count, 0, HOF_MASTER_COUNT) ||
|
|
!Array.isArray(value) ||
|
|
value.length > NUM_HOF_TEAMS
|
|
)
|
|
return false;
|
|
if ((count === 0) !== (value.length === 0) || value.length > count)
|
|
return false;
|
|
return value.every(
|
|
(team, index) =>
|
|
team !== null &&
|
|
typeof team === "object" &&
|
|
integer(team.win, 1, count) &&
|
|
(index === 0 ? team.win === count : team.win <= value[index - 1].win) &&
|
|
Array.isArray(team.mons) &&
|
|
team.mons.length <= PARTY_LENGTH &&
|
|
team.mons.every(
|
|
(mon: HallOfFameMon) =>
|
|
mon !== null &&
|
|
typeof mon === "object" &&
|
|
integer(mon.species, 1, NUM_POKEMON) &&
|
|
integer(mon.otId, 0, 65535) &&
|
|
integer(mon.dvs, 0, 65535) &&
|
|
integer(mon.level, 1, 100) &&
|
|
typeof mon.nickname === "string" &&
|
|
NICKNAME_PATTERN.test(mon.nickname),
|
|
),
|
|
);
|
|
}
|