Skip to main content

Progression

Progression

Status: IN PROGRESS — Tracked open issues; see Known Issues section

Last updated: October 7, 2026

Lineage -- Progression & Victory System Design

Status: IN PROGRESS — Tracked open issues; see Known Issues section

Progression in Lineage is driven by turn-based advancement: each turn processes resource production, dynasty events, random occurrences, AI decisions, and victory checks. The dynasty system adds generational persistence through ruler succession, trait inheritance, and marriage alliances. Victory is achieved through one of 5 distinct conditions.

Turn Processing Pipeline

Source: ,

Global World Turn Schedule

Turn trigger: Every Sunday at midnight UTC
Turn number: (currentTimeMs / MILLIS_PER_WEEK) starting from epoch
Season: derived from calendar month
  WINTER = Dec, Jan, Feb
  SPRING = Mar, Apr, May
  SUMMER = Jun, Jul, Aug
  AUTUMN = Sep, Oct, Nov

Source:

Constant Value
GLOBAL_REGION_SIZE 50 (50x50 grid per region)
BATCH_SIZE 100 kingdoms per batch
MILLIS_PER_WEEK 604,800,000

Per-Kingdom Turn Processing Order

Source:

1. Worker-based production
   For each resource in workerAssignments:
     kingdom.resources[resource] += workerCount * 5

2. Territory-based production (Global mode only)
   For each owned territory:
     production = calculateResourceProduction(territory)
     kingdom.addResources(production)

3. Kingdom bonus production
   For each KingdomBonus with *_production keys:
     kingdom.resources[resource] += bonusAmount

4. Mana regeneration
   manaPool.currentMana = min(current + regen, maxMana)

5. Process AI turn (if AI-controlled)
   AI personality-driven decisions: build, recruit, diplomacy, war

6. Random events (10% chance per turn)
   See Random Events section below

7. Dynasty events
   Ruler aging, health checks, heir births, succession

8. Treaty expiration check
   Expire any treaties past their duration

9. Victory condition check
   Evaluate all 5 victory conditions

10. Save kingdom state

Local/Multiplayer Turn Processing

Source: (SP), (MP)

Local/SP and Multiplayer modes now have near-complete feature parity with Global. The endTurn() handler runs:

1. Worker-based production (calculateProductionRates)
2. Territory/building-based production (calculateTotalProduction + calculateTerritoryIncome)
3. Empire maintenance deduction (enhancement upkeep + sprawl)
4. Army upkeep deduction (gold, with morale penalty on deficit)
5. Food consumption (population/3 via FOOD_CONSUMPTION_DIVISOR=3, with surplus growth and famine escalation)
6. Research progress advancement (10 base + bonuses per turn)
7. Random events (10% chance per turn)
8. Dynasty events (ruler aging, heir births, succession)
9. AI kingdom turns (processAllAITurns)
10. Army movement reset
11. Victory condition checks (all 5 types + milestone awards)

Multiplayer () additionally processes:

  • All kingdoms in the shared game map (not just the player's)
  • Treaty expirations, expired events
  • Victory checks and eliminations at the map level

Victory Conditions

Source:

5 Victory Types (Updated Thresholds)

The code has been significantly revised from the original design. Current thresholds:

Victory Type Condition Threshold (Code) Original Design
DOMINATION Own >= 50% of all territories 50% Was 75%
ECONOMIC Accumulate >= 100,000 gold 100k gold Was 500k + 20% territory
DIPLOMATIC Allied with >= 3 kingdoms (fixed count) 3 alliances Was 50% of kingdoms
TECHNOLOGICAL Research ALL 5 legendary technologies LEGENDARY_WARFARE, LEGENDARY_MAGIC, LEGENDARY_ECONOMY, LEGENDARY_DIPLOMACY, LEGENDARY_CONSTRUCTION Was "5 legendary techs"
MAGICAL Cast the TIME_STOP spell (checked via spellCasts history) SpellCast with status=COMPLETED Was kingdom flag

Victory Check Flow

Source:

Pre-checks:
  - Skip if gameMapData/world is null
  - Skip if world.isCompleted == true (game already over)
  - If only 1 active (non-eliminated) kingdom remains: automatic DOMINATION win

For each active kingdom:
  1. Check DOMINATION:
     ownedTerritories / totalTerritories >= 0.50

  2. Check ECONOMIC:
     kingdom.gold >= 100,000

  3. Check DIPLOMATIC:
     Count active ALLIANCE or MILITARY_ALLIANCE treaties involving kingdom
     allianceCount >= 3

  4. Check TECHNOLOGICAL:
     kingdom.discoveredTechnologies.containsAll(LEGENDARY_TECHNOLOGIES)
     All 5 required: LEGENDARY_WARFARE, LEGENDARY_MAGIC, LEGENDARY_ECONOMY,
     LEGENDARY_DIPLOMACY, LEGENDARY_CONSTRUCTION

  5. Check MAGICAL:
     Check spellCasts list for entry where:
       kingdomId matches AND spellId == "TIME_STOP" AND status == "COMPLETED"

If multiple kingdoms achieve victory simultaneously: resolve via resolveTie()

Victory Priority

Conditions are checked per-kingdom in order: DOMINATION, ECONOMIC, DIPLOMATIC, TECHNOLOGICAL, MAGICAL. First match for that kingdom wins. If multiple kingdoms win simultaneously, tie resolution applies.

Tie Resolution

Source:

When multiple kingdoms achieve victory conditions simultaneously:

1. Territory count (most territories wins)
2. Gold reserves (highest gold wins)
3. Morale (highest kingdom morale wins)
4. Alphabetical by kingdom ID (deterministic fallback)

Elimination

Source: , processEliminations

A kingdom is eliminated when:
  ownedTerritories == 0 AND totalArmyUnits == 0
  (armies checked via isEmpty() method — 0 total units)

processEliminations() iterates all non-eliminated kingdoms and flags newly eliminated ones.
Eliminated kingdoms are flagged (isEliminated=true) and skip further turn processing.

Victory Progress Tracking

Source:

Returns a map of victory type to VictoryProgress(current, required, description):

DOMINATION: current=ownedTerritories, required=ceil(totalTerritories * 0.50)
ECONOMIC: current=gold, required=100000
DIPLOMATIC: current=allianceCount, required=3
TECHNOLOGICAL: current=legendaryTechCount, required=5
MAGICAL: 4-step incremental progress (0/1/2/3/4 out of 4):
  0: no BASIC_MAGIC
  1: BASIC_MAGIC researched
  2: TIME_MAGIC researched
  3: TIME_MAGIC researched AND mana > 0 (prereq: step 2 must be complete)
  4: TIME_STOP cast successfully

The magical victory progress implements the incremental UX fix from the original design decision.

Mid-Game Victory Milestones

Source:

Milestones reward pursuit of non-economic victory paths. Each is awarded once (tracked via kingdom.awardedMilestones):

Milestone Trigger Reward
DOMINATION 25% 25% progress toward 50% territory goal +500 gold
DOMINATION 50% 50% progress toward 50% territory goal +750 gold, +5 morale
DIPLOMATIC 1st 1st alliance formed +300 gold, +5 morale
DIPLOMATIC 2nd 2nd alliance formed +400 gold, +5 morale
TECH 1st First technology discovered +200 gold
MAGIC attuned BASIC_MAGIC researched +50 mana

Narration cap (BL-03, 2026-07-17): if more than TURN_EVENT_NARRATION_CAP (2) milestones fire in the same turn, only the first 2 speak their full reward sentence; the rest roll into one summary line ("N more milestones reached: ...") via 's milestoneTitle() helper. Every milestone's reward is still applied in full regardless of narration order -- only the SPOKEN sentence is capped. Mirrors the existing story-beat / loyalty-transition narration cap in the same method (added to prevent end-turn response length scaling unbounded with a large kingdom; see / report 260).

Where Victory Checks Run

Mode Where Source
Single Player After all turn processing
Multiplayer line ~329 After resource/dynasty processing
Global World Not currently called per-kingdom

Victory checks now run in all three modes via the handler-level endTurn() and multiplayer service.


Dynasty System

Source:

Dynasty Initialization

Source:

Create ruler:
  name = randomly generated
  age = 30 to 44 inclusive  // 30 + (int)(Math.random() * 15)
  health = HEALTHY
  stats: leadership, warfare, diplomacy, magic = 50 each
  traits = [] (empty initially)

Create dynasty:
  dynastyName = kingdom name + " Dynasty"
  founder = ruler
  currentRuler = ruler
  heirs = []
  generation = 1
  history = []

Ruler Model

Source: LineageRuler

Field Type Range
name String Generated
age Integer 30-44 inclusive (starting)
health String HEALTHY, ILL, DYING
leadership Integer 0-100
warfare Integer 0-100
diplomacy Integer 0-100
magic Integer 0-100
traits List DynastyTrait names

Ruler Aging & Death

Source: LineageRuler.ageOneYear, LineageRuler.shouldDieNaturally

age += 1

Health transitions (ageOneYear):
  If age > 60 AND health == HEALTHY:
    random() < 0.05 * (age - 60) -> health = ILL
    (e.g., age 62 = 10% chance; age 70 = 50% chance)

  If age > 70 AND health == ILL:
    random() < 0.10 * (age - 70) -> health = DYING
    (e.g., age 73 = 30% chance; age 75 = 50% chance)

Death checks (shouldDieNaturally):
  If health == DYING:
    25% chance per turn -> ruler DIES
  Else if age > 80:
    random() < 0.05 * (age - 80) -> ruler DIES naturally
    (e.g., age 85 = 25% chance; age 90 = 50% chance)

Death triggers succession.

Cross-ref: index.md § Ruler Aging & Death table.

Effective Stats (Trait Bonuses)

Source: LineageRuler.getEffectiveStats

For each stat (leadership, warfare, diplomacy, magic):
  effectiveStat = baseStat + SUM(trait bonuses for this stat)
  effectiveStat = clamp(effectiveStat, 0, 100)

Trait bonuses are defined per trait in the DynastyTrait definitions (see Dynasty Traits section).


Heir System

Source:

Heir Generation

Source:

1. Check fertility:
   - Ruler must have a spouse
   - Spouse fertility check (see Spouse Fertility)
   - 25% base trigger chance per turn, layered over a guaranteed pace (see Heir Birth Pacing)

2. Generate heir stats:
   For each stat (leadership, warfare, diplomacy, magic):
     baseStat = (rulerStat + spouseStat) / 2
     variance = random(-10, +10)
     heirStat = clamp(baseStat + variance, 20, 80)

3. Generate heir traits:
   For each parent trait: DynastyTrait.rollInheritance()
   Bloodline / isBloodline ALWAYS inherit; else STRONG 70% / AVERAGE 50% / WEAK 40%

Heir Birth Pacing (findings 900 + 913)

Source: + generateGuaranteedHeir

Heirs arrive on a per-turn RNG chance (HEIR_BIRTH_TRIGGER_CHANCE = 0.25, times the consort's 2-40% fertility) layered over a guaranteed pace, so a reign reliably yields SEVERAL heirs rather than the single guaranteed one the original finding-900 floor produced. The pace: a fertile marriage is guaranteed a new child every HEIR_GUARANTEE_TURNS (=4) turns — measured from the last birth, or the marriage when there are no children yet — until the consort has borne its rolled targetFamilySize (2-4; HEIR_TARGET_FAMILY_SIZE = 3 is only the fallback default). RNG births can push beyond the target up to DIRECT_HEIR_CAP (=5) living direct children. The guaranteed path (generateGuaranteedHeir) bypasses the fertility roll but STILL requires a fertile consort — an aged or absent consort remains barren, so a neglected line can still end (the intended stakes). LineageSpouse.lastBirthTurn stamps each birth so the pace measures from it.

Constant Value Meaning
HEIR_GUARANTEE_TURNS 4 Turns between guaranteed births (since last birth, or marriage if none yet). Default only — every marriage rolls its own guaranteeTurns in GUARANTEE_TURNS_MIN-GUARANTEE_TURNS_MAX = 3-5.
HEIR_TARGET_FAMILY_SIZE 3 Guaranteed-paced children per consort before the pace stops. Default only — every marriage rolls its own targetFamilySize in TARGET_FAMILY_SIZE_MIN-TARGET_FAMILY_SIZE_MAX = 2-4, so the guaranteed floor a reign can rely on is 2, not 3.
HEIR_BIRTH_TRIGGER_CHANCE 0.25 Per-turn RNG trigger, layered over the pace
DIRECT_HEIR_CAP 5 Living-heir cap for the ruler's own direct children

Two caps, not one: players report both "capped at 6" and a garbled "63". There is no 63 constant. The intentional pair is DIRECT_HEIR_CAP (5, ruler's own children) and HEIR_ROSTER_SOFT_CAP (12 after 2026-07-31; was 6) for the whole living roster including cadets. Paced target family size (~3) is a birth-pace target, not a hard cap. Unlimited is rejected (voice UX).

Legibility is paired with the pace: view heirs and the return briefing state the actual gate (a young consort produces heirs within a few turns; an aged consort ends the line) instead of a vague "heirs may be born over time" that was false for an infertile marriage.

Guards: (single-floor + aged-consort-produces-nothing); (>= target over a reign); scenario_test 2616 (end-to-end: a married fertile dynasty grows to 2-5 heirs over 15 turns).

The roll-invariant band is 2-5, and 3 is NOT a floor. Because targetFamilySize is rolled per marriage in 2-4 and guaranteeTurns in 3-5, the worst fertile case (target 2, pace 5) bears its two guaranteed children on turns +5 and +10 — both inside the 15-turn reign, after which the pace stops. The ceiling is DIRECT_HEIR_CAP (5): a founding-generation ruler has no siblings, so no cadet branch adds to the roster and HEIR_ROSTER_SOFT_CAP (12) never binds on this path. Measured over 100 clean runs of the 2616 command sequence: 2 heirs 21%, 3 heirs 52%, 4 heirs 22%, 5 heirs 5% — never below 2, never above 5. An end-to-end assertion with a floor of 3 therefore fails about one run in five BY DESIGN; assert the 2-5 band, never the 3 default.

Why the floor of 2 is analytic and not merely observed: nothing in a 15-turn founding reign can kill an heir or close the fertility window before the second guaranteed birth. initializeDynasty seeds the founding ruler at age 30-44 (30 + (int)(Math.random() * 15)), so at +15 turns they are at most 59 and LineageRuler.shouldDieNaturally (DYING, or age above 80) cannot fire — there is no succession to promote an heir out of the roster. Newborn heirs cannot reach LineageHeir OLD_AGE_THRESHOLD (75). The consort starts at age 20-29 (20 + (int)(Math.random() * 10)) and ages one year per turn, so at the worst-case second guaranteed birth (turn +10) they are 30-39, still inside LineageSpouse.canHaveChildren (female under 45); and there is no consort-death path. Hence 0 or 1 heirs is UNREACHABLE on this path, which is why 2 is a safe assertion floor rather than a lucky one.

Cadet Branches (Sibling Lines) (finding 913)

Source: / marrySibling / birthCadetHeir

The dynasty tree is no longer a single ruler-and-consort line. An adult sibling of the ruler (a BROTHER or SISTER heir — the relation the succession reclassifier produces when a child ascends and their siblings remain in the line) weds and bears nephews and nieces, a second living branch of the house. Each turn processSiblingLines:

  • marries every adult (16+) unwed BROTHER/SISTER heir to a fresh fertile consort (guaranteed — the reliability the dynasty fantasy needs), then
  • births a NEPHEW/NIECE into each wed sibling's branch on the same guaranteed pace, up to CADET_TARGET_FAMILY_SIZE (=2) per sibling.

Bounded on purpose: one cadet generation only (a child born INTO a branch, whose parentRulerId names a household heir, never founds a further branch). Adult grandchildren may be player-married (they are never auto-wed); their children are GREAT_GRANDSON/GREAT_GRANDDAUGHTER, not another GRANDSON/GRANDDAUGHTER.

A former monarch's child keeps the right to found a branch, whatever succession relabels them. Succession relabels every heir relative to the new monarch, so when the first in line is a nephew or a cousin, the old monarch's own children read as COUSIN (and under a sibling monarch, as NEPHEW/NIECE). They are still the house's main line. is the one predicate for "may this heir wed and bear children": the ruler's son, daughter, brother, sister, grandson or granddaughter, or any heir whose parentRulerId names no heir in the house, which is a monarch (heirs are never pruned from the roster, and an heir who ascends leaves it). The auto-wed step, the player's arrange-marriage picker and branch births all read it. Every founder outside the ruler's own line (son, daughter, grandchild) is wed by the crown, the same as a sibling. A cousin's child reads as COUSIN: the relation list has no once-removed form.

Why: gating on the label alone let a house fill with cousins who could neither marry nor bear. On a saved house of 65 generations every living heir was a cousin, so only an aged monarch could bear the next generation, the pattern players heard as "the ruler marries in their 60s, has one child in their 70s and dies" (player feedback 11789, 13090, 13106). Guard: . Cadet children read as NEPHEW/NIECE, are parented to the sibling (parentRulerId), and surface under Other claimants in view family. LineageHeir.spouse holds a cadet consort. A cadet birth adds a little dynasty prestige (+2).

Because siblings only exist AFTER a succession (a child ascends, the rest become BROTHER/SISTER), cadet branches are a multi-generational payoff — an engaged player who plays a dynasty through a succession sees a second line grow.

Constant Value Meaning
CADET_TARGET_FAMILY_SIZE 2 Default nephews/nieces per sibling branch (roll range 1–3)
HEIR_ROSTER_SOFT_CAP 12 (was 6; design change 2026-07-31) Living-heir soft cap including cadets (cadet births pause here)
DYNASTY_EXPANSION_ROSTER_BONUS 4 Extra living-heir room per paid Dynasty Expansion (cadet room ONLY)
MAX_DYNASTY_EXPANSIONS 2 Paid expansion cap — effective roster cap 12 then 16 then 20

Soft-cap starvation (player feedback 10937–10944, 10989 — design change 2026-07-31): the prior HEIR_ROSTER_SOFT_CAP = 6 combined with DIRECT_HEIR_CAP = 5 left at most one roster slot for every cadet branch once the direct line was healthy. Adult siblings married (and could sit at ~50) but never produced children because births paused at the soft cap. That undercut the dynasty fantasy the cadet system exists to deliver. The soft cap rises to 12 so a full direct line plus several sibling branches can each bear their target cadet children. Unlimited is still rejected (Alexa voice must not enumerate an unbounded roster). Code must still keep spoken view heirs manageable: list the first six living heirs in full, then one summary line ("and N more claimants") when the roster exceeds six.

Cadet marriages and births are surfaced each turn: SP () speaks "X has married and begun a new branch of the house" / "A cadet branch grows, Y has been born"; MP () adds them to the turn-event list. Each category is narration-capped at TURN_EVENT_NARRATION_CAP (2) per turn in SP, with any excess rolled into one "N more cadet marriages/births join the house" summary line (BL-03, 2026-07-17, ) -- mirrors the story-beat / loyalty-transition narration cap in the same endTurn() method.

Guards: + cadetLineDoesNotFanOut_nephewsDoNotThemselvesFoundBranches.

Dynasty Expansion (in-skill purchase)

Status: IMPLEMENTED 2026-08-25. Owner-approved 2026-08-06 on an explicit condition — build and prove the FREE base first — so the task was gated behind the roster-cap work landing. That gate was re-verified before implementing rather than trusted: HEIR_ROSTER_SOFT_CAP reads 12 and passes 5/5, including cadetBranchesBearChildrenPastTheOldRosterCapOfSix.

Field Value
Shop name Dynasty Expansion
Matched key DYNASTY_EXPANSION (also EXPAND_DYNASTY, HEIR_ROSTER, ROSTER_EXPANSION)
Cost 80 Royal Seals — deliberately priced with Granary Expansion, the sibling permanent-cap raise
Effect +4 living-heir soft cap per purchase
Max purchases 2 — effective cap 12 then 16 then 20
SMAPI No new SKU and no new certification. This is a Royal Seals SPEND inside , not an Amazon product. Real-money purchases in Lineage are seal PACKS only (), and this reuses them.

The non-negotiable term: paid must NEVER raise DIRECT_HEIR_CAP. It stays 5 and free forever. Raising it with money would paywall succession depth, which is the whole thing the free cadet design exists to avoid. The paid lever buys collateral room only — a bigger house, not a deeper line.

One question, one answer. The effective cap is a single predicate, = HEIR_ROSTER_SOFT_CAP + purchases x DYNASTY_EXPANSION_ROSTER_BONUS. EVERY caller reads it — the cadet-birth gate in processSiblingLines, the view heirs cap line in , and the end-turn line in . A message quoting twelve to a player who bought room to twenty is a lie the player can hear, and a gate reading the raw constant would silently refuse the births they paid for. Do not add a second cap calculation anywhere (.claude/rules/refine-gates-never-stack.md). (repository root)

Voice rule is unaffected by roster size. is 6 and the readout breaks at index > 6 unconditionally, so a 20-heir house still speaks six heirs in full then "And 14 more claimants further down the line." The roster cap and the spoken cap remain the matched pair the constant's own Javadoc describes.

Fairness: a cadet birth adds 2 prestige, negligible against the territory, building and army terms of Realm Power on the Eternal Realm Ledger, and the paid stack is capped at two purchases — so the maximum paid advantage stays inside noise.

Guards, and why they are split across two surfaces. scenario_tests 3425 (the shop lists Dynasty Expansion at 80 seals) and 3426 (three purchase attempts in one chain reach the refusal rather than charging a third time) cover the SPOKEN surface. The cap ARITHMETIC — the 12/16/20 ladder, saturation at two purchases, and the DIRECT_HEIR_CAP invariant — lives in (7 cases) instead.

That split is deliberate, not an oversight. dynastyExpansionPurchases PERSISTS on the kingdom, and the scenario runner reuses a deterministic per-test user, so a scenario test asserting an absolute cap ("now holds 16") would pass its first run and fail every run afterwards, when the same kingdom is already at 20. Both scenario rows therefore assert only what is true on EVERY run: the listing, and the refusal at maximum. Do not "improve" them by adding a purchase-success or absolute-cap assertion — that is the state-relic anti-pattern in .claude/rules/hermetic-scenario-tests.md, and the pure arithmetic is already pinned hermetically in JUnit.

SHIPPED 2026-08-10. HEIR_ROSTER_SOFT_CAP is now 12 in , and reads the first SPOKEN_HEIR_DETAIL_CAP (6) living heirs in full before collapsing the rest into one "And N more claimants further down the line" line. The roster cap and the spoken cap are a matched pair — raising the roster without the readout makes view heirs unlistenable, and raising the readout without the roster leaves the dynasty starved. Change one, revisit the other.

A second defect was found and fixed in the same pass, and it is the reason the cap raise alone would have backfired. DIRECT_HEIR_CAP was being measured against LineageKingdom.getLivingHeirsCount() — the WHOLE living roster, including siblings, nephews, nieces and grandchildren — rather than against the ruler's own living children. With both caps low (5 and 6) that was nearly invisible, because the roster could barely exceed the direct cap anyway. At a roster cap of 12 it inverts the system: cadet branches grow, every nephew counts against the ruler's own birth cap, and the main succession line starves exactly as the cadet lines did before. The direct cap now reads getLivingDirectChildrenCount() (SON and DAUGHTER only), which is what this doc always specified. The two counts must never be conflated again: the roster cap bounds how large the house may grow, the direct cap bounds how many children ONE ruler bears.

Spouse Fertility

Source: LineageSpouse.getFertilityChance / canHaveChildren

Shipped formula (: the previous flat-band table was stale — it matched neither the slope nor several band values). Female age 35 is still 0.3 (age > 35 starts the decline). Null age falls back to 0.3. Result is clamped to 0.0–0.5.

Fertile window:
  Female spouse: age < 45
  Male spouse: age < 60

Female chance:
  age < 25: 0.4
  age 25-35 inclusive: 0.3
  age 36-44: 0.2 - 0.02 * (age - 35)   // 36=0.18 … 44=0.02
  age >= 45: 0.0

Male chance:
  age <= 50: 0.3   // no under-35 bump; default is 0.3
  age 51-59: 0.2 - 0.02 * (age - 50)   // 51=0.18 … 59=0.02
  age >= 60: 0.0

Succession

Source: LineageKingdom.processSuccession

When current ruler dies:
  1. If heirs exist:
     newRuler = heirs.get(0) (first/oldest heir) via LineageHeir.toRuler
     Remove from heirs list
     reclassifyHeirsRelativeToNewRuler (siblings/cousins relabeled)
     Set as currentRuler
     generation += 1
     Apply consort rules (see Succession Consort below)

  2. If no heirs:
     Generate emergency ruler:
       gender = randomly drawn (.randomGender())
       name = randomly drawn from the wide name pool for that gender (.randomName)
       title = "Regent"
       age = 25 to 44 inclusive  // 25 + (int)(Math.random() * 20)
       stats = 40 each (below-average)
       traits = [] (none)

     This represents a power vacuum / regency

Succession Consort (design change 2026-07-31, / feedback 10923)

The outgoing ruler's consort does not remain Queen/King Consort of the new reign — that is working as designed (a new monarch does not inherit their parent's marriage). The player complaint ("when a new heir takes over the consort just disappears") is half expected and half a real gap:

Case Verdict Required behavior
Deceased ruler's consort vanishes without narration Unclear copy Succession narration MUST say the late consort's reign ends and the new ruler needs a spouse of their own (or name the carried spouse). Silent disappearance reads as a bug.
Ascending heir already has a cadet LineageHeir.spouse (sibling branch that later inherits) Design change LineageHeir.toRuler MUST carry that spouse onto the new LineageRuler. Today toRuler drops spouse entirely — a code gap against this design.
New ruler unmarried after succession, even with living siblings/heirs Design change Auto-arrange a customary marriage on the succession turn (extend the continuity safety net). The prior net only fired when livingHeirsCount == 0, so a house full of siblings still left the new monarch unmarried and barren for a new direct line. Founding generation (gen 1) first marriage stays player-driven.

Unlimited heirs remain rejected. Cap values live in Heir Birth Pacing + Cadet Branches.

Living Dynasty Continuity Fixes

The dynasty previously read as patterned and mortality-exempt for anyone but the ruler. Five fixes, all in / LineageKingdom / LineageHeir / LineageSpouse:

  • Wide name pool wired in — used a local ~15-name array; it now delegates every name draw (ruler, spouse, heir, cadet, dynasty name) to (~97 male / ~97 female names, plus the dynasty prefix/suffix pools), the same source LineageWorldInitializer and already used for kingdom identity.
  • De-patterned births — a direct marriage previously always guaranteed exactly HEIR_TARGET_FAMILY_SIZE (3) children every HEIR_GUARANTEE_TURNS (4) turns, and a cadet branch always exactly CADET_TARGET_FAMILY_SIZE (2) nephews/nieces on the same fixed pace — reading as a scripted "4-then-2-then-0" / "always one niece one nephew" shape. Each marriage (arrangeMarriage, marrySibling) now rolls its OWN target family size and guaranteed-birth pace once, stored on the LineageSpouse (targetFamilySize 2-4 direct / 1-3 cadet, guaranteeTurns 3-5), so family size and pace vary reign to reign and branch to branch while the guaranteed-floor reliability is unchanged. A spouse built without these fields (a legacy save) falls back to the original fixed constants.
  • Continuity safety net — past the founding generation (dynasty.generationNumber > 1), a ruler with no spouse is a succession risk for the next direct line even when siblings already sit in the heir list. As of : auto-arrange a customary marriage when the ruler is unmarried after succession (or mid-reign with no spouse), not only when livingHeirsCount == 0. The old heirs-must-be-empty gate left a new monarch unmarried while "other claimants" aged without a new consort for the throne. The founding ruler's first marriage stays entirely player-driven (unaffected).
  • Heir mortality — LineageHeir.shouldDieOfOldAge() (age 75+, escalating chance 0.04 times years over 75, mirrors LineageRuler.shouldDieNaturally over-80 curve) runs each turn for EVERY living heir, including the ruler's own SON/DAUGHTER. Exempting the direct line let children occupy DIRECT_HEIR_CAP into a second century (player ages 130-154) and starve new births. A hard ceiling at LineageHeir.MAX_AGE (100) makes death certain once the curve already reaches 1.0. Dead heirs do not count toward either cap. Guards: LineageHeirAgingTest, LineageDynastyElderMortalityTest.
  • Named regents — the emergency regent was a placeholder string ("Regent of ") with a hardcoded MALE gender. It now draws a real name and a randomized gender from the same wide pool as every other family member, keeping the "Regent" title as a separate field.

Guards: (safety-net marriage, extended-family mortality, named regent); (updated to a range assertion for cadet family size); scenario_tests for faction identity and dynasty continuity.


Marriage System

Source:

Marriage Types

Type Mechanics
LOVE Random spouse generation, +10 morale
ARRANGED Spouse from specified kingdom, +5 diplomacy opinion
DIPLOMATIC Spouse from ally kingdom, strengthens alliance (+15 opinion)
CONQUEST Forced marriage from conquered kingdom, -10 morale to conquered

Marriage Flow

1. Generate or select spouse via buildArrangedSpouse for EVERY marriage type
   (LOVE / ARRANGED / DIPLOMATIC / CONQUEST share this helper):
   - age: 20 + (int)(Math.random() * 10)  → 20-29 inclusive
   - each of leadership, warfare, diplomacy, magic:
     40 + (int)(Math.random() * 20)  → 40-59 inclusive
     (Math.random() is 0 inclusive to 1 exclusive; the upper bound is exclusive)
   - LOVE does NOT use a 30-70 range; that table was stale
   - ARRANGED/DIPLOMATIC/CONQUEST do NOT currently pull stats from the source kingdom
2. Assign spouse to ruler
3. Apply marriage type effects (morale/opinion bonuses)
4. Record in dynasty history

Family Tree Views — living vs the whole house (L5)

Two readouts, deliberately split rather than one filtered dump:

Command Speaks Why
view family (also "family tree", "royal family") The living house only: the ruler's recorded parents (none for a regent; the previous rulerHistory entry for a ruler with none recorded), the ruler, a living consort, living children, living other claimants Answers "who is with me now" — the roster a player makes decisions against
view full family (also "whole house", "full dynasty", "ancestors", "the fallen") Everything view family says, then "The fallen, N members of the house" Answers "who has this house been" — a dynasty game needs its dead, and the living-only readout could never tell a ruler who had buried three children from one who had buried none

The fallen are named oldest-first up to SPOKEN_FALLEN_DETAIL_CAP = 4, then roll up into one line ("And N more who rest in the crypt"). That cap is deliberately SMALLER than SPOKEN_HEIR_DETAIL_CAP (6): the dead are history rather than a choice in front of the player, and unlike the living roster they are bounded by nothing — they accumulate across every generation. When nobody has died the section is omitted entirely, so a young dynasty is never handed an empty memorial.

Routing lives in the EXISTING end-anchored view-noun patterns in , so "full family" cannot be swallowed by the bare "family" alternative regardless of alternation order; "view the fallen" works because the optional possessive group consumes "the".

Dynasty Traits (10 Predefined)

Source: DynastyTrait

Trait Definitions

Factories in . Inheritance is rollInheritance(): BLOODLINE / isBloodline always inherit; otherwise STRONG=70%, AVERAGE=50%, WEAK=40% (traitStrength). The old per-row 0.25–0.8 column was stale — those numbers are not what rollInheritance reads.

Trait Category Strength / inherit Stat bonuses (code) Other
BRILLIANT MENTAL STRONG 70% leadership +5, diplomacy +5, magic +10 potential +15
WARLIKE PERSONALITY STRONG 70% warfare +15, diplomacy -5 potential +5
DIPLOMATIC PERSONALITY AVERAGE 50% diplomacy +15, leadership +5 potential +10; display name Silver Tongue
MAGICAL_AFFINITY BLOODLINE always magic +20 mana_production +10; potential +10
SICKLY PHYSICAL WEAK 40% warfare -10, leadership -5 potential -15
CRUEL PERSONALITY WEAK 40% warfare +5, diplomacy -10 morale -10, stability -5; potential -10
AMBITIOUS PERSONALITY AVERAGE 50% leadership +10, diplomacy -5 gold_production +5; potential +5. Not +5 warfare.
PATIENT PERSONALITY WEAK 40% diplomacy +10, leadership +5 potential +5
DRAGONBLOOD BLOODLINE always warfare +10, magic +10, leadership +5 military_strength +10; potential +20; not removable
LEGITIMIZED BLOODLINE always diplomacy -5 potential -5; not removable. No +5 leadership/+5 diplomacy.

Two extra factory traits exist in code and are not in the original 10-row table: WEAK_WILLED (PERSONALITY WEAK; leadership -10, diplomacy -5) and LAZY (PERSONALITY WEAK; warfare -10, leadership -5). Dud-chance design (15% to gain one of those) is still the open DECISION block below, not shipped as an automatic roll.

Trait Categories

Category Behavior
PERSONALITY Standard inheritance check
PHYSICAL Standard inheritance check
MENTAL Standard inheritance check
BLOODLINE Always inherited (inheritance check bypassed)

Trait Inheritance Rules

For each trait on parent (ruler):
  if trait.category == BLOODLINE OR trait.isBloodline:
    heir ALWAYS gets trait (100%)
  else:
    chance = STRONG 0.70 / AVERAGE 0.50 / WEAK 0.40 (traitStrength)
    if random(0, 1) < chance:
      heir gets trait
    else:
      trait not inherited

Spouse traits follow the same rules. DynastyTrait.rollInheritance() is the one home.

Research & Technology

Source: ,

Research Flow

Player says "research AGRICULTURE" (or "research mining")
  -> Normalize tech name
  -> Look up in .TECH_GOLD_COSTS (validates known tech)
  -> Validate: not already researched
  -> Set kingdom.currentResearchId = techId, kingdom.researchPoints = 0
  -> Each turn: researchPoints += BASE_PROGRESS_PER_TURN (10) + kingdom research bonuses
  -> Completion: when researchPoints >= .getRequiredProgress(techId)
  -> Rush option: pay getRushCost(techId) = 2x gold value for instant completion

Technology Tree

Source:

Research is time-based (not gold-based). Base 10 progress/turn. Rush completion costs 2× gold value.

Technology Era Gold Value Required Progress Turns (base)
AGRICULTURE Ancient 100 10 1
MINING Ancient 100 10 1
BRONZE_WORKING Ancient 100 10 1
WRITING Ancient 100 10 1
IRON_WORKING Classical 200 20 2
CURRENCY Classical 200 20 2
MATHEMATICS Classical 200 20 2
HORSEBACK_RIDING Classical 200 20 2
STEEL Medieval 400 40 4
SIEGE_WARFARE Medieval 400 40 4
BANKING Medieval 400 40 4

Full Technology Tree (23 technologies across 5 eras + magic track)

Era Technology Progress Rush Prerequisites
Ancient AGRICULTURE 10 (1 turn) 200g None
Ancient MINING 10 (1 turn) 200g None
Ancient BRONZE_WORKING 10 (1 turn) 200g None
Ancient WRITING 10 (1 turn) 200g None
Classical IRON_WORKING 20 (2 turns) 400g MINING + BRONZE_WORKING
Classical CURRENCY 20 (2 turns) 400g WRITING
Classical MATHEMATICS 20 (2 turns) 400g WRITING
Classical HORSEBACK_RIDING 20 (2 turns) 400g AGRICULTURE
Classical BASIC_MAGIC 20 (2 turns) 400g WRITING
Medieval STEEL 40 (4 turns) 800g IRON_WORKING
Medieval SIEGE_WARFARE 40 (4 turns) 800g IRON_WORKING + MATHEMATICS
Medieval BANKING 40 (4 turns) 800g CURRENCY + MATHEMATICS
Medieval TIME_MAGIC 60 (6 turns) 1200g BASIC_MAGIC + MATHEMATICS
Advanced ADVANCED_WARFARE 80 (8 turns) 1600g STEEL + SIEGE_WARFARE
Advanced ADVANCED_MAGIC 80 (8 turns) 1600g BASIC_MAGIC + MATHEMATICS
Advanced ADVANCED_ECONOMY 80 (8 turns) 1600g BANKING + CURRENCY
Advanced ADVANCED_DIPLOMACY 80 (8 turns) 1600g WRITING + CURRENCY
Advanced ADVANCED_ENGINEERING 80 (8 turns) 1600g STEEL + MATHEMATICS
Legendary LEGENDARY_WARFARE 200 (20 turns) 4000g ADVANCED_WARFARE
Legendary LEGENDARY_MAGIC 200 (20 turns) 4000g ADVANCED_MAGIC + TIME_MAGIC
Legendary LEGENDARY_ECONOMY 200 (20 turns) 4000g ADVANCED_ECONOMY
Legendary LEGENDARY_DIPLOMACY 200 (20 turns) 4000g ADVANCED_DIPLOMACY
Legendary LEGENDARY_CONSTRUCTION 200 (20 turns) 4000g ADVANCED_ENGINEERING

Source: — TECH_GOLD_COSTS, TECH_REQUIRED_PROGRESS, TECH_PREREQUISITES maps.

Rush Research NLU

Bare 'rush research' (no tech name) routes via BLOOD_LINEAGE tier 4 to LINEAGE_RESEARCH (direct_match=1) which opens the research menu. Player then picks a tech; the handler's rush flow (2× gold) applies when player says rush research <tech>. Added 2026-04-19, after 3 bare 'rush research' fallbacks/24h.

The research menu dynamically shows only available (prerequisite-met, not-yet-discovered) technologies via . Attempting to research a tech with unmet prerequisites shows the missing prerequisites.

Research Progress (Per Turn)

Base 10 progress points per turn. Library and Academy buildings provide research_bonus from KingdomBonuses.

Advancement Node Model

Source: AdvancementNode

Field Type Purpose
nodeId String Unique identifier
category String Research category
tier Integer 1-5 (complexity level)
resourceCost Map<String, Integer> Multi-resource costs
requiredProgress Integer Points needed to complete
tags List Classification tags

The AdvancementNode model supports multi-resource costs and progress-based research, but the handler bypasses this entirely with flat gold costs and instant completion.


Random Events

Source:

Event Trigger

Per turn: 10% chance of a random event occurring
If triggered: select event by weighted random from event pool

Event Definitions (13 Events — 10 Generic + 3 Seasonal)

Source:

Generic Events (any season)

Event Weight Effect Theme
BOUNTIFUL_HARVEST 15 Food surplus (more likely in autumn)
PLAGUE 8 Population/morale loss (more likely spring)
GOLD_DISCOVERY 10 Gold windfall
REBELLION 8 Stability/morale crisis
MAGICAL_STORM 10 Mana surge or drain
TRADE_CARAVAN 15 Trade resource bonus (more likely summer)
NATURAL_DISASTER 6 Building/territory damage
HERO_EMERGES 8 Army strength bonus
DIVINE_BLESSING 12 Morale/mana boost
BANDIT_RAIDERS 8 Gold loss

Total generic weight: ~100

Seasonal Events (season-locked)

Event Season Weight Effect Theme
BLIZZARD WINTER only 25 Food stockpile loss, movement penalty, -5 stability
DROUGHT SUMMER only 20 Severe food/morale penalty
HARVEST_FESTIVAL AUTUMN only 25 Food bonus, morale boost

Storage note: Only the 10 generic events are rows in lineage.rule_lineage_common (rule_type EVENT = 10 rows). The 3 seasonal events (BLIZZARD, DROUGHT, HARVEST_FESTIVAL) are code-driven constants — EVENT_BLIZZARD / EVENT_DROUGHT / EVENT_HARVEST_FESTIVAL in — not DB rows. An EVENT rule_type count of 10 (not 13) is therefore correct and expected.

Event Selection (Weighted Random)

totalWeight = SUM(all event weights)  // ~100
roll = random(0, totalWeight)
cumulativeWeight = 0
For each event:
  cumulativeWeight += event.weight
  if roll < cumulativeWeight:
    return event

Higher-weight events (BOUNTIFUL_HARVEST, MERCHANT_CARAVAN at 15) occur roughly twice as often as lower-weight events (MAGICAL_ANOMALY, DRAGON_SIGHTING at 7).

Event Choice System

Each event presents 3 choices with different outcomes:

Example: BOUNTIFUL_HARVEST

Choice Effect
Choice 1: Store surplus +200 food
Choice 2: Sell surplus +100 gold
Choice 3: Feast +15 morale, +50 food

Example: PLAGUE

Choice Effect
Choice 1: Quarantine -10 population, -5 morale
Choice 2: Pray for healing -20 mana, 50% chance: no effect or -20 population
Choice 3: Burn infected areas -5 population, -1 random building

Example: DRAGON_SIGHTING

Choice Effect
Choice 1: Send army Risk: 50% lose 10 units, 50% gain 500 gold + DRAGONBLOOD trait
Choice 2: Offer tribute -300 gold, dragon leaves
Choice 3: Ignore 30% chance dragon attacks (-15 population, -100 gold)

Event Expiration

If player does not choose within 3 turns:
  Apply default penalty (typically worst outcome)
  Mark event as expired

Event History

Maximum 50 events stored in kingdom.eventHistory
Oldest events pruned when limit exceeded

AI Turn Processing

Source:

AI Detection

Source:

A kingdom is AI-controlled if:
  1. ownerUserId is null (abandoned kingdom)
  2. ownerUserId starts with "AI_" (explicitly AI)
  3. Owner inactive for 30+ days:
     daysSinceActive = (now - lastActivityTms) / (86400000)
     isAbandoned = daysSinceActive >= 30

AI Decision Loop

For each AI kingdom per turn:
  1. Evaluate personality priorities (from AIPersonality)
  2. Build decisions: construct buildings based on priority weights
  3. Recruit decisions: recruit units based on military priority
  4. Diplomatic decisions:
     - Calculate war likelihood against each neighbor
     - Calculate alliance likelihood with each neighbor
     - Check betrayal threshold for existing allies
     - Execute highest-priority diplomatic action
  5. Military decisions:
     - Move armies toward targets based on stance
     - Attack if war likelihood exceeds threshold

Season System

Source:

Seasons are derived from the real-world calendar month in Global mode, or from turn count in SP/MP (every 12 turns):

Season Months (Global) SP/MP Turn Cycle
WINTER December, January, February Every 4th season
SPRING March, April, May Every 1st season
SUMMER June, July, August Every 2nd season
AUTUMN September, October, November Every 3rd season

Seasonal Production Modifiers

Source: ,

Season Food Gold Wood
SPRING +20% - -
SUMMER - +10% -
AUTUMN +15% - +10%
WINTER -20% - -

Seasonal Secondary Effects

Source:

Season Effect
SPRING +10% population growth
SUMMER +5 morale
AUTUMN (none)
WINTER +15 defense bonus (fortification), -10% movement

AI Seasonal Aggression

Source:

Season Aggression Modifier
SPRING +15 (expansion season)
SUMMER 0
AUTUMN -5 (harvest focus)
WINTER -20 (supply lines difficult)

Applied in to modify effective aggression trait before stance decisions.


Design Review — Family Growth / Heir Cap / Claimant Fertility (2026-07-31)

Source feedback: claude.player_feedback 10923, 10937–10939, 10941–10944, 10989 (heavy engagers; Blood Lineage growth cohort). Design arbiter: this doc § Heir System / Cadet Branches / Succession / Spouse Fertility. Player-proposed "unlimited heirs" is rejected.

# Item Verdict Design section Notes
1 Heir cap reports as 6 in some places and "63" in others Working as designed (two intentional caps) + unclear copy § Heir Birth Pacing (DIRECT_HEIR_CAP=5) + § Cadet Branches (HEIR_ROSTER_SOFT_CAP) There is no 63 constant in code. Live values are 5 (direct living children of the ruler) and 6→12 (whole living roster soft cap). Target paced family size is 3 (range 2–4). Player "63" is almost certainly speech/typo collision of 6 and 3, not a third cap. Unlimited is rejected so Alexa view heirs stays speakable. Copy must name both caps when growth stops.
2 Non-ruler claimants (~50) never produce children Design change needed (done in this doc) § Cadet Branches Cadet system is intentional and correct in spirit (siblings wed + bear nephews/nieces). Soft cap 6 next to direct cap 5 starved cadet births. Soft cap raised to 12; speak-summarize when roster > 6. Code SHIPPED 2026-08-10, together with the direct-cap counting fix described in § Cadet Branches.
3 Consort disappears when a new heir takes over (10923) Unclear copy + design change needed (done in this doc) § Succession Consort Late consort does not stay on the throne (WAD). Silent drop is bad copy. Carry ascending heir's spouse in toRuler; auto-marry unmarried post-succession rulers even when siblings exist. Code not yet updated.
4 Family growth / "bloodline will end" overall (10937–10938) Design change needed (same as #2–#3) § Cadet Branches + § Succession Consort Core fantasy is multi-generation continuity; the soft-cap interaction + post-succession unmarried gap made a healthy dynasty feel sterile. Caps stay finite; numbers and succession marriage rules change.

Code follow-ups (out of scope for this analysis task; do not ship without code + hermetic tests):

  1. HEIR_ROSTER_SOFT_CAP 6 → 12 in . SHIPPED 2026-08-10.
  2. view heirs: full detail for first 6 living heirs, then "and N more claimants". SHIPPED 2026-08-10 in via SPOKEN_HEIR_DETAIL_CAP. view family still enumerates in full and is the deliberate fallback the summary line points players at.
  3. LineageHeir.toRuler: copy spouse when present.
  4. Succession / continuity: auto-arrange marriage for unmarried gen 2+ rulers even when heirs exist; succession narration names the late consort's end and the new marriage need or carry.
  5. Copy when a birth is blocked by a cap: name which cap (direct vs roster).