feat(@projects/@magic-civilization): ✨ add race episode filtering logic
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
77181f8808
commit
916dcac056
3 changed files with 38 additions and 7 deletions
BIN
mcp_wave_b_c_d_home.png
Normal file
BIN
mcp_wave_b_c_d_home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
|
|
@ -45,6 +45,18 @@ const DEV_GUIDE_ALL_EPISODES = 999
|
|||
const ACTIVE_EPISODE: number =
|
||||
import.meta.env.VITE_DEV_GUIDE === '1' ? DEV_GUIDE_ALL_EPISODES : 1
|
||||
|
||||
// Races playable in the current episode. Every race JSON carries an
|
||||
// `episode: number` field = the episode where that race first becomes a
|
||||
// valid leader choice (cumulative). Game 1 = Dwarves only; Game 3 =
|
||||
// Dwarves + Kzzkyt + four Elf races; etc. Dev bundle
|
||||
// (`VITE_DEV_GUIDE=1` → episode=999) unlocks every race whose episode
|
||||
// field is set. Races without an `episode` field (stubs, wrapper JSONs
|
||||
// like strategic_axes) are excluded.
|
||||
const playableRaces = allRaces.filter(
|
||||
(r): r is typeof r & { episode: number } =>
|
||||
typeof r.episode === 'number' && r.episode <= ACTIVE_EPISODE,
|
||||
)
|
||||
|
||||
export default function App(): ReactElement {
|
||||
const { preferences, isFirstVisit, save } = usePlayerPreferences()
|
||||
const [searchParams] = useSearchParams()
|
||||
|
|
@ -183,7 +195,7 @@ export default function App(): ReactElement {
|
|||
return (
|
||||
<EpisodeProvider episode={ACTIVE_EPISODE}>
|
||||
<GuideDataProvider data={guideData}>
|
||||
<PreferencesProvider preferences={activePrefs} races={allRaces}>
|
||||
<PreferencesProvider preferences={activePrefs} races={playableRaces}>
|
||||
<RaceThemeProvider
|
||||
colorMode={activePrefs.colorMode}
|
||||
dyslexicFont={activePrefs.dyslexicFont}
|
||||
|
|
@ -201,7 +213,7 @@ export default function App(): ReactElement {
|
|||
return (
|
||||
<EpisodeProvider episode={ACTIVE_EPISODE}>
|
||||
<GuideDataProvider data={guideData}>
|
||||
<PreferencesProvider preferences={activePrefs} races={allRaces}>
|
||||
<PreferencesProvider preferences={activePrefs} races={playableRaces}>
|
||||
<AppShell save={save} showWelcome={showWelcome} setShowWelcome={setShowWelcome} preferences={activePrefs}>
|
||||
{routes}
|
||||
</AppShell>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useEffect, useRef, type ReactElement } from 'react'
|
||||
import { useState, useEffect, useMemo, useRef, type ReactElement } from 'react'
|
||||
import { useEpisode } from '@magic-civ/guide-engine'
|
||||
import type { PlayerPreferences, ColorMode, FontSize } from '@/hooks/usePlayerPreferences'
|
||||
import { allRaces } from '@/data'
|
||||
import { resolveRace, resolveGender, type ConcreteRace, type ConcreteGender } from '@/contexts/PreferencesContext'
|
||||
|
|
@ -23,13 +24,21 @@ const RULER_NAMES: Record<string, string[]> = {}
|
|||
interface RaceOption {
|
||||
id: ConcreteRace
|
||||
label: string
|
||||
episode: number
|
||||
}
|
||||
|
||||
const RACE_OPTIONS: RaceOption[] = allRaces
|
||||
.filter((r) => r.id in RACE_ICONS)
|
||||
/** Every race whose JSON carries an `episode: number` field + a sidebar icon
|
||||
* in `RACE_ICONS`. The active-episode filter (inside the component) trims
|
||||
* the list down to races playable in the current game — Game 1 = Dwarves
|
||||
* only, Game 2 adds Kzzkyt, Game 3 unlocks the four Elf races, etc. */
|
||||
const ALL_ELIGIBLE_RACES: RaceOption[] = allRaces
|
||||
.filter((r): r is typeof r & { episode: number } =>
|
||||
typeof r.episode === 'number' && r.id in RACE_ICONS,
|
||||
)
|
||||
.map((r): RaceOption => ({
|
||||
id: r.id as ConcreteRace,
|
||||
label: r.name,
|
||||
episode: r.episode,
|
||||
}))
|
||||
|
||||
const GENDER_OPTIONS: ReadonlyArray<{ id: ConcreteGender; label: string }> = [
|
||||
|
|
@ -64,6 +73,16 @@ export function WelcomeModal({ onConfirm, onClose, onPreview, initialPreferences
|
|||
const initRaceRandom = !initialPreferences || initialPreferences.race === 'random'
|
||||
const initGenderRandom = !initialPreferences || initialPreferences.gender === 'random'
|
||||
|
||||
// Race grid filtered by the active episode — every race's `episode` field
|
||||
// declares the game where it first becomes a valid leader choice
|
||||
// (cumulative). Game 1 = Dwarves only; Game 3 would offer Dwarves +
|
||||
// Kzzkyt + four Elf races; `VITE_DEV_GUIDE=1` unlocks the full roster.
|
||||
const activeEpisode = useEpisode()
|
||||
const raceOptions = useMemo<RaceOption[]>(
|
||||
() => ALL_ELIGIBLE_RACES.filter((r) => r.episode <= activeEpisode),
|
||||
[activeEpisode],
|
||||
)
|
||||
|
||||
const [raceIsRandom, setRaceIsRandom] = useState(initRaceRandom)
|
||||
const [genderIsRandom, setGenderIsRandom] = useState(initGenderRandom)
|
||||
|
||||
|
|
@ -146,8 +165,8 @@ export function WelcomeModal({ onConfirm, onClose, onPreview, initialPreferences
|
|||
Shuffle
|
||||
</ShuffleButton>
|
||||
</SectionRow>
|
||||
<OptionGrid $cols={RACE_OPTIONS.length}>
|
||||
{RACE_OPTIONS.map((opt) => {
|
||||
<OptionGrid $cols={raceOptions.length}>
|
||||
{raceOptions.map((opt) => {
|
||||
const RaceIcon = RACE_ICONS[opt.id][visibleGender]
|
||||
return (
|
||||
<OptionButton
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue