feat(climate-sim): ✨ Update biome/terrain rendering, add async worker hook, and refresh sprite generation database
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
344614c7f0
commit
5a559140a1
7 changed files with 41 additions and 21 deletions
|
|
@ -5,7 +5,6 @@ import { SCENARIOS, DEFAULT_SCENARIO_TURNS } from '@magic-civ/engine-ts'
|
|||
import { getCache, putCache, pruneStaleEntries } from '@/simulation/simCache'
|
||||
import type { CachedScenario } from '@/simulation/simCache'
|
||||
|
||||
import SimulationWorker from '../simulation/simulation.worker.ts?worker'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
|
|
@ -134,7 +133,7 @@ export function useSimulationWorker(): UseSimulationWorkerResult {
|
|||
// Defined before useEffect so HMR partial re-evaluation keeps references stable.
|
||||
|
||||
const speculateSingle = (scenarioId: string, seed: number, payload: NonNullable<typeof initPayloadRef.current>): void => {
|
||||
const bgWorker = new SimulationWorker()
|
||||
const bgWorker = new Worker(new URL('../simulation/simulation.worker.ts', import.meta.url), { type: 'module' })
|
||||
bgWorkersRef.current.push(bgWorker)
|
||||
|
||||
bgWorker.onmessage = (e: MessageEvent<WorkerResponse>): void => {
|
||||
|
|
@ -175,7 +174,7 @@ export function useSimulationWorker(): UseSimulationWorkerResult {
|
|||
.then((cached) => {
|
||||
if (cached && cached.turns >= DEFAULT_SCENARIO_TURNS) return
|
||||
|
||||
const bgWorker = new SimulationWorker()
|
||||
const bgWorker = new Worker(new URL('../simulation/simulation.worker.ts', import.meta.url), { type: 'module' })
|
||||
bgWorkersRef.current.push(bgWorker)
|
||||
|
||||
bgWorker.onmessage = (e: MessageEvent<WorkerResponse>): void => {
|
||||
|
|
@ -204,7 +203,7 @@ export function useSimulationWorker(): UseSimulationWorkerResult {
|
|||
|
||||
// ── Initialize worker ──────────────────────────────────────────────────
|
||||
useEffect(() => {
|
||||
const worker = new SimulationWorker()
|
||||
const worker = new Worker(new URL('../simulation/simulation.worker.ts', import.meta.url), { type: 'module' })
|
||||
workerRef.current = worker
|
||||
|
||||
worker.onmessage = (e: MessageEvent<WorkerResponse>): void => {
|
||||
|
|
|
|||
|
|
@ -128,14 +128,23 @@ function fmtVal(v: number): string {
|
|||
|
||||
interface BiomeReferenceProps {
|
||||
layerMask: number
|
||||
presentBiomes?: ReadonlySet<string>
|
||||
}
|
||||
|
||||
export function BiomeReference({ layerMask }: BiomeReferenceProps): ReactElement | null {
|
||||
export function BiomeReference({ layerMask, presentBiomes }: BiomeReferenceProps): ReactElement | null {
|
||||
const [hoveredBiome, setHoveredBiome] = useState<string | null>(null)
|
||||
|
||||
// Only relevant when Terrain (bit 5) or Biomes (bit 0) layer is active
|
||||
if ((layerMask & ((1 << 5) | (1 << 0))) === 0) return null
|
||||
|
||||
const visibleFamilies = BIOME_FAMILIES.flatMap((family) => {
|
||||
const visibleBiomes = presentBiomes
|
||||
? family.biomes.filter((b) => presentBiomes.has(b.id))
|
||||
: family.biomes
|
||||
if (visibleBiomes.length === 0) return []
|
||||
return [{ ...family, biomes: visibleBiomes }]
|
||||
})
|
||||
|
||||
return (
|
||||
<MapOverlayPanel title="Biome Reference">
|
||||
<Intro>
|
||||
|
|
@ -143,7 +152,7 @@ export function BiomeReference({ layerMask }: BiomeReferenceProps): ReactElement
|
|||
the decision path and example input values that produce it.
|
||||
</Intro>
|
||||
|
||||
{BIOME_FAMILIES.map((family) => (
|
||||
{visibleFamilies.map((family) => (
|
||||
<FamilySection key={family.label}>
|
||||
<FamilyHeader>
|
||||
<PanelSectionLabel style={{ margin: 0 }}>{family.label}</PanelSectionLabel>
|
||||
|
|
|
|||
|
|
@ -324,6 +324,11 @@ export function ClimateSimDisplay({ easterEggs }: ClimateSimDisplayProps): React
|
|||
const timings = scenarioData?.timings ?? []
|
||||
const currentStats = stats[currentTurn]
|
||||
|
||||
const presentBiomes = useMemo(
|
||||
() => new Set(Object.keys(currentStats?.terrain_counts ?? {})),
|
||||
[currentStats],
|
||||
)
|
||||
|
||||
const snapshot = useMemo((): GridSnapshot | null => {
|
||||
if (!currentFrame) return null
|
||||
return frameAsSnapshot(currentFrame, currentStats)
|
||||
|
|
@ -541,8 +546,8 @@ export function ClimateSimDisplay({ easterEggs }: ClimateSimDisplayProps): React
|
|||
</OverlayTopRight>
|
||||
|
||||
<OverlayTopLeft>
|
||||
<TerrainLegend layerMask={layerMask} />
|
||||
<BiomeReference layerMask={layerMask} />
|
||||
<TerrainLegend layerMask={layerMask} presentBiomes={presentBiomes} />
|
||||
<BiomeReference layerMask={layerMask} presentBiomes={presentBiomes} />
|
||||
</OverlayTopLeft>
|
||||
|
||||
<OverlayBottomLeft>
|
||||
|
|
|
|||
|
|
@ -84,9 +84,10 @@ const has = (mask: number, b: number): boolean => (mask & (1 << b)) !== 0
|
|||
|
||||
interface TerrainLegendProps {
|
||||
layerMask: number
|
||||
presentBiomes?: ReadonlySet<string>
|
||||
}
|
||||
|
||||
export function TerrainLegend({ layerMask }: TerrainLegendProps): ReactElement | null {
|
||||
export function TerrainLegend({ layerMask, presentBiomes }: TerrainLegendProps): ReactElement | null {
|
||||
const showTerrain = has(layerMask, 5) || has(layerMask, 0)
|
||||
const showTemp = has(layerMask, 1)
|
||||
const showMoisture = has(layerMask, 2)
|
||||
|
|
@ -112,18 +113,24 @@ export function TerrainLegend({ layerMask }: TerrainLegendProps): ReactElement |
|
|||
{showTerrain && (
|
||||
<Section>
|
||||
<BiomeList>
|
||||
{TERRAIN_SECTIONS.map(({ label, biomes }, si) => (
|
||||
<BiomeGroup key={label}>
|
||||
{si > 0 && <GroupDivider />}
|
||||
<GroupLabel>{label}</GroupLabel>
|
||||
{biomes.map(({ id, label: biomeLabel, rgb }) => (
|
||||
<SwatchRow key={id}>
|
||||
<Swatch style={{ background: rgbToCSS(rgb) }} />
|
||||
<SwatchLabel>{biomeLabel}</SwatchLabel>
|
||||
</SwatchRow>
|
||||
))}
|
||||
</BiomeGroup>
|
||||
))}
|
||||
{TERRAIN_SECTIONS.flatMap(({ label, biomes }, si) => {
|
||||
const visibleBiomes = presentBiomes
|
||||
? biomes.filter(({ id }) => presentBiomes.has(id))
|
||||
: biomes
|
||||
if (visibleBiomes.length === 0) return []
|
||||
return [(
|
||||
<BiomeGroup key={label}>
|
||||
{si > 0 && <GroupDivider />}
|
||||
<GroupLabel>{label}</GroupLabel>
|
||||
{visibleBiomes.map(({ id, label: biomeLabel, rgb }) => (
|
||||
<SwatchRow key={id}>
|
||||
<Swatch style={{ background: rgbToCSS(rgb) }} />
|
||||
<SwatchLabel>{biomeLabel}</SwatchLabel>
|
||||
</SwatchRow>
|
||||
))}
|
||||
</BiomeGroup>
|
||||
)]
|
||||
})}
|
||||
</BiomeList>
|
||||
{showTerrain && (
|
||||
<GradientRow>
|
||||
|
|
|
|||
BIN
legend-filtered.png
Normal file
BIN
legend-filtered.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Add table
Reference in a new issue