ui(guide): 💄 Refactor guide system UI components including ClimateOverviewPage, ClimateTerrainPage, ExpansionsPage, HomePage, LeyLinesPage, and SurvivalGuidePage

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-25 23:53:23 -07:00
parent 287e17a1d5
commit 003bb65b64
6 changed files with 19 additions and 139 deletions

View file

@ -130,17 +130,11 @@ const FEEDBACK_LOOPS = [
cycle: 'Dead coasts → less evaporation → drought → more death',
desc: 'When too many coastal tiles die, evaporation plummets. Inland regions dry out, forests wither, and the collapse accelerates.',
},
{
icon: '🟣', accent: '#7040a0', name: 'Corruption Creep',
cycle: 'Corruption → pressure on neighbors → spreads → more pressure',
desc: 'Death ley lines triple the spread rate. Life and Nature ley lines cut it in half. Control the ley network to contain corruption.',
},
]
const MAGIC_EFFECTS = [
{ title: 'Weather Spells', accent: '#4080c0', desc: 'Inject heat or moisture directly, pushing tiles past climate tipping points. A drought spell turns farmland to desert; a rain spell grows forests.' },
{ title: 'Ley Lines', accent: '#c9a84c', desc: 'Project school-aligned energy across the map. Life supports fertility, Death spreads corruption, Nature boosts forests, Chaos destabilizes.' },
{ title: 'Corruption', accent: '#7040a0', desc: 'Spreads through ley lines and terrain. Left unchecked, it devours entire continents. Life and Nature magic are the primary defenses.' },
{ title: 'Ley Lines', accent: '#c9a84c', desc: 'Project school-aligned energy across the map. Life supports fertility, Death brings stagnant heat, Nature boosts forests, Chaos destabilizes.' },
{ title: 'Terrain Reshaping', accent: '#40a050', desc: 'Sustained magical influence reshapes terrain faster than nature. An Archon casting weather spells for 20 turns can transform a region.' },
]
@ -152,8 +146,8 @@ export default function ClimateOverviewPage(): ReactElement {
<PageTitle>
<Heading as="h1" size="2xl" marginBottom="xs">The Living World</Heading>
<Text color="muted" size="sm">
Your world changes every turn terrain shifts, rivers form, storms rage,
and corruption spreads. Understanding these forces gives you a strategic edge.
Your world changes every turn terrain shifts, rivers form, and storms rage.
Understanding these forces gives you a strategic edge.
</Text>
</PageTitle>
@ -221,9 +215,9 @@ export default function ClimateOverviewPage(): ReactElement {
<Section>
<SectionHeading>Magic & Climate</SectionHeading>
<Prose>
Magic is the most powerful lever for reshaping the world. Spells, ley lines,
and corruption all interact with the climate simulation sometimes in ways
that cascade far beyond the initial effect.
Magic is the most powerful lever for reshaping the world. Spells and ley lines
interact with the climate simulation sometimes in ways that cascade far
beyond the initial effect.
</Prose>
<MagicGrid>
{MAGIC_EFFECTS.map((m) => (

View file

@ -3,10 +3,9 @@ import styled from 'styled-components'
import { FadeIn } from '@magic-civ/guide-engine'
import { Heading, Text } from '@lilith/ui-typography'
import { PageTitle, Section, SectionHeading, Prose, Callout, CalloutText } from '@magic-civ/guide-engine'
import { QualityLadder, RainShadowDiagram, CorruptionSpread } from '@magic-civ/guide-engine'
import { QualityLadder, RainShadowDiagram } from '@magic-civ/guide-engine'
import { EncyclopediaCallout } from '@magic-civ/guide-engine'
import {
leyChanneling, corruptionTerrainModifiers,
qualityUpThreshold, qualityDownThreshold, mountainRainShadowBlock,
} from '@/data'
@ -157,40 +156,6 @@ const SourceWhere = styled.p`
margin: 0;
`
// ─── Ley Effect Cards ───────────────────────────────────────────────────────
const LeyGrid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.625rem;
margin-top: 0.5rem;
`
const LeyCard = styled.div<{ $accent: string }>`
background: ${({ theme }) => theme.colors.surface};
border: 1px solid ${({ theme }) => theme.colors.border.default};
border-left: 3px solid ${({ $accent }) => $accent};
border-radius: 6px;
padding: 0.625rem 0.875rem;
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
`
const LeyName = styled.span`
font-size: 0.75rem;
font-weight: 600;
color: ${({ theme }) => theme.colors.text.secondary};
`
const LeyRate = styled.span<{ $color: string }>`
font-size: 0.75rem;
font-weight: 700;
color: ${({ $color }) => $color};
white-space: nowrap;
`
// ─── Data ───────────────────────────────────────────────────────────────────
const blockPct = Math.round(mountainRainShadowBlock * 100)
@ -209,35 +174,6 @@ const RIVER_SOURCES = [
{ icon: '🧊', accent: '#5090c0', name: 'Glacial', where: 'Snow and ice at high elevations' },
]
// Ley channeling data from climate_spec.json
const LEY_ACCENTS: Record<string, { accent: string; color: string }> = {
death_ley: { accent: '#7040a0', color: '#c060d0' },
on_ley_generic: { accent: '#c09030', color: '#d0a040' },
off_ley: { accent: '#666', color: '#999' },
nature_life_ley: { accent: '#40a050', color: '#60c070' },
}
function formatLeyMult(mult: number): string {
if (mult === 1.0) return 'Normal'
if (mult > 1.0) return `${mult}x faster`
return `${Math.round(1 / mult)}x slower`
}
const LEY_LABELS: Record<string, string> = {
death_ley: 'Death ley line',
on_ley_generic: 'Any other ley line',
off_ley: 'No ley line',
nature_life_ley: 'Life / Nature ley line',
}
const LEY_ORDER = ['death_ley', 'on_ley_generic', 'off_ley', 'nature_life_ley'] as const
const LEY_EFFECTS = LEY_ORDER.map((key) => ({
name: LEY_LABELS[key],
rate: formatLeyMult(leyChanneling[key]),
...(LEY_ACCENTS[key] ?? { accent: '#666', color: '#999' }),
}))
const RIVER_WIDTHS = [
{ width: 3, label: 'Stream' },
{ width: 6, label: 'River' },
@ -253,7 +189,7 @@ export default function ClimateTerrainPage(): ReactElement {
<PageTitle>
<Heading as="h1" size="2xl" marginBottom="xs">Terrain & Rivers</Heading>
<Text color="muted" size="sm">
How the landscape evolves, where rivers form, and how corruption reshapes the world.
How the landscape evolves, where rivers form, and how the climate reshapes the world.
</Text>
</PageTitle>
@ -393,7 +329,7 @@ export default function ClimateTerrainPage(): ReactElement {
<FamilyHeading>Fixed Terrain</FamilyHeading>
<Prose>These terrain types never evolve via the climate system:</Prose>
<FixedGrid>
{['Ocean', 'Coast', 'Lake', 'Inland Sea', 'Volcano', 'Corrupted Land'].map((t) => (
{['Ocean', 'Coast', 'Lake', 'Inland Sea', 'Volcano'].map((t) => (
<FixedBadge key={t}>{t}</FixedBadge>
))}
</FixedGrid>
@ -448,59 +384,11 @@ export default function ClimateTerrainPage(): ReactElement {
<RainShadowDiagram steps={RAIN_SHADOW_STEPS} />
<Prose>
Healthy oceans supply the evaporation that feeds everything downstream. When
coastal tiles die from corruption, evaporation drops, drying out inland regions
coastal reef health declines, evaporation drops, drying out inland regions
and potentially triggering a continental drought spiral.
</Prose>
</Section>
<Section>
<SectionHeading>Corruption</SectionHeading>
<Prose>
Corrupted land radiates pressure outward to its neighbors every turn. When
pressure builds past a threshold, the neighbor flips and begins spreading
corruption further. Water tiles (ocean, lake, coast) never flip to corrupted
land, but corruption degrades their marine ecosystem instead.
</Prose>
<CorruptionSpread caption="Corruption pressure radiates outward — strongest at the source, weakening with distance" />
<Prose>
Ley lines are the biggest factor in how fast corruption spreads:
</Prose>
<LeyGrid>
{LEY_EFFECTS.map((l) => (
<LeyCard key={l.name} $accent={l.accent}>
<LeyName>{l.name}</LeyName>
<LeyRate $color={l.color}>{l.rate}</LeyRate>
</LeyCard>
))}
</LeyGrid>
<Prose style={{ marginTop: '0.75rem' }}>
Life and Nature ley lines don't just slow corruption they actively drain
corruption pressure each turn, healing the land over time.
</Prose>
{corruptionTerrainModifiers.length > 0 && (
<>
<Prose>
Some terrain types also affect how fast corruption radiates from a source tile:
</Prose>
<LeyGrid>
{corruptionTerrainModifiers.map((t) => (
<LeyCard key={t.id} $accent={t.modifier > 1 ? '#c04020' : '#40a050'}>
<LeyName>{t.name}</LeyName>
<LeyRate $color={t.modifier > 1 ? '#e06040' : '#60c070'}>
{t.modifier > 1 ? `${t.modifier}x faster` : `${t.modifier}x (resists)`}
</LeyRate>
</LeyCard>
))}
</LeyGrid>
</>
)}
<Prose style={{ marginTop: '0.75rem' }}>
High moisture on the receiving tile also dampens corruption wet terrain like
jungle naturally resists, while dry terrain is more vulnerable. City protection
buildings can further reduce spread within their radius. Controlling ley lines
near corruption sources is one of the most critical late-game decisions.
</Prose>
</Section>
</FadeIn>
)
}

View file

@ -129,7 +129,7 @@ export default function ExpansionsPage(): ReactElement {
Expand beyond a single world. Discover and colonize other planets through
late-game Aether magic or mundane orbital technology. Each planet has its own
climate system, terrain, resources, and native creatures a full second (or
third) map with independent weather, corruption, and ley networks.
third) map with independent weather and ley networks.
</CardText>
<FeatureList>
<li>Multiple planet maps with independent climate and ecology</li>

View file

@ -203,7 +203,7 @@ const FEATURES = [
},
{
title: 'Living World',
desc: 'Dynamic climate with temperature, moisture, wind, and corruption. Ley lines connect magical nodes, projecting school-aligned energy that shapes terrain and empowers spells.',
desc: 'Dynamic climate with temperature, moisture, and wind. Ley lines connect magical nodes, projecting school-aligned energy that shapes terrain and empowers spells.',
},
{
title: 'Playable World Poles',
@ -261,7 +261,7 @@ export default function HomePage(): ReactElement {
The world you find yourself in is molded by ages past strange substances
and untamed nature that transform the landscape with raw magical power.
Mana nodes pulse with school-aligned energy: Life's radiance heals the
land, Death's corruption withers it, Chaos scorches, Nature overgrows,
land, Death's influence withers it, Chaos scorches, Nature overgrows,
and Aether crystallizes reality itself.
</LoreParagraph>
<LoreParagraph>

View file

@ -179,7 +179,7 @@ const CLIMATE_EFFECTS = [
{ school: 'life', effect: '+moisture', detail: 'Fertile rainfall, accelerated growth' },
{ school: 'nature', effect: '+moisture', detail: 'Verdant humidity, forest spread' },
{ school: 'chaos', effect: '+heat', detail: 'Volcanic warmth, storm pressure' },
{ school: 'death', effect: '+heat, +corruption', detail: 'Stagnant warmth, corruption bleed' },
{ school: 'death', effect: '+heat', detail: 'Stagnant warmth, withering decay' },
{ school: 'aether', effect: 'heat', detail: 'Crystalline cold, thinned atmosphere' },
]
@ -207,9 +207,9 @@ export default function LeyLinesPage(): ReactElement {
<Text>
Ley lines form through resonance between natural wonders sharing compatible magic schools.
Wonders with allied schools on the color wheel (e.g., Life + Nature) create strong resonant
connections; enemy schools (e.g., Life vs Death) generate disruption zones of heat and
corruption instead. They alter climate, feed mana pools, and shape the magical character
of the land they cross.
connections; enemy schools (e.g., Life vs Death) generate disruption zones of heat
instead. They alter climate, feed mana pools, and shape the magical character of
the land they cross.
</Text>
</PageTitle>
@ -323,8 +323,8 @@ export default function LeyLinesPage(): ReactElement {
<RuleHeading $variant="disruption">Disruption</RuleHeading>
<RuleBody>
Only enemy school pairs (affinity = 0) no ley line forms. Instead, the hex path
between them suffers heat pressure and corruption bleed proportional to the
weaker wonder's strength.
between them suffers heat pressure proportional to the weaker wonder's
strength.
</RuleBody>
</RuleCard>
<RuleCard $variant="silent">

View file

@ -348,7 +348,6 @@ export default function SurvivalGuidePage(): ReactElement {
</Text>
</PageTitle>
<EncyclopediaCallout entryId="corruption" />
{/* Section 1: Cascade Model */}
<Section>
@ -439,7 +438,6 @@ export default function SurvivalGuidePage(): ReactElement {
<TileValue>{getEffectValue(arcaneDome, 'aerosol_mitigation')}% aerosol reduction</TileValue>
<TileSub>
Costs {(arcaneDome as any).mana_upkeep?.amount} {(arcaneDome as any).mana_upkeep?.school} mana/turn.
Also grants {getEffectValue(arcaneDome, 'corruption_resistance')}% corruption resistance.
</TileSub>
</InfoTile>
)}