import { RomReader } from "../rom/reader"; import { OVERWORLD } from "../rom/profile"; import { PokemonData } from "../rom/pokemon"; import { defaultTextContext, readString } from "../scripts/text"; export const DECORATION_SLOTS = [ "bed", "carpet", "plant", "poster", "console", "bigDoll", "left", "right", ] as const; export type DecorationSlot = (typeof DECORATION_SLOTS)[number]; export type Decorations = Record; export const emptyDecorations = (): Decorations => ({ bed: 0, carpet: 0, plant: 0, poster: 0, console: 0, bigDoll: 0, left: 0, right: 0, }); export const initialDecorations = (): Decorations => ({ ...emptyDecorations(), bed: 2, poster: 16, }); /** DecorationAttributes and GetDecoName, read from the user's ROM. */ export class DecorationData { constructor( private reader: RomReader, private pokemon: PokemonData, ) {} attribute(id: number) { if (!Number.isInteger(id) || id < 0 || id > 52) throw new Error("Invalid decoration."); const p = OVERWORLD.DecorationAttributes + id * 6, r = this.reader; return { type: r.byte(p), name: r.byte(p + 1), action: r.byte(p + 2), event: r.word(p + 3), sprite: r.byte(p + 5), }; } private word(index: number) { let p = OVERWORLD.DecorationNames; for (let i = 0; i < index; i++) { while (this.reader.byte(p++) !== 0x50) {} } return readString(this.reader, p, defaultTextContext("PLAYER")); } name(id: number) { const a = this.attribute(id); if (a.type === 1) return this.word(a.name); if (a.type === 2 || a.type === 3) return this.word(a.name) + this.word(a.type === 2 ? 13 : 14); const mon = this.pokemon.speciesName(a.name); return a.type === 6 ? this.word(17) + mon : mon + this.word(a.type === 4 ? 15 : 16); } owned(events: Set, action: number) { return Array.from({ length: 52 }, (_, i) => i + 1).filter((id) => { const a = this.attribute(id); return a.action === action && events.has(a.event); }); } valid(value: unknown, events: Set): value is Decorations { if ( !value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 8 ) return false; const slots = value as Decorations; return ( DECORATION_SLOTS.every((slot, i) => { const id = slots[slot]; return ( Number.isInteger(id) && id >= 0 && id <= 52 && (id === 0 || (this.attribute(id).action === (i < 6 ? i * 2 + 1 : 13) && events.has(this.attribute(id).event))) ); }) && (!slots.left || slots.left !== slots.right) ); } }