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/SISTERheir to a fresh fertile consort (guaranteed — the reliability the dynasty fantasy needs), then - births a
NEPHEW/NIECEinto each wed sibling's branch on the same guaranteed pace, up toCADET_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
LineageWorldInitializerand already used for kingdom identity. - De-patterned births — a direct marriage previously always guaranteed exactly
HEIR_TARGET_FAMILY_SIZE(3) children everyHEIR_GUARANTEE_TURNS(4) turns, and a cadet branch always exactlyCADET_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 theLineageSpouse(targetFamilySize2-4 direct / 1-3 cadet,guaranteeTurns3-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 whenlivingHeirsCount == 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_typeEVENT= 10 rows). The 3 seasonal events (BLIZZARD, DROUGHT, HARVEST_FESTIVAL) are code-driven constants —EVENT_BLIZZARD/EVENT_DROUGHT/EVENT_HARVEST_FESTIVALin — not DB rows. AnEVENTrule_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):
SHIPPED 2026-08-10.HEIR_ROSTER_SOFT_CAP6 → 12 in .SHIPPED 2026-08-10 in viaview heirs: full detail for first 6 living heirs, then "and N more claimants".SPOKEN_HEIR_DETAIL_CAP.view familystill enumerates in full and is the deliberate fallback the summary line points players at.LineageHeir.toRuler: copyspousewhen present.- 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.
- Copy when a birth is blocked by a cap: name which cap (direct vs roster).