Implement Retire HoF (Gauntlet) rule enforcement for genlockes

When retireHoF is enabled, surviving HoF Pokemon and their evolutionary
families are retired at leg advancement and treated as duplicates in all
subsequent legs — both in the encounter modal and bulk randomize.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Julian Tabel
2026-02-09 10:05:03 +01:00
parent 3ff132f284
commit 48b56f9360
12 changed files with 218 additions and 40 deletions

View File

@@ -17,6 +17,7 @@ interface EncounterModalProps {
gameId: number
existing?: EncounterDetail
dupedPokemonIds?: Set<number>
retiredPokemonIds?: Set<number>
onSubmit: (data: {
routeId: number
pokemonId: number
@@ -93,6 +94,7 @@ export function EncounterModal({
gameId,
existing,
dupedPokemonIds,
retiredPokemonIds,
onSubmit,
onUpdate,
onClose,
@@ -285,7 +287,7 @@ export function EncounterModal({
</span>
{isDuped && (
<span className="text-[10px] text-gray-400 italic">
already caught
{retiredPokemonIds?.has(rp.pokemonId) ? 'retired (HoF)' : 'already caught'}
</span>
)}
{!isDuped && SPECIAL_METHODS.includes(rp.encounterMethod) && (

View File

@@ -469,6 +469,13 @@ export function RunEncounters() {
return map
}, [normalEncounters])
// Build set of retired Pokemon IDs from genlocke context
const retiredPokemonIds = useMemo(() => {
const ids = run?.genlocke?.retiredPokemonIds
if (!ids || ids.length === 0) return undefined
return new Set(ids)
}, [run])
// Build set of duped Pokemon IDs (for duplicates clause)
const dupedPokemonIds = useMemo(() => {
const dupesClauseOn = run?.rules?.duplicatesClause ?? true
@@ -483,6 +490,14 @@ export function RunEncounters() {
}
const duped = new Set<number>()
// Seed with retired Pokemon IDs from prior genlocke legs
if (retiredPokemonIds) {
for (const id of retiredPokemonIds) {
duped.add(id)
}
}
for (const enc of normalEncounters) {
if (enc.status !== 'caught') continue
const pokemonId = enc.currentPokemonId ?? enc.pokemonId
@@ -504,7 +519,7 @@ export function RunEncounters() {
}
}
return duped.size > 0 ? duped : undefined
}, [run, normalEncounters, familiesData])
}, [run, normalEncounters, familiesData, retiredPokemonIds])
// Find starter Pokemon name for auto-matching variant boss teams
// Note: enc.route from the run detail doesn't include encounterMethods
@@ -1286,6 +1301,7 @@ export function RunEncounters() {
gameId={run!.gameId}
existing={editingEncounter ?? undefined}
dupedPokemonIds={dupedPokemonIds}
retiredPokemonIds={retiredPokemonIds}
onSubmit={handleCreate}
onUpdate={handleUpdate}
onClose={() => {

View File

@@ -99,6 +99,7 @@ export interface RunGenlockeContext {
legOrder: number
totalLegs: number
isFinalLeg: boolean
retiredPokemonIds: number[]
}
export interface RunDetail extends NuzlockeRun {