feat(@projects/@magic-civilization): ✨ add harvest policy system
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
24ed631a60
commit
d702b1ebae
6 changed files with 533 additions and 1 deletions
|
|
@ -9,6 +9,7 @@ import { SpecialistsPage } from "./pages/Specialists";
|
|||
import { FreepeopleLifecyclePage } from "./pages/FreepeopleLifecycle";
|
||||
import { TerrainEcologyPage } from "./pages/TerrainEcology";
|
||||
import { FoodWebPage } from "./pages/FoodWeb";
|
||||
import { HarvestPoliciesPage } from "./pages/HarvestPolicies";
|
||||
import { CombatPreviewPage } from "./pages/CombatPreview";
|
||||
import { CombatCalculatorPage } from "./pages/CombatCalculator";
|
||||
import { PermutationsPage } from "./pages/Permutations";
|
||||
|
|
@ -70,6 +71,7 @@ export function App(): React.ReactElement {
|
|||
<Route path="/freepeople-lifecycle" element={<FreepeopleLifecyclePage />} />
|
||||
<Route path="/terrain-ecology" element={<TerrainEcologyPage />} />
|
||||
<Route path="/food-web" element={<FoodWebPage />} />
|
||||
<Route path="/harvest-policies" element={<HarvestPoliciesPage />} />
|
||||
<Route path="/trees" element={<BuildingTreesPage />} />
|
||||
<Route path="/promotion" element={<PromotionPickerPage />} />
|
||||
<Route path="/stats" element={<StatisticsPage />} />
|
||||
|
|
|
|||
|
|
@ -30,6 +30,22 @@ function summariseYields(ys: SpecialistYield[]): string {
|
|||
}).join(" ");
|
||||
}
|
||||
|
||||
// ── Real harvest-policy data load ────────────────────────────────────────────
|
||||
interface HarvestPolicy {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
yield_multiplier: { food: number; production: number; trade: number; culture: number };
|
||||
biome_quality_delta_per_turn: number;
|
||||
}
|
||||
const policyModules = import.meta.glob<{ default: HarvestPolicy[] }>(
|
||||
"../../../../../public/resources/harvest_policies/*.json",
|
||||
{ eager: true },
|
||||
);
|
||||
const ALL_POLICIES: HarvestPolicy[] = Object.values(policyModules).flatMap(m => m.default);
|
||||
const POLICY_BY_ID = new Map(ALL_POLICIES.map(p => [p.id, p]));
|
||||
|
||||
const MapBleed = styled.div`
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
|
@ -465,6 +481,35 @@ export function CityScreenPage(): React.ReactElement {
|
|||
</CitizenRow>
|
||||
</CitizensSection>
|
||||
|
||||
<SectionHead>Tile Harvest Policies</SectionHead>
|
||||
<CitizensSection>
|
||||
{/* Real per-tile policies — driven by harvest_policies.json */}
|
||||
{[
|
||||
{ tile: "🌲 Forest (0,3)", policyId: "extract_sustainable" },
|
||||
{ tile: "🌲 Forest (4,1)", policyId: "replenish" },
|
||||
{ tile: "🌲 Boreal (5,2)", policyId: "extract_high" },
|
||||
{ tile: "🌳 Wooded Foothills (1,2)", policyId: "remove_chop" },
|
||||
].map((row, i) => {
|
||||
const p = POLICY_BY_ID.get(row.policyId);
|
||||
if (!p) return null;
|
||||
return (
|
||||
<CitizenRow key={i}>
|
||||
<CitizenIcon>{p.icon}</CitizenIcon>
|
||||
<span style={{ minWidth: 100, color: p.color, fontWeight: 600 }}>{p.name}</span>
|
||||
<CitizenTile>{row.tile}</CitizenTile>
|
||||
<CitizenYields>
|
||||
🌾×{p.yield_multiplier.food} ⚒×{p.yield_multiplier.production}
|
||||
{p.biome_quality_delta_per_turn !== 0 && (
|
||||
<span style={{ marginLeft: 8, color: p.biome_quality_delta_per_turn > 0 ? "#7cd9a0" : "#d95940" }}>
|
||||
biome {p.biome_quality_delta_per_turn > 0 ? "+" : ""}{p.biome_quality_delta_per_turn}
|
||||
</span>
|
||||
)}
|
||||
</CitizenYields>
|
||||
</CitizenRow>
|
||||
);
|
||||
})}
|
||||
</CitizensSection>
|
||||
|
||||
<SectionHead>City Stats</SectionHead>
|
||||
<StatsGrid>
|
||||
<StatCard><StatCardLabel>City HP</StatCardLabel><StatCardVal $color={t.sem.positive}>200</StatCardVal><StatCardSub>/ 200 max · Walled</StatCardSub></StatCard>
|
||||
|
|
|
|||
301
.project/designs/app/src/pages/HarvestPolicies.tsx
Normal file
301
.project/designs/app/src/pages/HarvestPolicies.tsx
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
import { type ReactElement } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { t } from "../theme";
|
||||
|
||||
interface YieldMul { food: number; production: number; trade: number; culture: number }
|
||||
interface OneTimeYield { production_base: number; gold_base: number; quality_tier_multiplier: number }
|
||||
interface TerrainTransform { default: string; from_hills?: string; from_jungle?: string; from_swamp?: string }
|
||||
|
||||
interface Policy {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
yield_multiplier: YieldMul;
|
||||
biome_quality_delta_per_turn: number;
|
||||
one_time_yield: OneTimeYield | null;
|
||||
terrain_transform: TerrainTransform | null;
|
||||
fauna_population_delta: number;
|
||||
flora_density_delta: number;
|
||||
applicable_terrains: string[];
|
||||
flavor: string;
|
||||
}
|
||||
|
||||
import policiesJson from "@resources/harvest_policies/policies.json";
|
||||
const POLICIES = policiesJson as readonly Policy[];
|
||||
|
||||
const PageWrap = styled.div`
|
||||
background: ${t.bg.menu};
|
||||
min-height: 100vh;
|
||||
color: ${t.text.primary};
|
||||
font-family: ${t.font.body};
|
||||
padding-bottom: 60px;
|
||||
`;
|
||||
const Header = styled.div`
|
||||
background: ${t.bg.panel};
|
||||
border-bottom: 1px solid ${t.border.panel};
|
||||
padding: 14px 28px;
|
||||
display: flex; align-items: center; gap: 24px;
|
||||
`;
|
||||
const HeaderTitle = styled.div`
|
||||
font-family: ${t.font.heading};
|
||||
font-size: 24px; color: ${t.text.title};
|
||||
letter-spacing: 0.04em;
|
||||
`;
|
||||
const BackLink = styled(Link)`
|
||||
color: ${t.text.muted}; text-decoration: none;
|
||||
font-size: 13px; font-family: ${t.font.mono};
|
||||
&:hover { color: ${t.accent.gold}; }
|
||||
`;
|
||||
const SourceTag = styled.div`
|
||||
margin-left: auto; font-family: ${t.font.mono};
|
||||
font-size: 11px; color: ${t.text.muted};
|
||||
`;
|
||||
const Content = styled.div`
|
||||
max-width: 1200px; margin: 0 auto;
|
||||
padding: 28px 24px;
|
||||
display: flex; flex-direction: column; gap: 24px;
|
||||
`;
|
||||
const Intro = styled.div`
|
||||
background: ${t.bg.panel};
|
||||
border: 1px solid ${t.border.panel};
|
||||
border-radius: ${t.radius.panel};
|
||||
padding: 14px 18px;
|
||||
font-size: 13px; color: ${t.text.secondary};
|
||||
line-height: 1.6;
|
||||
& strong { color: ${t.accent.gold}; }
|
||||
& code { color: ${t.accent.science}; font-family: ${t.font.mono}; font-size: 12px; }
|
||||
`;
|
||||
const Grid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 14px;
|
||||
`;
|
||||
const Card = styled.div<{ $color: string }>`
|
||||
background: ${t.bg.panel};
|
||||
border: 1px solid ${p => p.$color + "55"};
|
||||
border-top: 4px solid ${p => p.$color};
|
||||
border-radius: ${t.radius.panel};
|
||||
padding: 14px 16px;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
`;
|
||||
const CardHead = styled.div`
|
||||
display: flex; align-items: baseline; gap: 10px;
|
||||
border-bottom: 1px solid ${t.border.divider};
|
||||
padding-bottom: 8px;
|
||||
`;
|
||||
const Icon = styled.div<{ $color: string }>`
|
||||
font-size: 28px; color: ${p => p.$color};
|
||||
`;
|
||||
const Name = styled.div`
|
||||
flex: 1;
|
||||
font-family: ${t.font.heading};
|
||||
font-size: 16px;
|
||||
color: ${t.text.primary};
|
||||
`;
|
||||
const Desc = styled.div`
|
||||
font-size: 12px; color: ${t.text.secondary};
|
||||
line-height: 1.55;
|
||||
`;
|
||||
const Flavor = styled.div`
|
||||
font-size: 11px; color: ${t.text.muted};
|
||||
font-style: italic; line-height: 1.5;
|
||||
border-left: 2px solid ${t.border.divider};
|
||||
padding-left: 8px;
|
||||
`;
|
||||
const Stats = styled.div`
|
||||
display: flex; flex-wrap: wrap; gap: 6px;
|
||||
`;
|
||||
const Stat = styled.span<{ $color: string }>`
|
||||
display: inline-flex; align-items: baseline; gap: 4px;
|
||||
padding: 2px 8px; border-radius: 8px;
|
||||
font-size: 10px; font-family: ${t.font.mono};
|
||||
background: ${p => p.$color + "22"};
|
||||
color: ${p => p.$color};
|
||||
border: 1px solid ${p => p.$color + "55"};
|
||||
`;
|
||||
const Section = styled.div`
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
`;
|
||||
const Sub = styled.div`
|
||||
font-size: 10px; font-family: ${t.font.mono};
|
||||
text-transform: uppercase; letter-spacing: 0.06em;
|
||||
color: ${t.text.muted};
|
||||
`;
|
||||
const Header2 = styled.h2`
|
||||
font-family: ${t.font.heading};
|
||||
font-size: 16px; color: ${t.accent.gold};
|
||||
margin: 12px 0 4px;
|
||||
letter-spacing: 0.04em;
|
||||
`;
|
||||
const Table = styled.table`
|
||||
width: 100%; border-collapse: collapse;
|
||||
font-size: 12px; font-family: ${t.font.mono};
|
||||
th, td {
|
||||
padding: 6px 12px; text-align: center;
|
||||
border-bottom: 1px solid ${t.border.divider};
|
||||
}
|
||||
th {
|
||||
color: ${t.text.muted};
|
||||
font-size: 10px; text-transform: uppercase;
|
||||
letter-spacing: 0.06em; font-weight: 600;
|
||||
}
|
||||
td { color: ${t.text.primary}; }
|
||||
th:first-child, td:first-child { text-align: left; }
|
||||
`;
|
||||
|
||||
const fmtMul = (x: number) => x === 1 ? "1×" : x === 0 ? "—" : x.toFixed(2).replace(/\.?0+$/, "") + "×";
|
||||
const fmtDelta = (x: number) => x === 0 ? "0" : (x > 0 ? "+" : "") + x.toFixed(2);
|
||||
|
||||
export function HarvestPoliciesPage(): ReactElement {
|
||||
return (
|
||||
<PageWrap>
|
||||
<Header>
|
||||
<BackLink to="/">← back</BackLink>
|
||||
<HeaderTitle>Harvest Policies</HeaderTitle>
|
||||
<SourceTag>
|
||||
driven by @resources/harvest_policies/policies.json · {POLICIES.length} policies
|
||||
</SourceTag>
|
||||
</Header>
|
||||
|
||||
<Content>
|
||||
<Intro>
|
||||
Each tile worked by a city carries a <strong>harvest policy</strong> — a per-tile setting
|
||||
that controls how aggressively citizens extract from the land. Civ5 had only one option
|
||||
(work the tile, or chop it once). We have <strong>four</strong>, and the choice ripples
|
||||
forward through ecology dynamics: a tile under <code>extract_high</code> for too many
|
||||
turns degrades; a tile under <code>replenish</code> recovers; a tile chopped via
|
||||
<code> remove_chop</code> gives one burst of production and is gone.
|
||||
<br /><br />
|
||||
Policies are assigned in the City Screen and persist until changed. The simulator
|
||||
(<code>mc-ecology::dynamics</code>) reads <code>biome_quality_delta_per_turn</code> +
|
||||
<code> fauna_population_delta</code> + <code>flora_density_delta</code> per active policy
|
||||
and applies them per turn to the tile state.
|
||||
</Intro>
|
||||
|
||||
<Header2>The Four Policies</Header2>
|
||||
<Grid>
|
||||
{POLICIES.map(p => (
|
||||
<Card key={p.id} $color={p.color}>
|
||||
<CardHead>
|
||||
<Icon $color={p.color}>{p.icon}</Icon>
|
||||
<Name>{p.name}</Name>
|
||||
</CardHead>
|
||||
<Desc>{p.description}</Desc>
|
||||
|
||||
<Section>
|
||||
<Sub>yield multiplier</Sub>
|
||||
<Stats>
|
||||
<Stat $color="#7cd9a0">🌾 food {fmtMul(p.yield_multiplier.food)}</Stat>
|
||||
<Stat $color="#e69933">⚒ prod {fmtMul(p.yield_multiplier.production)}</Stat>
|
||||
<Stat $color="#ccbf73">🪙 trade {fmtMul(p.yield_multiplier.trade)}</Stat>
|
||||
<Stat $color="#a07cc9">✦ culture {fmtMul(p.yield_multiplier.culture)}</Stat>
|
||||
</Stats>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<Sub>per-turn ecology delta</Sub>
|
||||
<Stats>
|
||||
<Stat $color={p.biome_quality_delta_per_turn >= 0 ? "#7cd9a0" : "#d95940"}>
|
||||
biome quality {fmtDelta(p.biome_quality_delta_per_turn)}
|
||||
</Stat>
|
||||
<Stat $color={p.flora_density_delta >= 0 ? "#7cd9a0" : "#d95940"}>
|
||||
flora {fmtDelta(p.flora_density_delta)}
|
||||
</Stat>
|
||||
<Stat $color={p.fauna_population_delta >= 0 ? "#7cd9a0" : "#d95940"}>
|
||||
fauna {fmtDelta(p.fauna_population_delta)}
|
||||
</Stat>
|
||||
</Stats>
|
||||
</Section>
|
||||
|
||||
{p.one_time_yield && (
|
||||
<Section>
|
||||
<Sub>one-time yield (consumes tile)</Sub>
|
||||
<Stats>
|
||||
<Stat $color="#e69933">⚒ +{p.one_time_yield.production_base} prod (× tier)</Stat>
|
||||
<Stat $color="#ccbf73">🪙 +{p.one_time_yield.gold_base} gold (× tier)</Stat>
|
||||
</Stats>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{p.terrain_transform && (
|
||||
<Section>
|
||||
<Sub>terrain transform on apply</Sub>
|
||||
<Stats>
|
||||
<Stat $color="#888">→ {p.terrain_transform.default}</Stat>
|
||||
{p.terrain_transform.from_hills && <Stat $color="#888">hills → {p.terrain_transform.from_hills}</Stat>}
|
||||
{p.terrain_transform.from_jungle && <Stat $color="#888">jungle → {p.terrain_transform.from_jungle}</Stat>}
|
||||
{p.terrain_transform.from_swamp && <Stat $color="#888">swamp → {p.terrain_transform.from_swamp}</Stat>}
|
||||
</Stats>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Section>
|
||||
<Sub>applies to {p.applicable_terrains.length} terrain types</Sub>
|
||||
<Stats>
|
||||
{p.applicable_terrains.slice(0, 6).map(tr => <Stat key={tr} $color="#888">{tr}</Stat>)}
|
||||
{p.applicable_terrains.length > 6 && <Stat $color="#888">+{p.applicable_terrains.length - 6} more</Stat>}
|
||||
</Stats>
|
||||
</Section>
|
||||
|
||||
<Flavor>"{p.flavor}"</Flavor>
|
||||
</Card>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
<Header2>Comparison Matrix</Header2>
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Policy</th>
|
||||
<th>food ×</th>
|
||||
<th>prod ×</th>
|
||||
<th>biome Δ/turn</th>
|
||||
<th>flora Δ</th>
|
||||
<th>fauna Δ</th>
|
||||
<th>one-time</th>
|
||||
<th>transforms?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{POLICIES.map(p => (
|
||||
<tr key={p.id}>
|
||||
<td style={{ color: p.color, fontWeight: 600 }}>{p.icon} {p.name}</td>
|
||||
<td>{fmtMul(p.yield_multiplier.food)}</td>
|
||||
<td>{fmtMul(p.yield_multiplier.production)}</td>
|
||||
<td style={{ color: p.biome_quality_delta_per_turn === 0 ? "#888" : (p.biome_quality_delta_per_turn > 0 ? "#7cd9a0" : "#d95940") }}>
|
||||
{fmtDelta(p.biome_quality_delta_per_turn)}
|
||||
</td>
|
||||
<td>{fmtDelta(p.flora_density_delta)}</td>
|
||||
<td>{fmtDelta(p.fauna_population_delta)}</td>
|
||||
<td>{p.one_time_yield ? `+${p.one_time_yield.production_base}p +${p.one_time_yield.gold_base}g` : "—"}</td>
|
||||
<td>{p.terrain_transform ? `→ ${p.terrain_transform.default}` : "—"}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
<Header2>Strategic Use</Header2>
|
||||
<Intro>
|
||||
<strong>Replenishment</strong> is the long game — a tile under replenish for ~10 turns
|
||||
recovers fully and becomes higher-tier than baseline. Pair with the <em>Outsider's Parley</em>
|
||||
policy to keep neighboring freepeople from raiding (over-extraction shifts them toward
|
||||
raiding per the ecology coupling rules).
|
||||
<br /><br />
|
||||
<strong>Sustainable</strong> is the dwarven default. No ecology consequences. Most tiles run
|
||||
sustainable by default unless deliberately changed.
|
||||
<br /><br />
|
||||
<strong>High Extraction</strong> is the war economy lever. Pair with culture policy
|
||||
<em> war_economy</em> (statecraft e8) for mobilization; expect biome degradation and possible
|
||||
freepeople hostility within 5–10 turns.
|
||||
<br /><br />
|
||||
<strong>Removal</strong> is the Civ5 chop — a one-time burst that converts the tile to
|
||||
grassland (or plains, if hilly). Use to start a high-value city in a forest, or to free up
|
||||
adjacency for a wonder. Cannot be reversed without the <code>reforestation</code> improvement.
|
||||
</Intro>
|
||||
</Content>
|
||||
</PageWrap>
|
||||
);
|
||||
}
|
||||
|
|
@ -120,6 +120,7 @@ const routeCategories: RouteCategory[] = [
|
|||
{ path: "/freepeople-lifecycle", label: "❀ Freepeople Lifecycle — wanderers → camps → city-states; tribute diplomacy" },
|
||||
{ path: "/terrain-ecology", label: "🌳 Terrain & Ecology — terrain ↔ flora ↔ fauna ↔ wild creatures, grudge gate" },
|
||||
{ path: "/food-web", label: "🍃 Food Web — trophic pyramid, prey chains, carrying capacity, ecology coupling" },
|
||||
{ path: "/harvest-policies", label: "🌾 Harvest Policies — per-tile extraction settings, replenish/sustain/high/chop" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"root":["./src/app.tsx","./src/audiosynth.ts","./src/main.tsx","./src/theme.ts","./src/components/combat/combatantcard.tsx","./src/components/combat/damagematrix.tsx","./src/components/combat/hpafterbar.tsx","./src/components/combat/hpslider.tsx","./src/components/combat/kwbanner.tsx","./src/components/combat/modifierlist.tsx","./src/components/combat/probabilitybar.tsx","./src/components/combat/unitbrowser.tsx","./src/components/combat/unitrow.tsx","./src/components/tree/treeview.tsx","./src/components/tree/types.ts","./src/components/ui/button.tsx","./src/components/ui/panel.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tag.tsx","./src/data/allbuildings.ts","./src/data/allunits.ts","./src/data/audiopacks.ts","./src/data/scenarios.ts","./src/data/units.ts","./src/pages/audiopackdetail.tsx","./src/pages/audiopacks.tsx","./src/pages/audiosystem.tsx","./src/pages/borders.tsx","./src/pages/buildingtrees.tsx","./src/pages/cityscreen.tsx","./src/pages/civilopedia.tsx","./src/pages/clanpersonality.tsx","./src/pages/combatcalculator.tsx","./src/pages/combatpreview.tsx","./src/pages/credits.tsx","./src/pages/culturetree.tsx","./src/pages/designgallery.tsx","./src/pages/diplomacy.tsx","./src/pages/empiredashboard.tsx","./src/pages/endgamesummary.tsx","./src/pages/eraprogression.tsx","./src/pages/foodweb.tsx","./src/pages/freepeoplelifecycle.tsx","./src/pages/gamesetup.tsx","./src/pages/gdrustbridge.tsx","./src/pages/gdrustmap.tsx","./src/pages/greatpeople.tsx","./src/pages/greatworks.tsx","./src/pages/hexformation.tsx","./src/pages/hud.tsx","./src/pages/index.tsx","./src/pages/mainmenu.tsx","./src/pages/notifications.tsx","./src/pages/pastgames.tsx","./src/pages/permutations.tsx","./src/pages/promotionpicker.tsx","./src/pages/replay.tsx","./src/pages/settings.tsx","./src/pages/specialists.tsx","./src/pages/statistics.tsx","./src/pages/techtree.tsx","./src/pages/terrainecology.tsx","./src/pages/throneroom.tsx","./src/pages/turnsummary.tsx","./src/pages/unitactions.tsx","./src/pages/worldgen.tsx","./src/pages/hud/minimap.tsx","./src/pages/hud/chrome.ts","./src/pages/hud/positions.ts","./src/pages/worldgen/biometransitions.tsx","./src/pages/worldgen/climate.tsx","./src/pages/worldgen/ecology.tsx","./src/pages/worldgen/hydrology.tsx","./src/pages/worldgen/lab.tsx","./src/pages/worldgen/mappanel.tsx","./src/pages/worldgen/noiseanatomy.tsx","./src/pages/worldgen/pipelinepanel.tsx","./src/pages/worldgen/presets.tsx","./src/pages/worldgen/rng.tsx","./src/pages/worldgen/substrate.tsx","./src/pages/worldgen/tectonics.tsx","./src/pages/worldgen/terraincatalog.tsx","./src/pages/worldgen/whittakerplot.tsx","./src/pages/worldgen/_layerpage.tsx","./src/pages/worldgen/lab/mapcanvas.tsx","./src/pages/worldgen/lab/overlaytoggles.tsx","./src/pages/worldgen/lab/presetbar.tsx","./src/pages/worldgen/lab/tileinspector.tsx","./src/pages/worldgen/lab/constants.ts","./src/pages/worldgen/lab/mapinteractions.ts","./src/pages/worldgen/lab/observations.ts","./src/pages/worldgen/lab/render.ts","./src/pages/worldgen/lab/types.ts","./src/utils/combatcalc.ts","./src/utils/wasm/smoke.ts","./src/utils/wasm/usewasmgrid.ts","./src/utils/worldgen/hexcanvas.test.ts","./src/utils/worldgen/hexcanvas.ts","./src/utils/worldgen/indicatordecorations.ts","./src/utils/worldgen/noise.ts","./src/utils/worldgen/poisson.ts","./src/utils/worldgen/terrain.ts","../../reports/gd-rust-relationships.json","../../../public/resources/audio/library.json","../../../public/games/age-of-dwarves/data/audio/manifest.json","../../../public/games/age-of-dwarves/data/audio/pools.json"],"version":"5.9.3"}
|
||||
{"root":["./src/app.tsx","./src/audiosynth.ts","./src/main.tsx","./src/theme.ts","./src/components/combat/combatantcard.tsx","./src/components/combat/damagematrix.tsx","./src/components/combat/hpafterbar.tsx","./src/components/combat/hpslider.tsx","./src/components/combat/kwbanner.tsx","./src/components/combat/modifierlist.tsx","./src/components/combat/probabilitybar.tsx","./src/components/combat/unitbrowser.tsx","./src/components/combat/unitrow.tsx","./src/components/tree/treeview.tsx","./src/components/tree/types.ts","./src/components/ui/button.tsx","./src/components/ui/panel.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tag.tsx","./src/data/allbuildings.ts","./src/data/allunits.ts","./src/data/audiopacks.ts","./src/data/scenarios.ts","./src/data/units.ts","./src/pages/audiopackdetail.tsx","./src/pages/audiopacks.tsx","./src/pages/audiosystem.tsx","./src/pages/borders.tsx","./src/pages/buildingtrees.tsx","./src/pages/cityscreen.tsx","./src/pages/civilopedia.tsx","./src/pages/clanpersonality.tsx","./src/pages/combatcalculator.tsx","./src/pages/combatpreview.tsx","./src/pages/credits.tsx","./src/pages/culturetree.tsx","./src/pages/designgallery.tsx","./src/pages/diplomacy.tsx","./src/pages/empiredashboard.tsx","./src/pages/endgamesummary.tsx","./src/pages/eraprogression.tsx","./src/pages/foodweb.tsx","./src/pages/freepeoplelifecycle.tsx","./src/pages/gamesetup.tsx","./src/pages/gdrustbridge.tsx","./src/pages/gdrustmap.tsx","./src/pages/greatpeople.tsx","./src/pages/greatworks.tsx","./src/pages/harvestpolicies.tsx","./src/pages/hexformation.tsx","./src/pages/hud.tsx","./src/pages/index.tsx","./src/pages/mainmenu.tsx","./src/pages/notifications.tsx","./src/pages/pastgames.tsx","./src/pages/permutations.tsx","./src/pages/promotionpicker.tsx","./src/pages/replay.tsx","./src/pages/settings.tsx","./src/pages/specialists.tsx","./src/pages/statistics.tsx","./src/pages/techtree.tsx","./src/pages/terrainecology.tsx","./src/pages/throneroom.tsx","./src/pages/turnsummary.tsx","./src/pages/unitactions.tsx","./src/pages/worldgen.tsx","./src/pages/hud/minimap.tsx","./src/pages/hud/chrome.ts","./src/pages/hud/positions.ts","./src/pages/worldgen/biometransitions.tsx","./src/pages/worldgen/climate.tsx","./src/pages/worldgen/ecology.tsx","./src/pages/worldgen/hydrology.tsx","./src/pages/worldgen/lab.tsx","./src/pages/worldgen/mappanel.tsx","./src/pages/worldgen/noiseanatomy.tsx","./src/pages/worldgen/pipelinepanel.tsx","./src/pages/worldgen/presets.tsx","./src/pages/worldgen/rng.tsx","./src/pages/worldgen/substrate.tsx","./src/pages/worldgen/tectonics.tsx","./src/pages/worldgen/terraincatalog.tsx","./src/pages/worldgen/whittakerplot.tsx","./src/pages/worldgen/_layerpage.tsx","./src/pages/worldgen/lab/mapcanvas.tsx","./src/pages/worldgen/lab/overlaytoggles.tsx","./src/pages/worldgen/lab/presetbar.tsx","./src/pages/worldgen/lab/tileinspector.tsx","./src/pages/worldgen/lab/constants.ts","./src/pages/worldgen/lab/mapinteractions.ts","./src/pages/worldgen/lab/observations.ts","./src/pages/worldgen/lab/render.ts","./src/pages/worldgen/lab/types.ts","./src/utils/combatcalc.ts","./src/utils/wasm/smoke.ts","./src/utils/wasm/usewasmgrid.ts","./src/utils/worldgen/hexcanvas.test.ts","./src/utils/worldgen/hexcanvas.ts","./src/utils/worldgen/indicatordecorations.ts","./src/utils/worldgen/noise.ts","./src/utils/worldgen/poisson.ts","./src/utils/worldgen/terrain.ts","../../reports/gd-rust-relationships.json","../../../public/resources/audio/library.json","../../../public/games/age-of-dwarves/data/audio/manifest.json","../../../public/games/age-of-dwarves/data/audio/pools.json"],"version":"5.9.3"}
|
||||
183
public/resources/harvest_policies/policies.json
Normal file
183
public/resources/harvest_policies/policies.json
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
[
|
||||
{
|
||||
"id": "replenish",
|
||||
"name": "Replenishment",
|
||||
"description": "Tile is set aside for regrowth. Citizens work it lightly — gathering only what falls or what the land freely gives. Yields are halved, but the biome heals over time and quality_tier slowly rises toward its terrain ceiling.",
|
||||
"icon": "🌱",
|
||||
"color": "#7cd9a0",
|
||||
"yield_multiplier": {
|
||||
"food": 0.5,
|
||||
"production": 0.5,
|
||||
"trade": 0.5,
|
||||
"culture": 1
|
||||
},
|
||||
"biome_quality_delta_per_turn": 0.1,
|
||||
"one_time_yield": null,
|
||||
"terrain_transform": null,
|
||||
"fauna_population_delta": 0.05,
|
||||
"flora_density_delta": 0.05,
|
||||
"applicable_terrains": [
|
||||
"forest",
|
||||
"jungle",
|
||||
"boreal_forest",
|
||||
"temperate_rainforest",
|
||||
"tropical_rainforest",
|
||||
"cloud_forest",
|
||||
"grassland",
|
||||
"plains",
|
||||
"savanna",
|
||||
"wooded_foothills",
|
||||
"riverside_forest",
|
||||
"swamp",
|
||||
"bog"
|
||||
],
|
||||
"flavor": "The dwarves who tend the replenishment groves are elders. They do not rush the trees. The trees do not rush them.",
|
||||
"encyclopedia": {
|
||||
"category": "civilization",
|
||||
"entry_type": "harvest_policy",
|
||||
"detail_route": "/harvest-policies",
|
||||
"tags": [
|
||||
"harvest",
|
||||
"sustainability"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "extract_sustainable",
|
||||
"name": "Sustainable Extraction",
|
||||
"description": "Standard yield — citizens work the tile at the rate the land can sustain indefinitely. Biome holds steady; no degradation, no improvement.",
|
||||
"icon": "🪓",
|
||||
"color": "#ccbf73",
|
||||
"yield_multiplier": {
|
||||
"food": 1,
|
||||
"production": 1,
|
||||
"trade": 1,
|
||||
"culture": 1
|
||||
},
|
||||
"biome_quality_delta_per_turn": 0,
|
||||
"one_time_yield": null,
|
||||
"terrain_transform": null,
|
||||
"fauna_population_delta": 0,
|
||||
"flora_density_delta": 0,
|
||||
"applicable_terrains": [
|
||||
"forest",
|
||||
"jungle",
|
||||
"boreal_forest",
|
||||
"temperate_rainforest",
|
||||
"tropical_rainforest",
|
||||
"cloud_forest",
|
||||
"grassland",
|
||||
"plains",
|
||||
"savanna",
|
||||
"hills",
|
||||
"wooded_foothills",
|
||||
"riverside_forest",
|
||||
"swamp",
|
||||
"bog",
|
||||
"mountains",
|
||||
"tundra",
|
||||
"alpine_meadow",
|
||||
"alpine_tundra"
|
||||
],
|
||||
"flavor": "Take what the land offers. Leave what it needs. This is the dwarven default — older than coinage, older than runes.",
|
||||
"encyclopedia": {
|
||||
"category": "civilization",
|
||||
"entry_type": "harvest_policy",
|
||||
"detail_route": "/harvest-policies",
|
||||
"tags": [
|
||||
"harvest",
|
||||
"default"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "extract_high",
|
||||
"name": "High Extraction",
|
||||
"description": "Aggressive harvesting — +50% yield while the biome lasts, but quality_tier drops over time. Eventually the tile degrades into a lower-yield variant unless policy is changed.",
|
||||
"icon": "⚒",
|
||||
"color": "#e69933",
|
||||
"yield_multiplier": {
|
||||
"food": 1.5,
|
||||
"production": 1.5,
|
||||
"trade": 1.25,
|
||||
"culture": 0.75
|
||||
},
|
||||
"biome_quality_delta_per_turn": -0.05,
|
||||
"one_time_yield": null,
|
||||
"terrain_transform": null,
|
||||
"fauna_population_delta": -0.1,
|
||||
"flora_density_delta": -0.08,
|
||||
"applicable_terrains": [
|
||||
"forest",
|
||||
"jungle",
|
||||
"boreal_forest",
|
||||
"temperate_rainforest",
|
||||
"tropical_rainforest",
|
||||
"grassland",
|
||||
"plains",
|
||||
"savanna",
|
||||
"hills",
|
||||
"wooded_foothills",
|
||||
"mountains",
|
||||
"swamp"
|
||||
],
|
||||
"flavor": "When the front needs a thousand axe-shafts by winter, the deep groves are felled by spring.",
|
||||
"encyclopedia": {
|
||||
"category": "civilization",
|
||||
"entry_type": "harvest_policy",
|
||||
"detail_route": "/harvest-policies",
|
||||
"tags": [
|
||||
"harvest",
|
||||
"war_economy"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "remove_chop",
|
||||
"name": "Removal (Chop)",
|
||||
"description": "One-time conversion — clear-cut the tile for a burst of production and gold scaled to its quality_tier, then transform the terrain into grassland (or plains, if hilly). The Civ5 chop. Cannot be reversed without the Reforestation improvement.",
|
||||
"icon": "🪵",
|
||||
"color": "#d95940",
|
||||
"yield_multiplier": {
|
||||
"food": 0,
|
||||
"production": 0,
|
||||
"trade": 0,
|
||||
"culture": 0
|
||||
},
|
||||
"biome_quality_delta_per_turn": 0,
|
||||
"one_time_yield": {
|
||||
"production_base": 60,
|
||||
"gold_base": 25,
|
||||
"quality_tier_multiplier": 1
|
||||
},
|
||||
"terrain_transform": {
|
||||
"default": "grassland",
|
||||
"from_hills": "plains",
|
||||
"from_jungle": "grassland",
|
||||
"from_swamp": "plains"
|
||||
},
|
||||
"fauna_population_delta": -1,
|
||||
"flora_density_delta": -1,
|
||||
"applicable_terrains": [
|
||||
"forest",
|
||||
"jungle",
|
||||
"boreal_forest",
|
||||
"temperate_rainforest",
|
||||
"tropical_rainforest",
|
||||
"wooded_foothills",
|
||||
"riverside_forest",
|
||||
"swamp",
|
||||
"bog"
|
||||
],
|
||||
"flavor": "The forest gives one final gift, all at once. Then it is gone.",
|
||||
"encyclopedia": {
|
||||
"category": "civilization",
|
||||
"entry_type": "harvest_policy",
|
||||
"detail_route": "/harvest-policies",
|
||||
"tags": [
|
||||
"harvest",
|
||||
"permanent"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue