Skip to main content

Valenar - Feature Generation Contract

This document is the authoritative contract for generating, revealing, and using Features inside Valenar Locations.

Status: authoritative contract with a current runtime implementation for the prototype map. Valenar derives deterministic LocationFacts, evaluates Feature.EvaluatePlacement(Location, FeaturePlacementInput) through generated typed profile/query data, applies budgets/spacing/conflicts, and spawns selected Feature entities through generated templates. Future compiler work is to emit the current hand-written Generated/ shape directly from .secs.

Wave R-4 (2026-05-10) brought the runtime implementation in line with the multiplicative score = hardGate * suitability * rarityModifier * seededNoise * spacingPenalty * storyBias formula authored in this doc. Static factors (hardGate, suitability, seededNoise, storyBias = constant 1.0 stub) are computed by FeaturePlacementQuery.Evaluate; dynamic factors (rarityModifier = clamp(1 - used/budget, 0, 1), spacingPenalty = clamp(distance / minSpacing, 0, 1)) are applied by FeaturePlacementSelector at selection time. FeatureFamily was expanded to all 14 families listed below; the seven stub families (Water, Food, Forest, Travel, Shelter, Corruption, Nexus) have no committed templates yet. AvoidanceWeights was folded into SuitabilityWeights as negated entries; the avoidance dictionary is no longer a separate profile field. MinScore thresholds and per-template budget values are still tuned for the prior additive formula and remain open for a future balance-pass wave (see ../implementation/pr-code-sync-audit.md "Feature placement balance pass").

Scope

This contract covers:

  • Feature families.
  • Feature record shape.
  • Existence versus discovery.
  • Placement formulas.
  • Density, rarity, spacing, and budgets.
  • Compatibility and exclusion rules.
  • Starting Nexus exception.
  • Gates, taint, and threat-linked Features.
  • Activity generation.
  • Causal rewards.
  • Site conversion.
  • UX query contract.
  • Validation.

It does not define:

  • final numeric balance
  • individual story scripts
  • MC combat resolution
  • final art or icon treatment
  • district development internals after a Feature becomes a developed Site

Core Rule

Feature existence is generated world state. Discovery state controls what the player sees.

A Location can contain several Features, but most Locations should contain no explicit notable Feature. Do not model a Location as "one thing". The Location is the land unit; Features are notable contents, signs, or facts inside it.

Sites are first-class places inside the Location once content becomes discovered, enterable, exploitable, or developed. Threat is danger, pressure, or hostile presence attached to a Location, Feature, or Site; it is not a second place ontology.

Location facts
-> Feature template eligibility
-> suitability score
-> regional budget
-> spacing and conflict resolution
-> Feature entity
-> discovery state
-> activities and effects

Inputs

Feature generation consumes:

worldSeed
worldDataContentHash
Location records
derived Location classes and tags
pressure fields
route and topology facts
settlement/start constraints
Feature template registry
regional density budgets
story placement rules

The generator must use real Location facts. It must not place Features by independent flat chance per Location.

Outputs

Feature generation outputs Feature records:

Feature
featureId
templateId
locationId
category
placementReasonTags
generatedStrength
quality
discoveryState
handledState
progress
risk
blockers
available interactions
effectIds
siteConversionState

Names and labels can vary by discovery state:

Hidden:
no label

Hinted:
damp reeds

Discovered:
spring

Surveyed:
clean spring, reliable water

Developed:
secured spring site

Feature Families

Feature families and the surrounding category vocabulary are NOT tags in the valenar:tag/<name> sense. The tag layer and its modifier-vs-structural-predicate split, including how tags relate to Feature categories, are documented in ../systems/gd-tags-and-classification.md; the committed tag list lives in ../catalogs/gd-tag-catalog.md.

These are generation families for Feature records, not separate player-facing place ontologies. A hostile or pressure-bearing record can stay a Feature sign, or later feed a Threat Site or a Site with linked threats.

Feature families:

NaturalResource
Water
Food
Forest
Mineral
Travel
Shelter
Ruin
Threat
Corruption
Landmark
Nexus
SettlementOpportunity
Story

Examples:

NaturalResource:
WildGrainPatch, HerbMeadow, GrazingGround

Water:
HiddenSpring, SeasonalStream, ShallowPond, ReedPool

Forest:
ForestStand, OldGrowthGrove, GameTrail, HiddenCopse

Mineral:
OreVein, QuarryFace, ClayBank, CollapsedMine

Travel:
RoadTrace, Ford, OldBridge, CaravanCamp, MountainPass

Ruin:
RuinedMill, StandingStones, FallenWatchtower, ArchiveRuin

Threat:
BanditCamp, AmbushSite, PredatorTrail, DemonNest

Corruption:
BlackAshPatch, BlightedField, MinorFissure, TaintedRunoff

Nexus:
DormantNexus, DamagedWardheart, LeylineAnchor

Corruption family lore link: (See lore exposure: lh-game1-world-hooks.md — Gate scarring/taint fields row)

Nexus family lore link: (See lore exposure: lh-game1-world-hooks.md — Nexus manifestation row, Damaged Nexus row)

Feature Template Shape

Every Feature template should define:

id
family
rarity
minSpacing
maxPerLocation
maxPerProvince
maxPerArea
maxPerRegion
hardRequirements
suitabilityWeights
conflictTags
synergyTags
discoveryDifficulty
surveyDifficulty
interactionDifficulty
defaultDiscoveryState
possibleStates
available interactions
effects
modifiers
rewards
siteConversion

Template data answers:

  • where the Feature can exist
  • how rare it is
  • what it conflicts with
  • what it synergizes with
  • how it is discovered
  • which activities it creates
  • which scopes it can affect
  • whether it can become a Site

Existence Versus Discovery

The full ladder of state labels — FeatureDiscoveryState enum values plus their target intensity ranges — is documented in ../systems/gd-state-axes-and-thresholds.md under "Feature Discovery Axis". This section introduces the ladder informally; the state-axes doc is the normative reference for label ordering and intensity thresholds.

Existence is not knowledge.

exists:
the Feature entity is present in world state

hinted:
the player has a clue, but not the real identity

discovered:
the player knows the Feature category or name

surveyed:
quality, risk, blockers, and main outputs are known

handled:
threat or obstacle is resolved, but site may not be developed

developed:
owned or used as an ongoing Site/improvement

A dungeon, ruin interior, or resource node can begin in this ladder as a clue, Feature, or threat sign. Once it becomes enterable, exploitable, or developed, it should convert to a Site entry.

The Location UI must not show nonexistent Features. It must not show hidden Features as known rows. It may show clues when discovery state permits.

Placement Formula

Use score-and-budget placement:

score(location, template) =
hardGate(location, template)
* suitability(location, template)
* rarityModifier(template)
* seededNoise(worldSeed, locationId, templateId)
* spacingPenalty(location, template)
* storyBias(location, template)

Where:

hardGate:
0 or 1. Required conditions must pass.

suitability:
weighted match between Location facts and template preferences.

rarityModifier:
global, regional, family, and template budget pressure.

seededNoise:
deterministic variation without losing coherence.

spacingPenalty:
prevents unnatural clusters.

storyBias:
forces or encourages Nexus, gates, leyline anchors, ancient roads,
starting-survival resources, and other narrative geography.

Candidate selection:

for each region:
for each Feature family:
apply family density budget
for each template in family:
collect eligible candidates
score candidates
sort by score descending
select candidates while budget remains
enforce min spacing
enforce max Features per Location
enforce conflict rules
record placement reason tags

Do not do this:

for each Location:
roll 5 percent chance for hidden dungeon clue
roll 5 percent chance for ore
roll 5 percent chance for spring

Independent rolls create incoherent worlds at large scale.

Density And Capacity

Most Locations have zero explicit Features.

Suggested capacity:

ordinary Location: 0 Features
mildly interesting Location: 1 Feature
rich, dangerous, strategic Location: 2 Features
rare landmark Location: 3 Features
story Location: 4 Features by override

Capacity formula:

capacityScore =
max(resourceRichness, threat, ancientness, routeImportance, settlementSuitability)
+ landmarkBias
+ seededNoise

Budget layers:

global budget:
maximum count across the whole world

region budget:
keeps macro distribution readable

area budget:
prevents one area from absorbing every rare Feature

province budget:
keeps local play varied

Location capacity:
prevents clutter inside one Location

Compatibility And Exclusion

Conflict examples:

DormantNexus conflicts with duplicate Nexus/Wardheart anchor placement
PristineSpring conflicts with SevereTaintSource unless explicitly corrupted
OpenMarketCrossroads conflicts with DeepWildernessOnly
LargeMineSite requires enough orePotential and terrain support
DenseForestFeature requires enough forestCoverage

Synergy examples:

AncientRoadSegment increases chance of RuinedWatchtower nearby.
RiverMeadow increases chance of GoodFarmland and OldMill.
DemonGate increases chance of DemonTracks, BlightedField, and RefugeeCamp nearby.
OreBearingUpland increases chance of OreVein and CollapsedMine.
ForestEdge increases chance of GameTrail, HiddenCopse, and AmbushGrass.

DemonGate synergy lore link: (See lore exposure: lh-game1-world-hooks.md — Demon gates row, gate scarring/taint fields row)

Synergy must be budgeted and deterministic. It must not create unbounded chains.

Starting Nexus Exception

The starting Nexus is a constrained story Feature, not ordinary random ruin placement.

Rules:

starting Location must contain a dormant or damaged Nexus Feature
starting Nexus starts hidden, dormant, damaged, buried, or unidentified
starting Nexus is revealed through the starting ruin chain
only the starting ruin chain has this guarantee
not every ruin is a Nexus
generic site suitability never unlocks founding by itself

Correct founding path:

find ruin clue
investigate ruin
identify dormant Nexus / Wardheart
stabilize Nexus
unlock Establish the Core
establish the Core once
remove founding activity forever

Gates, Taint, And Pressure Features

Some Features are pressure sources, not rewards.

The pressure itself is the Threat. The Feature record is the sign, hazard source, or partial place information carrying that Threat until a full Site exists.

Examples:

MinorFissure
LesserGate
DemonGate
BlackAshPatch
BlightedField
DemonTracks
CorruptedLeylineTrace

Pressure Features can:

  • spread taint
  • increase threat
  • lower route safety
  • block rest
  • block claim or designation
  • make local activities risky
  • spawn nearby clues
  • escalate if ignored
  • unlock cleansing, assault, containment, or sealing activities

Gate and taint Features must align with World Pressure and Nexus.

Feature State

Recommended state ladder:

Hidden
Hinted
Discovered
Surveyed
Interacting
Handled
Secured
Developed
Depleted
Destroyed

Not every Feature uses every state. The contract requires enough states to support mystery, partial knowledge, local activity, cleanup, ownership, and development.

Site-facing wording, once a Feature converts, should use explicit place states: wild, discovered, secured, active, owned, developed, depleted, sealed, destroyed.

Activity Generation

Activities are generated from Feature state plus broader state:

availableActivity(feature, actor, location) =
feature exists
required Feature state passes
Location knowledge state passes
actor is present or has valid remote access
required tools/resources/act pass
threat blockers pass or the activity handles the threat
route/control requirements pass

Activity families:

Survey
Investigate
Clear
Secure
Harvest
Excavate
Consecrate
Cleanse
Build Outpost
Develop Site
Study
Patrol
Seal
Exploit
Establish the Core

If the MC is not at the Location, local activities require travel or must state why they are unavailable.

Causal Rewards

Feature rewards must come from Feature identity.

Good examples:

RuinedMill:
tools, blueprint, local workshop unlock, restored production site

AncientForgeRuin:
forge upgrade, metalworking recipe, Forge Core clue

CollapsedMine:
ore vein reveal, mining site, stone or metal salvage

WardRuin:
shield knowledge, ward repair project, taint suppression, Nexus clue

ArchiveRuin:
map fragment, lore, research topic, quest chain

DemonFissure:
shard, taint reduction, gate warning, danger escalation if ignored

Bad pattern:

completed Feature -> random global production bonus

Do not replace Feature identity with generic pseudo-channels such as Research Bonus, Morale Bonus, Mineral Bonus, or Defense Bonus.

Prefer explicit feature-local values or explicit target modifiers instead. Examples only: DiscoveryDifficulty, SurveyDifficulty, InteractionDifficulty, Progress, ThreatLevel, LootValue, or named causal effects on the actually affected scope.

Effects And Modifiers

Apply effects to the scope that is actually affected.

Examples:

WildGrainPatch discovered:
reveals food potential and forage activity

WildGrainPatch secured:
adds local FoodOutput or FarmingSuitability modifier

OreVein surveyed:
reveals ore quality and mining activity

OreVein developed:
adds MetalOutput to owning Location, outpost, district, or settlement
through the proper scope

DemonNest active:
increases ThreatLevel
lowers RouteSafety and RestSafety
blocks safe work

DemonNest handled:
removes local threat modifier
may reveal salvage, shard, route safety, or taint source information

Use Location-scoped modifiers first. Promote effects to settlement, province, or region only when ownership, route connection, designation, site development, or story state justifies it.

Site Conversion

Sites and buildings are first-class place content. They are not the same category as generated wild Features, even when a Feature is the discovery path that leads to them.

Resource nodes, ruins, and dungeon leads can begin as Features and convert into Sites when they become enterable, exploitable, defensible, or developed. Threat-heavy places follow the same rule: they become Threat Sites or Sites with linked threats once the place itself is part of play.

Examples:

HiddenSpring -> WaterSite
OreVein -> MineSite
RuinedMill -> RestoredWorkshopSite
WardRuin -> WardSite
DemonFissure -> SealedRiftSite or CleansingSite

Conversion requirements can include:

  • discovery state
  • survey result
  • threat cleared
  • ownership or outpost access
  • workers
  • resources
  • route connection
  • act unlock
  • designation compatibility

Plains Feature Examples

Plains are about food, grazing, visibility, roads, exposure, old travel routes, and pressure.

Possible plains Features:

Food/resource:
WildGrainPatch
GrazingHerd
GoodFarmland
ForagingGround
HerbMeadow

Water:
SeasonalStream
ShallowPond
ReedPool
HiddenSpring

Travel:
RoadTrace
CaravanCamp
MarketCrossroads
OldBoundaryStones

Shelter/defense:
LoneHill
WindbreakCopse
EarthenRise
ExposedApproach

Threat:
AmbushGrass
PredatorTrail
BanditCamp
DemonTracks
BlightedField

History:
StandingStones
BattlefieldRemnant
BuriedRuins
AncientRoadSegment
FallenWatchtower
SealedBarrow

Example WildGrainPatch:

eligible if:
plainsCoverage >= 35
fertility >= 55
corruption <= 50

score =
plainsCoverage * 0.30
+ fertility * 0.45
+ moisture * 0.15
- forestCoverage * 0.10
- corruption * 0.25
+ noise * 0.10

Example RoadTrace:

eligible if:
slopeAvg <= 35
tinyWaterCoverage <= 35
routeImportance >= 40

score =
routeImportance * 0.45
+ plainsCoverage * 0.20
+ roadSuitability * 0.25
- wetlandCoverage * 0.20
- roughness * 0.15
+ noise * 0.10

Example DemonTracks:

eligible if:
demonPressure >= 35
knowledgeState >= Sighted

score =
demonPressure * 0.45
+ corruption * 0.25
+ visibility * 0.10
+ routeImportance * 0.10
+ noise * 0.10

Other Terrain Examples

Deep Forest:

high forestCoverage
medium cover
lower visibility
possible Features:
ForestStand
GameTrail
HiddenCopse
OldShrine
AmbushSigns
WitchPath

Rocky Upland:

high slope and rockyCoverage
lower fertility
higher defensibility
possible Features:
OreVein
QuarryFace
CollapsedMine
WatchHill
CaveMouth
FallenTower

Wet Meadow:

high moisture and fertility
moderate tinyWaterCoverage or water adjacency
possible Features:
SeasonalStream
ReedPool
GoodFarmland
OldMillRace
SoftGround
TaintedRunoff

Blighted Field:

high corruption or corruptedCoverage
lower rest safety
possible Features:
BlackAshPatch
DemonTracks
BlightedCrop
MinorFissure
DeadGrove
CleansingSite

Land-Coastal:

land Location adjacent to water Location
possible Features:
FishingCamp
LandingBeach
CliffPath
OldPier
SmugglerCove
StormWreck

Water-Coast or Water-Inland-Lake:

water Location, not settlement land by default
possible Features:
FishingGround
ShallowReef
FerryRoute
LakeIsland
SunkenRuin
DangerousCurrent

UX Query Contract

The Location UI consumes generated Feature state.

Rules:

  • Do not show nonexistent Features.
  • Do not show hidden Features as known rows.
  • Show hints only when discovery state permits.
  • Group activities by source: Location, Feature, Approach, Threat, Site.
  • Use site-first wording for enterable, exploitable, or developable places.
  • Dungeons surface through Site entries once discovered or enterable; before then only clues, Features, or threat signs show.
  • Feature panels open inside the Location workspace.
  • Feature detail panels show status, known facts, unknowns, progress, risks, blockers, available activities, possible outcomes, and effects on Location.

Validation

Feature validation must check:

every Feature has a valid Location
every Feature template id exists
every selected Feature passes hard requirements
Feature count does not exceed Location capacity
rare Features respect spacing
family and regional budgets hold
incompatible Features do not coexist
synergy placements are deterministic and bounded
story-required Features exist
starting Nexus Feature exists
no visible Feature is Hidden
no activity references a missing Feature, Site, or Location
all modifiers target valid scopes
all rewards are causal to Feature identity

Runtime Migration Direction

Required direction:

1. Extend FeatureData beyond binary discovered/cleared/hostile state.
2. Replace random Feature sprinkling with score-and-budget placement.
3. Store placement reason tags and generated strength/quality.
4. Generate discovery state from Location knowledge and Feature difficulty.
5. Wire OnDiscovered, OnInteract, OnCleared, and later state transitions.
6. Replace hard-coded Act 0 feature rows with snapshot-backed Features and
hints.
7. Enforce Nexus-gated founding through runtime state.

The current templates for ruins, dungeon-linked clues and sites, ore veins, shrines, groves, and similar world content remain useful as content seeds, but their placement and lifecycle must be driven by this contract.