Economic Map Zones
Tax, property, and growth are segment overlays — axis-aligned zones on the same map layout as terrain and resources. They are not biomes and not terrain surface tiles. Biomes, adjacency, and cultivation live in 38 — Biomes, adjacency & terrain transitions.
Related: 10 — Economy & markets, 23 — Harvest, 08 — Inventory & storage (banks after property), 21 — World content schemas, player ledger (LedgerCategory::Taxes).
1. Summary
Designers draw three overlay kinds in content-admin World → Map layout (primary large-scale editor):
| Overlay | Purpose |
|---|---|
| Tax zones | Copper sink on successful harvest inside the zone |
| Property zones | Crown-owned parcels players can buy/sell; upkeep; reclaim |
| Growth zones | Fertility + crop tags shorten matching plant node respawn |
Layers stay independent: a cell can sit in temperate biome + taxed crown land + fertile growth at once. Sampling uses the same highest z_order wins rule as terrain zones (tie-break: later in the YAML list).
2. Design principles
| Principle | Meaning |
|---|---|
| Overlays, not tiles | Econ rules do not replace terrain_zones; they query (x,y) at harvest / buy / respawn |
| Web Map first | Place, select, drag, delete, and property panels ship in content-admin Map layout; native editor is fine-detail only |
| Soft tax | Broke players still complete harvest; tax deducts what they can pay (no hard block) |
| Growth ≠ biome | Fertility and crop tags are economic/farming rules; climate is a biome layer (38) |
| Reserved farm gates | Growth zones declare allowed_kinds / allowed_biomes for later procgen and cultivate — even before those systems run |
| Weather as multiplier | Respawn uses weather_mult (default 1.0) so weather can land without schema rewire |
3. Shared zone shape
Same rect model as terrain (21):
tax_zones:
- id: mill-road-tax
z_order: 0
rects:
- { x0: 120, y0: 130, x1: 160, y1: 150 }
# fields below vary by kind
Legacy single-rect x0/y0/x1/y1 on the zone may load; prefer rects:.
Overlap: highest z_order among zones of the same overlay family (tax vs tax, growth vs growth). Different families stack independently.
4. Tax zones
4.1 Fields
| Field | Type | Meaning |
|---|---|---|
id |
string | Stable id |
rects |
list | Coverage |
z_order |
int | Stack within tax overlays |
rate_bps |
int | Tax in basis points of harvest copper value (e.g. 500 = 5%) |
flat_copper |
int | Optional flat copper added to the tax bill (default 0) |
label |
string? | Designer / UI label |
Harvest copper value: sum of minted/granted items’ nominal copper (template or instance valuation used elsewhere for ledgers). If valuation is unset, use flat_copper only or skip percentage.
4.2 Runtime
On harvest complete (finish_harvest):
- Resolve
tax_zone_at(x, y). - Compute
bill = flat_copper + floor(harvest_value_copper * rate_bps / 10000). - If the harvester owns an overlapping property parcel (see §5), apply
owner_tax_discount_bpsfrom that property (or a global settings default). - Deduct
min(bill, player_copper)— soft deduct. Ledger entry:LedgerCategory::Taxes. - Never cancel the harvest for insufficient funds.
Workers harvesting for an employer: tax the employer copper when policy says so (same soft rule); otherwise tax the acting character. Default: tax the character that receives the items.
5. Property zones
5.1 Fields
| Field | Type | Meaning |
|---|---|---|
id |
string | Parcel id |
rects |
list | Parcel footprint |
z_order |
int | Stack within property overlays |
crown_price_copper |
int | Buy price from the crown |
max_area_m2 |
float? | Optional cap; reject buy if rect area exceeds |
upkeep_copper_per_day |
int | Upkeep sink (game-day via WorldClock) |
upkeep_grace_days |
int | Days unpaid before reclaim (default e.g. 3) |
owner_tax_discount_bps |
int | Discount on tax bills while owned (e.g. 2500 = 25% off) |
label |
string? | UI label |
Runtime ownership is not stored only in YAML: segment defines the parcel geometry and crown terms; ownership + upkeep arrears live in control-plane / worker persistence keyed by (segment_id, property_zone_id).
5.2 Buy / sell / upkeep
| Action | Behavior |
|---|---|
| Buy from crown | Player in/near parcel, pays crown_price_copper, becomes owner if unowned |
| Sell to crown | Owner sells back at configured fraction of crown price (settings), clears ownership |
| Upkeep | Each game-day tick: charge owner upkeep_copper_per_day (soft or hard per settings; default soft then grace) |
| Reclaim | After upkeep_grace_days with arrears: ownership clears; parcel returns to crown |
Banks / housing static storage (08) may key off owned property later; property zones are the land-claim precursor.
6. Growth zones
6.1 Fields
| Field | Type | Meaning |
|---|---|---|
id |
string | Zone id |
rects |
list | Coverage |
z_order |
int | Stack within growth overlays |
fertility |
float | >= 1.0 shortens respawn (e.g. 1.5 → respawn / 1.5). Clamp to settings min/max |
crop_tags |
string[] | Node or item tags that receive the bonus; empty = all plant nodes in zone |
allowed_kinds |
string[] | Reserved: sim terrain kinds allowed for farm tiles / cultivate (38) |
allowed_biomes |
string[] | Reserved: biome ids that may host this growth overlay’s farming rules |
label |
string? | UI label |
6.2 Respawn formula
At harvest-complete when entering cooldown:
base = node.respawn_ticks
zone = growth_zone_at(node.x, node.y)
if zone matches node (crop_tags empty OR intersection with node/item tags):
effective = base / fertility * weather_mult
else:
effective = base * weather_mult_outside # usually 1.0
until_tick = tick + max(1, ceil(effective))
weather_mult defaults to 1.0 until regional weather exists (38 § weather). Same stub on the growth path avoids a later schema break.
6.3 Crop tags
Resource nodes and/or yield item templates carry crop_tags: [carrot, vegetable, …]. Catalog and Map layout expose tag pickers via /api/catalog.
7. Sampling helpers
Sim provides:
tax_zone_at(segment, x, y) -> Option<&TaxZoneDef>property_zone_at(...)/property_zones_covering(...)for ownership checksgrowth_zone_at(...)
Mirror terrain_zone_at overlap rules within each family.
8. Content-admin Map layout
Required for done criteria (content-admin rule):
| Capability | Detail |
|---|---|
| Draw | Brush or rect tool for tax / property / growth |
| Select / drag / delete | Same interaction patterns as terrain and nodes |
| Property panel | Kind-specific fields above |
| Labels | Two-letter tags + hover titles |
| Validate | Overlap warnings; property area vs max_area_m2; unknown crop tags |
| Save | PUT map-layout → segment YAML |
Native flatland-editor may visualize overlays later; web Map layout is primary for large-scale placement and catalog cross-links.
9. Settings knobs
Under assets/config/server-settings.yaml (names illustrative):
economy_zones:
tax_soft_deduct: true
default_owner_tax_discount_bps: 0
property_sellback_bps: 5000 # 50% of crown price
growth_fertility_min: 1.0
growth_fertility_max: 3.0
weather_mult_default: 1.0
10. Example (starter-plains sketch)
tax_zones:
- id: plains-crossroads-tax
z_order: 0
rate_bps: 400
flat_copper: 0
rects:
- { x0: 118, y0: 128, x1: 155, y1: 148 }
property_zones:
- id: crown-plot-a
z_order: 0
crown_price_copper: 5000
upkeep_copper_per_day: 25
upkeep_grace_days: 3
owner_tax_discount_bps: 2500
rects:
- { x0: 130, y0: 135, x1: 145, y1: 145 }
growth_zones:
- id: mill-fields
z_order: 0
fertility: 1.4
crop_tags: [carrot, potato, vegetable]
allowed_kinds: [grass, tilled]
allowed_biomes: [temperate_plains]
rects:
- { x0: 125, y0: 120, x1: 150, y1: 135 }
11. Exit criteria
- Zones load from segment YAML and appear in Map layout CRUD
- Harvest inside tax zone writes a tax ledger line and soft-deducts copper
- Owned property applies tax discount
- Matching growth zone shortens plant
respawn_ticksby fertility - Crown buy/sell + upkeep grace reclaim work for a sample parcel
weather_multandallowed_kinds/allowed_biomesare present (mult may stay 1.0)