diff --git a/games/age-of-dwarves/guide/src/pages/BiomeBrowserPage.tsx b/games/age-of-dwarves/guide/src/pages/BiomeBrowserPage.tsx deleted file mode 100644 index 4771f92a..00000000 --- a/games/age-of-dwarves/guide/src/pages/BiomeBrowserPage.tsx +++ /dev/null @@ -1,351 +0,0 @@ -import { useState, useMemo, type ReactElement } from 'react' -import styled from 'styled-components' -import { FadeIn, PageTitle, Section, SectionHeading, Prose, FilterRow, FilterChip } from '@magic-civ/guide-engine' -import { Heading, Text } from '@lilith/ui-typography' -import { ALL_BIOMES, BIOME_CATEGORIES, QUALITY_COLORS, QUALITY_LABELS } from '@/data/ecology' -import type { BiomeDisplay } from '@/data/ecology' - -function BiomePieChart({ biomes }: { biomes: BiomeDisplay[] }): ReactElement { - const total = biomes.length - let currentAngle = 0 - - const slices = biomes.map((biome) => { - const fraction = 1 / total - const startAngle = currentAngle - const endAngle = currentAngle + fraction * 360 - currentAngle = endAngle - - const startRad = (startAngle - 90) * (Math.PI / 180) - const endRad = (endAngle - 90) * (Math.PI / 180) - const largeArc = fraction > 0.5 ? 1 : 0 - const x1 = 50 + 45 * Math.cos(startRad) - const y1 = 50 + 45 * Math.sin(startRad) - const x2 = 50 + 45 * Math.cos(endRad) - const y2 = 50 + 45 * Math.sin(endRad) - - return { - biome, - d: `M 50 50 L ${x1} ${y1} A 45 45 0 ${largeArc} 1 ${x2} ${y2} Z`, - } - }) - - return ( - - - {slices.map(({ biome, d }) => ( - - {biome.name} - - ))} - - - {BIOME_CATEGORIES.map(cat => { - const count = biomes.filter(b => b.category === cat.id).length - if (count === 0) return null - return ( - - - {cat.label} ({count}) - - ) - })} - - - ) -} - -function BiomeCard({ biome }: { biome: BiomeDisplay }): ReactElement { - return ( - - - - {biome.name} - {biome.category} - - - - Quality Range - - {Array.from({ length: biome.quality_range[1] - biome.quality_range[0] + 1 }, (_, i) => { - const q = biome.quality_range[0] + i - return ( - - {q} - - ) - })} - - - - Temperature - {biome.temp_range[0].toFixed(2)} - {biome.temp_range[1].toFixed(2)} - - - Moisture - {biome.moisture_range[0].toFixed(2)} - {biome.moisture_range[1].toFixed(2)} - - - Fauna Capacity - {biome.fauna_capacity} - - - - - - - - Canopy {biome.flora_climax.canopy} - Undergrowth {biome.flora_climax.undergrowth} - Fungi {biome.flora_climax.fungi} - - - - ) -} - -export default function BiomeBrowserPage(): ReactElement { - const [categoryFilter, setCategoryFilter] = useState('all') - - const filtered = useMemo(() => { - if (categoryFilter === 'all') return ALL_BIOMES - return ALL_BIOMES.filter(b => b.category === categoryFilter) - }, [categoryFilter]) - - return ( - - - Biome Browser - - {ALL_BIOMES.length} biomes across aquatic, tropical, temperate, cold, elevation, and - special categories. Each biome defines climate ranges, flora climax values, and fauna capacity. - - - -
- Distribution - - Biomes emerge from substrate type, temperature, moisture, and canopy state. - The BiomeClassifier reclassifies tiles each turn when conditions change significantly. - - -
- -
- All Biomes - - setCategoryFilter('all')}> - All ({ALL_BIOMES.length}) - - {BIOME_CATEGORIES.map(cat => { - const count = ALL_BIOMES.filter(b => b.category === cat.id).length - return ( - setCategoryFilter(cat.id)} - > - {cat.label} ({count}) - - ) - })} - - - {filtered.map(biome => ( - - ))} - -
- -
- Color Legend - - {ALL_BIOMES.map(biome => ( - - - {biome.name} - - ))} - -
-
- ) -} - -// --------------------------------------------------------------------------- -// Styled components -// --------------------------------------------------------------------------- - -const PieContainer = styled.div` - display: flex; - align-items: center; - gap: 2rem; - margin-top: 1rem; - flex-wrap: wrap; -` - -const PieLegend = styled.div` - display: flex; - flex-direction: column; - gap: 0.5rem; -` - -const PieLegendItem = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.8125rem; - color: ${({ theme }) => theme.colors.text.secondary}; -` - -const PieLegendDot = styled.span` - width: 0.75rem; - height: 0.75rem; - border-radius: 50%; - flex-shrink: 0; -` - -const BiomeGrid = styled.div` - display: grid; - grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); - gap: 1rem; - margin-top: 1rem; -` - -const Card = styled.div` - background: ${({ theme }) => theme.colors.surface}; - border: 1px solid ${({ theme }) => theme.colors.border.default}; - border-radius: 6px; - overflow: hidden; -` - -const CardHeader = styled.div` - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.75rem 1rem; - border-bottom: 1px solid ${({ theme }) => theme.colors.border.default}; -` - -const BiomeSwatch = styled.span` - width: 1.25rem; - height: 1.25rem; - border-radius: 4px; - flex-shrink: 0; -` - -const CardTitle = styled.span` - font-weight: 600; - font-size: 0.9375rem; - color: ${({ theme }) => theme.colors.text.primary}; - flex: 1; -` - -const CategoryTag = styled.span` - font-size: 0.6875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: ${({ theme }) => theme.colors.text.muted}; - padding: 0.125rem 0.375rem; - border: 1px solid ${({ theme }) => theme.colors.border.default}; - border-radius: 3px; -` - -const CardBody = styled.div` - padding: 0.75rem 1rem; - display: flex; - flex-direction: column; - gap: 0.5rem; -` - -const StatRow = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.8125rem; -` - -const StatLabel = styled.span` - color: ${({ theme }) => theme.colors.text.muted}; -` - -const StatValue = styled.span` - font-family: monospace; - font-weight: 600; - color: ${({ theme }) => theme.colors.text.primary}; -` - -const QualityRange = styled.div` - display: flex; - gap: 0.25rem; -` - -const QualityDot = styled.span` - width: 1.5rem; - height: 1.5rem; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.6875rem; - font-weight: 700; - font-family: monospace; - color: #fff; -` - -const FloraBar = styled.div` - display: flex; - height: 0.375rem; - border-radius: 3px; - overflow: hidden; - background: ${({ theme }) => theme.colors.border.default}; - margin-top: 0.25rem; -` - -const FloraSegment = styled.div` - height: 100%; - min-width: 1px; -` - -const FloraLabels = styled.div` - display: flex; - gap: 0.75rem; - font-size: 0.6875rem; - font-family: monospace; - color: ${({ theme }) => theme.colors.text.muted}; -` - -const ColorLegend = styled.div` - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 0.375rem; - margin-top: 0.5rem; -` - -const LegendEntry = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.8125rem; - color: ${({ theme }) => theme.colors.text.secondary}; -` - -const LegendSwatch = styled.span` - width: 1rem; - height: 1rem; - border-radius: 3px; - flex-shrink: 0; -` - -const LegendName = styled.span` - white-space: nowrap; -`