People leave the places that fail them and move toward the places that thrive.
A population-migration and refugee mod for Sid Meier's Civilization VII.
Subscribe on Steam · Download · Changelog · Typeset PDF · Technical reference
When a settlement is starving, unhappy, or under siege, people leave. When one is thriving, people move there instead. Emigration adds migration and refugee systems to Civilization VII, moving population within and between civilizations and changing yields, growth, and Influence. Every move is logged with its cause.
Documentation on GitHub: README.md · typeset PDF, with every formula rendered.
- Every language reads in full. The Civilopedia pages and their quotations, the enclave names and descriptions on the map, the call-home and arrival pop-ups, the enclave and displaced-people quotes, and the options that were still in English are translated into all eleven languages.
- Chinese, Japanese and Korean draw every character. Panel titles, city names, tabs, filter buttons, the guide and the network map's labels used the game's Latin-only faces and showed missing-glyph boxes; they now use the language's own font the way the game does. The network map's war badges draw their flag as a shape, not a glyph those fonts lack.
- Notifications and Chronicle entries read in your current language. New entries are recorded as text keys and composed when shown, so switching the game's language translates them; entries recorded before this version keep the text they were written with.
- The network timeline's labels are translated: the Scaled Pop and Civ Pop units, Play and Pause, the age name and the year labels.
- The city readout sits beside the City Details panel instead of over it. When you open a settlement, the per-city migration readout moves out from under whichever city-screen panel holds its corner, and returns to the corner when City Details is closed.
- A disaster only displaces people where it did damage: a settlement shielded from the event (a Dam or a Levee against a flood, the Khmer Baray) or one the event spared sends nobody, instead of every settlement in the radius taking the same distress.
- A flood reaches every settlement along its river, not just the ring of tiles around the epicenter the event names.
- Flood-like events from other mods are weighed as floods rather than as unknown events.
- Yields on the buttons: the refugee, newcomer, call-home and Cultural Enclave pop-ups show each choice's gains and costs on its own button, with the game's yield icons.
- Enclave stances pay a real sum once: Culture, Science, Influence or Gold, sized to your economy and the age, usually at a price in Gold; a stance you cannot afford is grayed out.
- Newcomers settled by the city show the yields of the tile they will work before you choose.
- Calling your people home is two offers: a purchase from your own settlements, and a gamble on your people abroad, each in three sizes with Gold and Influence prices.
- Reasons to stay: wonders and civic buildings hold people in a settlement, and emigration pressure fades once a settlement's troubles pass.
- The Ethnic Composition lens blends every people living on a tile, shades by how crowded it is, seats each enclave's people on its own tile, and repaints as soon as the turn starts.
- Migrants keep who they are: a mixed city sends out a mixed crowd, and razed settlements leave the lists at once.
- Enclaves explain themselves: hover an enclave's tile for its stage and the source of every yield; the map marker shows the stage.
- The Prosperity lens scores each tile in points and lists every term behind the score.
- The network diagram: click a city to see just its flows, or drag a settlement out of its civilization's circle.
- The Civilopedia covers the whole mod, with a page of quotations for every people.
- The Mods tab has a switch for each decision pop-up and full hover help on every option.
Full notes: CHANGELOG.md.
Migration and displacement are abstracted here as game systems. In reality, people often leave home because of war, persecution, disaster, or hardship. This mod tries to acknowledge that reality without trivializing it.
I donated to the International Refugee Assistance Project while creating Emigration. I cannot sustain a per-subscriber pledge indefinitely, but I plan to mark major milestones with donations of time or money when I can. If you are able, please consider supporting UNHCR, the IRC, MSF, IRAP, or local refugee and mutual-aid groups.
- 100 subscribers: Donated $100 to the International Refugee Assistance Project.
- 50 subscribers: Donated $50 to Médecins Sans Frontières.
- 25 subscribers: Donated $25 to the International Rescue Committee.
- 10 subscribers: Pledged one hour of volunteer time to a refugee-support, humanitarian, or mutual-aid organization.
- Release donation: Made an initial donation to the International Refugee Assistance Project.
Anonymized receipts will be added to the repository.
Compatible with Civilization VII 1.5.0.
- Prosperity-based migration: settlements are scored each turn on yields, happiness, war weariness, and government; population flows toward stronger settlements
- Internal migration: movement between a civilization's own settlements, shown separately on the dashboard
- Cross-civilization migration: shaped by borders, alliances, war, and asylum
- Distance-weighted destinations: nearby settlements are favored
- Overcrowding pressure: tall cities push population outward
- Reasons to stay: wonders and civic buildings (a granary, a market, walls, a school) hold people in a settlement, however large it is
- Pressure that fades: a settlement's urge to lose people follows its current situation, so a city that recovers from a siege or a bad stretch settles down again
- Concurrent causes: war refugees and economic migrants can leave the same city in the same turn
- Migrant transit: travel time scales with distance and game speed
- War refugees: driven by district damage, sieges, and pillaging inside a city's borders
- Aggressor-aware flight: refugees prefer their own civilization, then neutrals, and avoid the attacker
- Disaster and plague refugees: floods, volcanoes, storms, and plague can force migration
- Famine and unrest: starvation and sustained unrest push people out
- Crisis deaths: prolonged siege, famine, war, or disaster can kill trapped populations
- Displacement caps: up to 60% of a city's population per siege and 50% per disaster
- Resettlement time: war and disaster refugees join the host population gradually; happy, open, uncrowded hosts settle them faster and pay upkeep while they wait
- Refugee decisions: rare choices to welcome refugees, settle them on the frontier, or turn them away
- Real departures: each departure removes an outlying rural improvement and its yields; pillaged tiles go first, food tiles last during famine
- Real arrivals: place newcomers yourself, let the city place them, or use a Migrant unit
- Integration cost: temporary gold cost and delayed Celebration for the host civilization
- Congestion headwind: limits how much one settlement can absorb
- Pro-Immigration Stance: stronger inbound migration and more Influence
- Anti-Immigration Stance: stronger retention and more Production
- Open Borders: increases cross-civilization migration
- Ethnic composition: population tracked by civilization of origin, preserved through capture, and carried by migrants who move on, so a mixed city sends out a mixed crowd
- Ethnic Composition lens (Shift+E): tile-by-tile map of population origins, each tile a blend of the peoples living on it and shaded by how crowded it is; an enclave's tile reads as its people's own quarter
- Integration: newcomers assimilate over time; war with the homeland stops it and unrest slows it
- Return migration: diasporas can return to peaceful, prosperous homelands
- Call our people home: pay Gold or Influence for a call, in a size you choose, to bring displaced people back to the settlement they fled; from your own settlements that many come, from abroad the call is paid for either way and each person asked may or may not answer
- Cultural Enclaves: lasting foreign communities can become real improvements with the origin civilization's identity and yields; hover an enclave's tile to see its stage and where each of its yields comes from
- Put down roots: enclave communities are less likely to return home
- Migration Chronicle: major movements become history entries
- Quotes from the displaced: refugee and newcomer pop-ups use words from that people's refugees, exiles, and migrants; the call-home pop-ups use words of those who longed for home and came back
- Migration dashboard: flows by civilization, cause, and settlement; on the network diagram, click a city to highlight just its migrant flows, or drag a settlement out of its civilization's circle to read its flows
- Cause-coded notifications: color-coded toasts plus a persistent log
- Move attribution: records the factor that decided each move
- City readout: pressure mix, reasons to leave and reasons to stay, and enclave progress by settlement
- Prosperity lens: tile-by-tile prosperity map, each tile scored in points from what stands on it and around it, with a hover readout that lists every term
- Demographics integration: Net Migration, Emigration, and Immigration graphs and tables, with movement split into internal and external
- Civilopedia section: every system, every policy card, and every quotation with a note on its speaker
- Intensity presets: Low, Medium, and High
- Arrival behavior by type: refugees, migrants, and returnees can ask where to settle or settle automatically; refugees ask by default
- Decision pop-ups on or off: refugee decisions, newcomer placement, call-home offers, and enclave stances each have their own switch on the Mods tab
- Hover help on every option: each Mods-tab option explains what it does, what each choice means, and when a change takes effect
- 121 advanced options: Options ▸ Mods ▸ Emigration
- Per-leader and per-civilization tuning: all 38 leaders and 50 civilizations, including alternate personas
- Game-speed scaling: Online through Marathon
- 12 languages: English, German, Spanish, French, Italian, Japanese, Korean, Polish, Portuguese (Brazil), Russian, Simplified Chinese, and Traditional Chinese
- Settlements are scored each turn on yields, happiness, war weariness, and government. Lower-scoring settlements lose people to stronger ones.
- War damage, sieges, and pillaged tiles create refugees.
- Starvation, unrest, plague, disasters, and overcrowding also push people out.
- Causes run independently, so a city can lose war refugees and economic migrants in the same turn.
- What a settlement has built (wonders, and each kind of civic building) is a reason to stay, and it is not divided by population, so building well lets a large city hold its people.
- The pressure behind ordinary departures fades when a settlement's troubles pass, so only a strong, lasting reason to leave empties a city over time.
- Nearby settlements are preferred.
- War refugees prefer their own civilization, then neutral civilizations, and avoid the attacker.
- Cross-civilization movement is shaped by borders, alliances, war, asylum, immigration stances, and Open Borders.
- A departure removes an outlying rural improvement and its yields. Pillaged tiles go first; starving cities keep food tiles as long as possible.
- Migrants spend several turns in transit, depending on distance and game speed.
- Arrival behavior is configurable. Refugees can ask where to settle; migrants and returnees settle automatically by default.
- War and disaster refugees can spend additional time resettling after arrival. The host pays upkeep while they wait.
- Arrivals add a temporary gold cost and delay the next Celebration. Congestion limits intake.
- Sustained siege, famine, war, or disaster can kill people who cannot escape.
- One siege displaces at most 60% of onset population; one disaster at most 50%.
- Settlements track population by civilization of origin, including after capture. Migrants carry the mix of the city they left, so a diaspora that moves on keeps its identity.
- A conquered city the mod first sees after the conquest (added mid-game, or a save from before the ledger) counts as its original civilization's people.
- The Ethnic Composition lens (Shift+E) maps that composition by tile and repaints at the start of each turn, even while it is open.
- Newcomers integrate over time; war with the homeland stops integration and unrest slows it.
- Diasporas can return home when the homeland is peaceful and prosperous.
- Lasting foreign communities can form Cultural Enclaves. Enclaves persist while the community does and can be built over.
- Refugee decisions, displaced-person quotes, and the Migration Chronicle add narrative context.
- Toasts and the Notifications log record each move and its cause.
- The dashboard shows flows by civilization, cause, and settlement.
- City readouts show local pressure, reasons to leave and to stay, and enclave progress, and the dashboard shows them across every settlement.
- Hovering an enclave's tile shows its stage and the source of every yield it brings in.
- The Civilopedia's Emigration section describes every system, policy card, and option, and lists every quotation with a note on its speaker.
- The Prosperity lens maps prosperity by tile: wonders, the center, buildings and quarters, worked land, rivers and natural wonders score up; ruin scores down. Hover a tile to see the terms.
- With Demographics installed, migration also appears in graphs and tables.
- Low, Medium, and High intensity presets plus individual options under Options ▸ Mods ▸ Emigration.
- Per-leader and per-civilization tuning.
- Automatic game-speed scaling from Online to Marathon.
- Interface text in 12 languages.
- Copy this folder to
~/Library/Application Support/Civilization VII/Mods/emigration/, relaunch, and enable Emigration in Additional Content. - Play turns. With
const DBG = true(the dev default), the mod logs toUI.log:grep -E "EMIG_" "~/Library/Application Support/Civilization VII/Logs/UI.log"release.shsetsDBGtofalsefor shipped builds. - Dev dock buttons: run a pass or dump the prosperity ranking. Console:
emigration.runNow(),emigration.rank(),emigration.window(),emigration.city(id). - Tune or disable layers: Options → Add-ons → Emigration → Advanced settings. All advanced-model and interactive-system switches default on.
Useful log markers: EMIGRATION … left … for …, assimilation: …, and ATTRITION … (no refuge).
Available through an optional dock button or the Demographics interface. Tabs cover Migration Network, Net Migration, Why People Move, Settlements, Diversity, Immigration Policies, Notifications, and Guide.
The dashboard reflects saved gameplay state, not cosmetic estimates: population, abandoned and settled tiles, gold, and Influence all change in game (§12).
- Player experience risks and mitigations: docs/player-experience-risks.md
- Player experience items addressed: docs/player-experience-addressed.md
- Engine behavior recorded by in-game probes: docs/engine-limits-from-probes.md
- Tooltip compatibility with other tooltip mods: docs/tooltip-mod-compatibility.md
- Migration mechanics: DESIGN.md
- Engine checks and verification: FINDINGS.md
- In-game validation: testing-requirements.md
- Civ VII modding mechanics and limits: civ7-mechanics-and-feasibility.md
- Leader/civ ability and memento interactions: leader-civ-memento-interactions.md
- Advanced migration formulas: algorithmic-improvements.md
- Interactive systems: interactive-extensions-design.md, interactive-extensions-implementation.md
The overview ends here. The rest is the technical reference: systems, formulas, modules, tuning, and persistence.
- What it does (player-facing)
- How it works: the per-turn loop
- The signals & the Prosperity score
- Population scaling (Demographics alignment)
- The advanced model (algorithms & per-civ tuning)
- Interactive systems (on by default)
- Consequences: the gameplay-write cost layer
- Reporting & Demographics integration
- In-game feedback & notifications
- Options & tuning
Appendices
- Architecture / module map
- Runtime behavior in game
- Persistence
- Localization
- Development
- Compatibility & mod coexistence
- Known open issues
Each turn, the mod scores settlements and moves population from weaker ones toward stronger nearby destinations.
- Peacetime: unhappy or low-yield settlements lose people to happier, wealthier ones.
- War refugees: district damage, siege, and pillaging cause rapid flight away from the nearest invader.
- Concurrent causes: war, disaster, and economic pressure are evaluated independently, so several can act on the same city in one turn.
- Cross-civilization: migration can stay within a civilization or cross borders.
- Regional: distance reduces pull, so nearby destinations usually win.
- Consequential: arrivals add integration costs and congestion, preventing unlimited growth.
- Speed-aware: pacing scales with game speed (§2).
Moves appear in toasts, Demographics graphs, and the dev log, for example:
EMIGRATION 1 population point (≈30,000 people) left Rome (Romans) for Carthage (Carthaginians).
Defaults are shown below; most are tunable in §10 and on the dashboard's Guide tab.
What makes people leave
| Counts? | ||
|---|---|---|
| Unhappiness / low yields | ✓ | Main peacetime driver; happiness weighs most, with low per-capita yields adding pressure |
| Empire-wide war weariness | ✓ | Modest civ-wide pressure on top of local violence |
| War damage to districts | ✓ | More damage means more pressure |
| Being besieged or attacked | ✓ | Applies only to the affected city |
| Attacked by a city-state / Independent Power | ✓ | Same local conflict pressure as a major-civ war |
| Pillaged tiles in the city's borders | ✓ | Damaged improvements on the city's own plots |
| Starvation | ✓ | Most flee; some die until food recovers |
| Plague / disease | ✓ | Infected cities lose people; plague carry is optional |
| Natural disasters | ✓ | Capped per-city pressure, only where the event did damage |
| Overcrowding | ✓ | Tall cities push population outward |
What attracts
| Attracts? | ||
|---|---|---|
| Higher prosperity | ✓ | Weighted per-capita food, production, gold, science, and culture |
| Higher happiness | ✓ | Strongest factor, but capped by the shaped model |
| Pro-Immigration Stance | ✓ | Raises inbound pull and Influence |
| Open Borders | ✓ | Cross-civ pull bonus |
| Being nearby | ✓ | Distance-penalized |
| What a settlement has built | ✓ | Wonders and each kind of civic building count as a reason to stay, not divided by population |
Who participates
| Participates? | ||
|---|---|---|
| Your civilization | ✓ | Sends and receives like any major civ |
| Towns | ✓ | Same system as cities |
| Your own settlements | ✓ | Internal migration shown separately |
| Other major civilizations | ✓ | Simulated from turn one |
| Unmet civilizations | ✓ | Simulated but masked in the UI by default |
| City-states / minor civs / Independent Powers | ✗ | Do not send or receive, though attacks by them can cause flight |
Behavior
| Migration between civilizations | ✓ | Shaped by borders, distance, and stance |
| Distant AI-vs-AI wars | ✓ | Only when they damage, besiege, or pillage a city's own territory |
| Anti-Immigration Stance retains people | ✓ | More retention and Production, less Influence |
| No Open Borders reduces cross-civ flow | ✓ | Movement remains possible but is harder |
| Population and yields change | ✓ | Real gameplay writes |
| Pacing adapts to game speed | ✓ | Cooldowns, ramps, transit, and thresholds scale (§2) |
| Systems can be tuned or disabled | ✓ | Presets + 121 options |
| Fighting outside a city's borders | ✗ | Does not drive that city's emigration |
| Migrants arrive instantly | ✗ | Travel takes time |
| Absorbing migrants is free | ✗ | Temporary happiness/celebration and gold costs apply |
| War can empty a city to zero | ✗ | Displacement is capped; crisis deaths are separate |
Identity, integration & return
| Settlements remember origins | ✓ | Composition by origin civ, preserved through capture |
| Migrants keep their identity | ✓ | A migrant carries the mix of the city they left |
| Newcomers integrate | ✓ | Non-owner origins drift toward the owner over time |
| War / unrest slows integration | ✓ | War with the homeland stops it; unrest slows it |
| Diasporas return home | ✓ | Possible when the homeland is peaceful and prosperous |
| Refugee waves can prompt a decision | ✓ | Welcome / frontier / turn away |
| Migrations become history | ✓ | Migration Chronicle entries in Notifications |
Scope & limits
| Changes AI or replaces base-game files | ✗ | Additive only |
| Moves population instantly | ✗ | Distance and transit apply |
| Lets you place individual migrants | ✗ | You shape flows indirectly; local arrivals can be placed |
| Lets one civ snowball unchecked | ✗ | Field-relative scoring, congestion, and anti-snowball headwinds limit runaway inflow |
Post-war recovery
- Will a war-shrunk city recover? Usually. Displacement removes rural improvements, but the city and districts remain unless captured. Food growth and immigration can rebuild population after the fighting stops.
- Do refugees return? Some can through Return Migration (§6g) once the homeland is peaceful and prosperous.
- Does repairing pillaged tiles restore population? No. It removes pressure but does not add population.
- How far can war shrink a city? Displacement is capped at
siegeLossCapPct(60% by default) of onset population. Crisis deaths are separate and can reduce rural population further. - Fastest recovery: make peace, repair pillaged tiles, and raise happiness.
Migration in transit
Moves are delayed by transitLagTurns, scaled by distance and game speed.
- Rate: war and disaster refugees can flee every turn; voluntary migration is slower. Each civilization has its own budget.
- Lifecycle: departure removes the source rural point immediately. In transit, the migrant belongs to no city. Integration cost begins on arrival.
- Per-city caps:
maxLossPerCityPerTurnandmaxGainPerCityPerTurn, scaled by intensity. Deaths do not count against them. - Failed arrivals: migrants wait if the destination is full. They can die in transit if the destination is razed, captured, or remains full.
- Travel time: typically 1–4 turns on Standard (
transitHexPerTurn≈ 5 hexes/turn); war/disaster refugees take at least one turn. - Reporting: Emigration can rise before Immigration catches up. Net Migration counts only settled cross-civ moves.
On each PlayerTurnActivated:
- Per-civ costs (
chargePerTurnCosts) run for the active civilization: decaying integration cost and migrant-unit holding cost (§7). - The emigration pass (
runPass) runs once on the local player's turn, gated byturnInterval:- Decay violence and disaster distress.
- Build one
CitySignalper simulated city (§3). - Score and rank settlements by Prosperity (§3, §5).
- Advance state: scaling turn, cooldowns, and per-owner population totals used for congestion.
- Process each source on the voluntary and crisis tracks below. Each civilization has its own safety ceilings:
maxMovesPerTurn + movesPerCity × settlementsfor voluntary movement andmovesPerSiege × cities in crisisfor crisis movement. - Persist state to
GameConfigurationand publish feedback (§9).
- Events registered at boot update supporting state:
DiplomacyDeclareWar/MakePeacemaintain the aggressor map (§6a), whileRandomEventOccurredrecords disaster distress (§6c).
Each source runs two independent tracks, so both can fire in the same turn:
- Crisis (war / disaster): can flee every turn, with no pressure bar or cooldown, bounded by
warSurgeMaxandsiegeLossCapPct. Cause is disaster when disaster distress dominates; otherwise war. - Voluntary (prosperity / unhappiness): builds pressure toward
emigrationBar, moves one point when the bar is crossed, then waitscooldownTurns. Cause is unhappiness when happiness is low; otherwise prosperity. Pressure carries over each turn atpressureRetention(0.9, re-based for game speed; "Pressure kept each turn", Advanced ▸ Pacing), so it tracks the settlement's current situation in both directions, with a half-life of about 7 turns. A departure clears it outright, and settlements on a cooldown or with nowhere to go cool down too. At the default only a steady pull above about a third of the bar per turn reaches the bar;pressureRetention = 1keeps pressure indefinitely.
Each track has its own per-civ budget (splitBudgetsEnabled). Records still carry one cause each, so cause-level telemetry stays clean. The city readout can show the live mix, such as "War 60% · Prosperity 40%" (splitUiReadoutEnabled). All three flags default on.
If a severely distressed city has no viable destination, it builds attrition pressure and can lose a rural point as a death rather than a move (§6d).
Game speed changes how many turns cover the same game progress. The mod scales turn-based pacing with S so migration behaves similarly in game-time across speeds.
| Speed | CostMultiplier | S | cooldown 8 → | bar 30 → |
|---|---|---|---|---|
| Online | 50 | 0.5 | 4 | 15 |
| Quick | 67 | 0.67 | 5 | 20 |
| Standard | 100 | 1.0 | 8 | 30 |
| Epic | 150 | 1.5 | 12 | 45 |
| Marathon | 300 | 3.0 | 24 | 90 |
- Durations ×S:
cooldownTurns,siegeRampTurns,transitLagTurns - Thresholds ×S:
emigrationBar,attritionThreshold - Decay re-based to
d^(1/S):violenceDecay,disasterDecay - Not scaled:
siegeLossCapPct, intensity thresholds, yield weights, friction, or per-turn move ceilings
The active speed is read from Configuration.getGame().gameSpeedType and GameInfo.GameSpeeds.lookup(...).CostMultiplier, cached, and falls back to S = 1 if unavailable. gameSpeedTuningEnabled is the rollback switch. See emigration-game-speed.js. The separate gameSpeedScalePopulation flag is off by default and affects only the §4 representative-people scaling.
emigration-cities.js builds a CitySignal for each city: owner, population, rural and urban population, per-capita yields, happiness, unrest, starvation, siege state, war state, violence, disaster distress, and infection. emigration-prosperity.js converts that signal into a score. The legacy linear model is:
popExponent (0.85, "Size dilutes prosperity"): below 1 it softens the per-head average, so a settlement's size divides away less of what it produces; 1 is a straight average.
emigration-built.js, builtEnabled, "Buildings are a reason to stay"): each wonder (1.5, up to builtWonderCap = 3) plus each kind of civic building present, counted once however many there are: amenity 1.0; safety, sustenance, learning and trade 0.8; shelter, culture and work 0.6; any other building 0.3. The total is capped at builtCap (6). It is neither divided by population nor scaled by happiness. Kinds are read from the compiled database by what a building grants, so buildings added by an age, DLC or another mod count; pillaged buildings do not. The scan refreshes every builtRefreshTurns (5). The city panel lists these as Reasons to stay, and the explainer adds a Wonders & buildings row.
Higher scores are more attractive. Happiness carries the largest default weight (localHappinessFactor = 6). Negative situational pressure drives both emigration and distress(s), which feeds crisis death (§6d) and the city readout. §5 replaces the linear happiness and violence behavior with the shaped model when enabled.
With polityModelEnabled, the model also reads:
- Happiness stage: the five-stage Angry (−2) to Ecstatic (+2) ordinal, adding bounded pull/push through
happinessStageWeight. - Celebration: Golden Age civilizations get extra attraction through
celebrationPull. - Government: a small clamped tie-breaker through
governmentWeightandgovernmentLeanCap; most government effects already arrive through yields and happiness. - War weariness: a modest civilization-wide push through
warWearinessModifier, separate from local violence.
These values are read once per civilization per pass and copied onto each CitySignal.
War migration responds to violence inside a city's own territory, not merely to being at war. Signals are read from game state, so the same rules apply to visible and distant AI wars.
- District damage: center-district health provides fresh-assault (
vwAssault) and standing-siege (vwSiege) pressure. - Pillage: damaged improvements on
getPurchasedPlots()addvwPillage. - Persistence: violence accumulates and decays through
violenceDecay, adjusted tod^(1/S). Sustained siege grows; isolated raids fade in about 2–3 turns of game-time. Algorithm D adds siege escalation and a cumulative cap (§5-D).
All checks are territory-scoped. Fighting elsewhere, neutral-field battles, and damage outside the city's footprint do not move its population. sig.atWar is informational only. A district marked besieged can still count when the besieging unit stands just outside the border; siegeBesiegedFloor keeps that pressure gradual.
- Distance:
−distanceFactor × hexDistance - Flee vector: above
violenceFleeThreshold, refugees prefer destinations away from the nearest enemy (fleeFactor) - Aggressor preference: own civ > neutral > attacker when §6a is enabled
- Open Borders:
openBordersBonusraises cross-civ pull between civilizations with an active agreement
Destination pull combines prosperity, targeted attraction, friction, and relationship permeability:
openness(d) controls inbound cross-civ flow and retention(s) controls outbound cross-civ flow. Both are neutral unless border policies are active (§6b).
Friction is:
perFewerPop (0.5, "Reluctance to move somewhere smaller", Advanced ▸ Brakes) mirrors perExtraPop: the score's per-citizen term favors a smaller destination just for being smaller, so moving down in size costs the same per citizen as crowding into a bigger place.
internalRefuge(s,d) gives crisis refugees a bonus toward destinations inside their own civilization; crisisEscapeBonus provides the corresponding escape incentive when no good homeland option exists. Both move with the "Movement between civilizations" slider. drainFor(s) reduces cross-civ outflow from civilizations below their fair population share.
dominanceFor(d) is the anti-snowball term:
$\mathrm{antiSnowballWeight}\cdot\max!\left(0,\frac{\mathrm{pop}\text{civ}(d)}{\overline{\mathrm{pop}}\text{civ}}-\mathrm{antiSnowballThreshold}\right)^{\mathrm{antiSnowballExponent}}$.
It applies only to cross-civ inflow into an above-average civilization and is tunable by strength and threshold.
War is not a hard routing gate. A besieged city becomes less prosperous, gains a flee vector, and then uses the same pull equation.
The lens scores each tile in points, as the sum of named terms: a wonder on the tile (+6), the city center (+3), each building (+2, and +1 for a completed quarter), a worked improvement (+1), the tile's own yield (+1 per 3), a river (+1), a natural wonder on the tile (+3) or next to it (+2 each), a wonder next to it (+1 each), and anything pillaged on the tile (−3 each) or next to it (−1 each). The score is absolute and banded, so a wonder tile reads the same in every settlement: Flourishing (8+), Thriving (5–7), Ordinary (2–4), Meager (0–1), Blighted (below 0). Wonders and natural wonders are recognized from the game's database, so ones added by an age or another mod count. Empty sea is excluded; worked coast counts normally.
Hovering a tile shows its band, its score, and every term behind it, then the settlement's world standing. Both the lens and the readout read the same scores from emigration-tile-score.js.
emigration-population.js converts abstract population points into representative people using the same formula as the Demographics mod, based on Civ VII's per-era growth cost:
W(N, era) = Σ cost(1..N) for era's {flat, scalar, exp}
eraParams(age, pct) = blend(prev-era, this-era params)
scaleCityPopulation = POP_K × W(size, eraParams) × megacity × overtime, soft-capped to the era max
Each age uses the game's own growth parameters, blended across age boundaries. A Modern megacity term lets the largest cities reach roughly 10–38M people; an endgame term extends growth past the normal endpoint; and a soft era ceiling limits the result.
Scaling is age-based, not turn-based, so game speed does not change the reported population. A moved point is reported as scale(pop) − scale(pop−1), with small event-level variation based on source happiness and urban/rural mix. moveRural relocates a point; removeRural removes one with no destination using the same rural-population accounting as starvation.
Aligned with Demographics. Both mods carry the same scaling and are checked bit-for-bit by
tests/scaling-demographics-parity.mjs. Turn-based migration pacing still scales by game speed (§2).
Four algorithms and a per-civ tuning table refine the baseline. All default on and can be disabled in Options. Full math and comparisons: algorithmic-improvements.md.
The original linear happiness × 6 term let extreme happiness bonuses dominate the model. The shaped version compares happiness with the world mean, saturates positive pull, steepens severe unhappiness with tanh, and uses happiness to amplify the economy rather than replace it. Franklin's Glass Armonica drops from roughly a 50× attraction effect to about 2× while deeply unhappy cities still shed strongly.
Civ VII charges no happiness per population point; tall-city unhappiness comes from density past the overcrowding threshold. Because getYield already includes the unhappiness penalty, tall cities would otherwise be penalized twice. This discount credits back density-driven unhappiness using urbanPopulation and overcrowdThreshold.
A civilization that has recently absorbed many migrants becomes a less attractive destination, based on per-capita assimilation load. integrationSpeed controls load decay and assimilationEase controls the gold cost. This provides a structural brake that cannot be overcome simply by producing more gold.
War pressure escalates from siegeFloor to full strength over siegeRampTurns (×S) and total displacement is capped at siegeLossCapPct of onset population. A city can lose substantial population but cannot be emptied by displacement alone.
A bounded registry adjusts leaders and civilizations whose abilities materially affect migration. Leader entries override civ entries. An alternate persona is treated as its own leader, because the engine
reports the _ALT type and the two personas can pull in opposite directions. Fields are happinessPull, integrationSpeed, assimilationEase, overcrowdDiscount, warRetention, and sourceBias.
Examples: Franklin happinessPull 0.75, Isabella 0.85 + ease 1.2, Xerxes ease 1.25, Khmer sourceBias 1.5, Pachacuti overcrowdDiscount 0.5, and Norman warRetention 1.4. Structural anti-runaway behavior still comes from the algorithms, not these nudges.
Brush & Blade coverage. Expansion civs and leaders are mapped to the same six fields. Conquest economies pay more to absorb spoils; defensive civs retain more population under siege; happiness magnets are damped; tall/few-settlement civs get relief from density penalties; and high-growth or food-penalized profiles get small source cushions. Civs and leaders without a migration-relevant outlier stay neutral.
Flatten knob (civTuningStrength, default 0.7). 1.0 uses the full table and 0 flattens every profile to neutral. The default preserves relative differences while shrinking them by about 30%.
When civ A attacks civ B, B's refugees prefer B's own settlements, then neutral civilizations, and treat A as a last resort. DiplomacyDeclareWar provides the declarer and target; emigration-war.js persists a victim→aggressors map until peace. ownCivRefugeeBonus and −aggressorPenalty feed geoAdjust only for cities under violence.
Two separate mechanics shape cross-civ migration.
Immigration stance (bordersEnabled). Each age adds Pro-Immigration Stance and Anti-Immigration Stance policy cards. They unlock at Citizenship (Antiquity), Economics (Exploration), and Social Question (Modern); a card whose civic is already complete when the mod is added mid-game is not granted. Internal IDs retain TRADITION_EMIG_OPEN/CLOSED_BORDERS_*.
- Pro-Immigration Stance: inbound migration ×1.5 plus +1/+2/+3 Influence.
- Anti-Immigration Stance: inbound migration ×0.4 (floor 0.15), own cross-civ outbound pull ×0.6, +2/+3/+4 Production per city, and −2/−3/−4 Influence.
Migration and retention are custom UI-VM mechanics. Influence and Production are native TraditionModifiers from data/emigration-policies-gameeffects.xml.
Diplomatic Open Borders (openBordersBonus). An active base-game Open Borders agreement increases migration both ways. Checked in emigration-geography.js; console: emigration.openBorders(aPid, bPid).
Antiquity:
| Policy (Civic) | Effects |
|---|---|
| Pro-Immigration Stance (Citizenship) | +1 Influence/turn; migration pull ×1.5 |
| Anti-Immigration Stance (Citizenship) | −2 Influence/turn, +2 Production/city; inbound ×0.4, outbound ×0.6 |
Exploration:
| Policy (Civic) | Effects |
|---|---|
| Pro-Immigration Stance (Economics) | +2 Influence/turn; migration pull ×1.5 |
| Anti-Immigration Stance (Economics) | −3 Influence/turn, +3 Production/city; inbound ×0.4, outbound ×0.6 |
| Talent Attraction (Inspiration) | +1 Science/turn; +1.5 Science pool per arrival |
| Cultural Magnetism (Society) | +1 Culture/turn; +1.5 Culture pool per arrival |
| Commercial Draw (Mercantilism) | +1 Gold/turn; +1.5 Gold pool per arrival |
| Selective Asylum (Piety) | +1 Influence/turn; refugee pull tilt |
Modern:
| Policy (Civic) | Effects |
|---|---|
| Pro-Immigration Stance (Social Question) | +3 Influence/turn; migration pull ×1.5 |
| Anti-Immigration Stance (Social Question) | −4 Influence/turn, +4 Production/city; inbound ×0.4, outbound ×0.6 |
| Talent Attraction (Modernization) | +2 Science/turn; +1.5 Science pool per arrival |
| Cultural Magnetism (Natural History) | +2 Culture/turn; +1.5 Culture pool per arrival |
| Commercial Draw (Capitalism) | +2 Gold/turn; +1.5 Gold pool per arrival |
| Refugee Compact (Political Theory) | +2 Influence/turn, +1 Culture/turn; refugee pull tilt |
Internal IDs: OPEN_BORDERS = Pro-Immigration, CLOSED_BORDERS = Anti-Immigration, TALENT = Talent Attraction, CULTPULL = Cultural Magnetism, TRADEPULL = Commercial Draw, and ASYLUM = Selective Asylum / Refugee Compact. Prefix TRADITION_EMIG_; age suffixes are _ANTIQUITY, _EXPLORATION, and _MODERN.
Attraction cards stack two yield layers:
- A fixed native yield from
data/emigration-policies-gameeffects.xml. - A carried dividend from immigrant intake (
emigration-dividend.js):
Each turn:
Defaults are dividendPerMigrant = 1.5, dividendDecay = 0.7, and dividendCap = 12 per turn per channel. The flat Influence bonus is part of the card; only the carried dividend scales with arrivals.
Floods, volcanoes, plague, hurricanes, blizzards, tornadoes, dust storms, and thunderstorms add per-city disaster distress in emigration-disasters.js. Distress decays with game-speed adjustment and lowers prosperity, producing disaster refugees. Signals come from city.isInfected and RandomEventOccurred, so they do not depend on visibility.
An event strikes the settlements owning its epicenter and the tiles within EVENT_RADIUS (1) of it; a flood also strikes every settlement on the river the epicenter lies on (MapRivers.getRiverPlots), since the floodplain runs well past that ring. Each struck settlement is then sized independently: its impact factor m is the larger of the event type's worst effect-table percentage and the share of its own plots the event newly pillaged, floored by disasterStrikeFloor for a confirmed strike. With disasterRequireDamage on (the default), a settlement the event pillaged nothing in takes no distress at all, so a Dam, a Levee, or the Khmer Baray removing a flood's damage also removes its refugees. Damage an earlier scan had already seen (a raid last turn) does not count as this event's. The distress ceiling comes from the event's class (CLASS_VOLCANO 12, CLASS_FLOOD and CLASS_PLAGUE 8, down to CLASS_THUNDERSTORM 3); an unlisted class inherits the weight of the base class its name contains as a whole word, so a mod that splits CLASS_FLOOD into variants keeps flood weight, and anything else falls back to 4.
Optional plagueCarryEnabled lets migrants from an infected city seed smaller outbreak distress at the destination.
Crisis death is tracked separately from migration. A city under lethal distress (distress >= attritionMinDistress) builds deathPressure from war, disaster, siege, or famine. Pure economic emigration cannot kill.
- Trapped: no viable destination; death pressure builds at the full rate.
- Crisis while fleeing (
crisisDeathEnabled): if refuge exists, death builds atcrisisDeathShareof the trapped rate (default 0.2), so flight remains the main outcome.
Crossing attritionThreshold (×S) removes a rural point using the game's starvation-style population write. deathRamp(crisisTenure) smooths onset from deathRampFloor (0.25) to full strength over deathRampTurns (6). Relief reduces tenure by one turn.
Crisis deaths do not count against the siege displacement cap or emigration rural floor. A prolonged crisis can therefore reduce rural population beyond normal displacement limits. Each loss records subject: tile if an improvement was abandoned or subject: counter if only the count changed. deathPressure and crisisTenure persist across saves.
starvationModifier defaults to −90; the stronger −200 value made starvation dominate prosperity before the death channel handled lethality separately.
emigration-pull.js combines prosperity with targeted attraction (tilt) and relationship permeability. asylumPushWeight pulls distressed refugees toward hospitable destinations; permOpenBorders, permAlly, and permWar scale cross-civ movement; tiltCap, permeFloor, and permeCeil bound the effect. These modify the normal pull equation rather than bypassing prosperity, geography, or congestion.
6f. Ethnic composition, integration & the per-tile lens (emigration-composition.js, emigration-ethnicity-lens.js)
Each settlement keeps a population ledger by civilization of origin, keyed by its center plot. A departure takes a slice of the source's mix and stamps it on the migrant (originMix), including across the turns in transit; the destination adds that same mix, so origins are conserved and a diaspora that moves on keeps its identity. Returnees carry their own origin home. Births add the current owner, other losses are proportional, and conquest changes the owner without rewriting origins. Migrations are matched to cities by plot, then by name for older records.
A settlement the ledger first meets already conquered from another major civilization (a mid-game install, a save from before the ledger) is seeded as its original owner's people; a captured city-state's people count as the conqueror's. A razed settlement leaves the ledger on the next pass. One held by a city-state or Independent Power keeps its entry but drops out of the settlement lists, the diversity ranking and the empire mix; a later recapture by a major civilization resumes its mix. STALE_TURNS (50) prunes anything unreadable.
The Ethnic Composition lens (Shift+E) renders that ledger tile by tile (emigration-ethnicity-distribution.js, -tiles.js, -colour.js). Each settlement's people are spread over its tiles by density (city center > urban > rural > wilderness, plus a bonus per constructible), and each foreign community gathers around a home tile and thins out with distance. Every tile carries its own mix, and its color is a blend of everyone living on it in proportion; how crowded the tile is sets how strong that color is, from a vivid civ color at a packed core to gray at the thinly settled edge. Every origin's tiles add up to its exact citywide share.
The lens is tied to the enclave rule. A community with a standing enclave lives on the enclave's tile first, which reads as its quarter whatever the community's citywide share, and the rest of the settlement fills around it. Where no enclave stands, no tile reads past quarterEstablishedShare, so a community short of an enclave shows as a real but lighter tint. The enclave tile is weighted as a built-up quarter. The border of every tile takes the settlement's majority origin. The lens and its hover readout repaint as soon as the turn's pass has recorded a new mix, including a lens left open across End Turn; the hover readout sits above the game's tooltip layer.
Integration moves a small share of each non-owner origin toward the current owner each turn (integrationRate). War with the homeland uses integrationWarRate; unrest uses integrationUnrestRate. Toggle: Options ▸ ethnic integration.
If an origin civilization is at peace with the host and its homeland is doing well, some of its diaspora can return. The move transfers real rural population from the host to a homeland settlement and preserves the returnees' true origin.
Return migration uses returnCooldownTurns plus a seeded per-pass returnRate, so outcomes vary by game but remain deterministic across reloads. Communities with an enclave have their return rate multiplied by quarterRootsReturnScale (0.25). Return migration never invents population and never targets a city-state homeland.
The Migration Chronicle turns major movements into short history entries: exodus, diaspora settlement, return home, and similar events. Entries appear in Notifications rather than a separate tab.
Rare refugee decisions can follow a major upheaval such as conquest or plague. The player can welcome the refugees, settle them on the frontier, or turn them away. Decisions can include an attributed quote from that people's refugees or migrants. They are capped per age by dilemmaMaxPerAge, use dilemmaCooldownTurns, and are dismissible. Unmet civilizations are described as hearsay rather than revealed.
6i. Cultural Enclaves (emigration-quarter.js, emigration-diaspora.js, emigration-enclave-place.js, emigration-enclave-skins.js)
A durable foreign community can form a Cultural Enclave, represented by a real improvement on a host-city tile and marked with the origin civilization's identity. Enclaves form in AI and player cities, can fade, and can be built over. Code and save-state names still use "quarter."
Formation (emigration-diaspora.js). The leading foreign origin is staged as:
established:stock >= quarterMinStock(3) and eithershare >= quarterEstablishedShare(0.30) orstock >= stockBarfoothold:stock >= 3andshare >= 0.25none: otherwise
The size qualifier is:
stockBar = quarterEstablishedStock (6) × clamp(meanPop / quarterStockRefPop (18), 0.5, 2)
This makes the requirement scale with actual settlement size rather than a fixed age table.
Established, then recognized. An enclave has two steps. The moment a community reaches the established stage, the enclave is created: its record is written, its tile is placed, the tile's yield starts, and the Chronicle announces "The {Civ} Enclave of {City}" with that yield. After the community has stayed established for quarterDwellTurns (8), with quarterDwellGrace (3) turns of tolerance below the bar, the enclave is recognized: the host takes a stance and that stance pays out once. If a different origin becomes dominant, the dwell clock resets. Normal integration erodes the minority, so an enclave that is not renewed by inflow fades (see Fade) whether or not it was ever recognized.
Per-age pacing (emigration-enclave-pacing.js). While a host has formed fewer than quarterTargetPerAge (1), requirements relax with age progress:
relax = quarterPacingMax (0.4) × clamp(ageProgress / quarterPacingBy (0.6), 0, 1)
shareBar = quarterEstablishedShare × (1 − relax)
sizeBar = stockBar × (1 − relax)
dwell = round(quarterDwellTurns × (1 − relax)), at least 2
The standing-points floor never relaxes. Forming an enclave restores the normal bars. quarterCapPerAge (3) hard-caps formations per host per age. quarterPacingEnabled disables this relaxation.
Recognition (quarterRecognition).
Creation is always automatic for the hosts a mode covers; the mode decides who is covered and how the stance is chosen at recognition.
2(default): every civilization; the stance is the origin's first one the host can afford (else its second, else "let them be"), chosen automatically, at most one recognition per host per pass.1: the local player's cities only, stance chosen automatically.0: the local player's cities only; recognition is a player decision offering two identity-based stances plus "let them be."
Each origin can have at most two enclaves per host, and each tile can hold only one enclave.
Tile skin (enclaveTileSkin, emigration-enclave-skins.js). The default THEMED mode tries, in order:
- The origin civilization's own unique improvement, when available and valid in the current age.
- Another civilization's improvement with a matching yield family.
- The base Village, which receives +2 Culture inside a major civilization's city.
Mode 2 always uses the Village. Mode 0 uses generated, never-buildable per-civ enclave improvement types without custom 3D models.
Placement prefers a nearby empty flat/hill plot, creating a rural district first when needed. If none is available, it replaces an outlying farmstead, preferring plain tiles before resources and farther plots before nearer ones. Terrain and age restrictions still apply. The marker reads the enclave record, so even a generic Village remains attributed to its true origin.
Tile yield plus a one-time stance payout (emigration-stance-payout.js). The tile carries its native yield from the day the enclave is established. When the enclave is recognized, its stance pays once: Culture (toward the civic being researched), Science (toward the tech being researched), Influence, or Gold. A stance that does not pay Gold costs Gold; a Gold stance costs nothing. The amounts are sized to the host's own economy, so a stance is worth the same share of an empire in every age and at every speed:
payout = S × max(floor[age], quarterStanceTurns (3) × host's income of that yield per turn)
Gold = S × max(floor[age], quarterGoldStanceTurns (1.5) × host's Gold income) (a Gold stance, free)
price = S × max(floor[age] × quarterStanceCostFloorScale (1.5), quarterStanceCostTurns (2) × host's Gold income)
floor is quarterStanceFloor (60 / 150 / 300 for Antiquity / Exploration / Modern), S is the game-speed scalar, and every figure rounds to the nearest 5. For example, an Exploration empire earning 221 Culture and 705 Gold a turn is offered +665 Culture for −1,410 Gold. Each button in the decision pop-up shows its stance's payout and price with the yields' own icons; a stance the host cannot afford is grayed out and says so. An established enclave has no stance, so it grants and costs nothing beyond its tile. Enclaves recognized in an older version keep their small per-turn stance yields. If replacement changes the plot's effective yield, the shortfall is stored as placed.compensation and granted while the tile remains.
Enclave tooltip and marker (emigration-enclave-tooltip.js, -enclave-tooltip-data.js, -enclave-yields.js). Hovering an enclave's tile replaces the game's tooltip for the borrowed improvement with the enclave's own: its name and settlement; its stage (established, with the turns left to recognition; recognized, with the stance; contested; or fading, with the turns left); and where the yields come from, source by source with a reason: the land and any improvement that stood there before (repaid every turn), the enclave's own works, and the stance, including its wartime cut. A total gives what the tile brings in each turn. The map marker carries a second line with the stage (Established, Recognized, Contested, Fading). Markers are cleared before each redraw, and a burst of map events triggers a single redraw.
Built over. After a two-turn grace period, an enclave whose tile has been replaced is retired and recorded in the Chronicle. A failed placement is also written off. Departures never choose an enclave tile. Pillage damages but does not remove it; razing the city does.
Fade (quarterFadeShare, quarterFadeTurns). The fade clock runs while both origin share and stock remain below their thresholds. After quarterFadeTurns (12), the tile and record are cleared. Crossing either bar resets the clock. quarterFadeShare = 0 makes enclaves permanent. With normal integration and no new inflow, a community typically fades in roughly 35–45 turns.
Contested in war. If host and homeland are at war when the enclave is recognized, the stance payout is multiplied by contestedQuarterYieldFactor (default 0.5; the price is unchanged). While they are at war, the host takes contestedQuarterPenalty (4) happiness strain per enclave, capped by diasporaWarStrainCap (12), which ends at peace.
Measured pace (2026-09-13, devtools/engine-probe/, mod test 31). In a 40-turn, seven-civ Exploration test, 19 of 84 settlements had a foreign minority, three or four were near foothold at a time, and three enclaves formed, all in AI cities: roughly one enclave every 13 world turns. The synthetic tile-transfer-stress.mjs harness is a calibration floor, not a forecast.
6j. Calling people home (emigration-call-home.js, -call-home-action.js, -call-home-view.js, callHomeEnabled)
A civilization can pay Gold or Influence to bring its displaced people back to the settlement they fled. The dialog offers a ladder of sizes for each currency (one person, about half of what is callable, everyone, up to callHomeMaxPointsPerAttempt = 3), priced with the game's own Gold and Influence icons. A size the treasury cannot cover stays in the list, grayed out, with a tooltip giving the price and the balance.
- From your own settlements it is a purchase: the pop-up names the settlement people are pulled back from and the one they return to, and exactly the number paid for come.
- From abroad it is a gamble: the pop-up names the foreign city and its ruler and gives the odds per person (
callHomeChanceExternal, 18%). The call is paid for at the chosen size whether or not anyone answers; each person asked is rolled, and the call may bring nobody. An unanswered call is recorded in the Chronicle and still starts the cooldown. - Price:
callHomeGoldPerPoint(60) orcallHomeInfluencePerPoint(12) for one person, climbing as n^1.5 (two people cost nearly three times one, three about five times), ×callHomeExternalCostScale(1.5) abroad, and ×callHomeAgeCostStep(3) per age past Antiquity ("Price climb per age": ×3 in Exploration, ×9 in Modern). - Cadence:
callHomeCooldownTurns(8) per civilization. WithcallHomeOfferWhenCalm("Call-home offers" on the Mods tab) the call is offered once no settlement is in distress and people are still away.
Each dialog closes on a homecoming epigraph in the caller's own civilization's voice (emigration-return-quotes.js), from Sinuhe's recall to Egypt to the Ten Thousand's "The sea! The sea!", with a general pool for civilizations without their own; every line was read in its source.
7a. Departures and arrivals made real (emigration-departure-tile.js, emigration-arrival-placement.js)
In-game testing on 2026-09-11 showed that addRuralPopulation(−1) lowers a counter but leaves the improvement and its yields in place. DESTROY_ELEMENT on a rural improvement removes both the tile and population point, including in AI cities. The mod therefore uses the tile write for departures.
- Departure (
departureRemovesTile, default on): after the move is reserved and the destination receives the point,commitSourcePointdestroys one outlying rural improvement. The source pays no extra treasury cost; the destination pays assimilation costs. Death uses the same tile removal above the rural floor and falls back to the counter when no rural improvement is available. - Plot cleanup: destroying an improvement leaves its rural district behind, which blocks future population placement.
emigration-plot-cleanup.jsremoves the empty district shortly afterward and sweeps for leftovers on load and at the start of the local player's turns. - Tile choice: pillaged improvements go first. During starvation, food tiles go last. Otherwise, plain tiles precede resource tiles and more distant plots are preferred.
- Existing brakes still apply: migration pressure, cooldowns, per-city caps,
warSurgeMax,siegeLossCapPct, inbound caps, and anti-snowball logic are unchanged.disasterLossCapPct(0.5) provides the equivalent cumulative cap for a single disaster crisis. - Arrival (
arrivalPlacement):addRuralPopulation(+1)creates a pending population placement. AI cities resolve it themselves. For the local player:1: automatic, using the game'sEXPANDcommand;arrivalPreferSpecialistscan try a district slot first2: ask (default), with a native "Newcomers" pop-up3: create a local-player Migrant unit0: leave placement to the base game's normal prompt
In ask mode, arrivalAskRefugees, arrivalAskMigrants, and arrivalAskReturnees control which arrival types prompt the player. Refugee prompts are on by default and can include an attributed quote from the newcomers' origin civilization.
Because Civ VII does not charge directly for population, the mod adds integration feedback through Players.grantYield(pid, YIELD_X, −amount). Gold can be deducted cross-civ. YIELD_HAPPINESS reduces the civilization's lifetime happiness stockpile, delaying the next Celebration; it does not make a settlement locally unhappy.
- Assimilation cost: each migrant adds load based on
assimilationLoadPerMigrantand destination population. Load decays throughassimilationDecay, optionally modified byintegrationSpeed. Per-turn happiness and gold costs scale with the remaining load; gold can also scale withassimilationEase. - Migrant-unit holding: unsettled
UNIT_MIGRANTunits cost their owner each turn. - Congestion headwind:
congestionPenaltyandassimLoadForlower the pull of heavily burdened destinations. - Carried dividend: attraction policies can convert arrivals into a decaying positive yield pool (
emigration-dividend.js).
All costs run on each civilization's own turn. Set the relevant knob to 0 to disable a cost.
When Demographics is installed, Emigration registers through globalThis.DemographicsMetricsAPI using an order-independent handshake.
- Top-level Emigration tab: the Data section includes metric and unit toggles for Scaled people or raw Civ numbers.
- Net Migration (Graph): cumulative arrivals minus departures by civilization.
- Net Migration (Table): the same value in table form with a diverging bar, beside the movement behind it in three groups, each counting people who Left and people who Arrived: Internal (moves between a civilization's own settlements), External (moves across a border, which is what Net measures) and Total (Internal plus External).
- Emigration / Immigration: gross outflow and inflow, including cause breakdowns.
- Refugees (Left / Arrived): displaced people sent or received, with optional war and disaster onset markers.
- Dashboard sub-tabs: Network, Causes, Settlements, Diversity, Immigration Policies, Notifications, and Guide. Registration is a silent no-op on older Demographics versions.
- Migration Chronicle: persisted prose for major movements, mirrored into Notifications instead of a separate tab.
- Cause drill-down: broad causes expand to named wars, disasters, or age-crisis mechanisms with emigration and death counts. Per-civ event tallies are persisted in
outByEventanddeathsByEvent. - Notifications: a permanent, expandable log of fired migration notifications with cause, named event, source, destination, and count.
- Ethnic Composition lens: Shift+E renders the population-origin mosaic; the plot tooltip adds exact origin percentages. Both follow the shared visibility policy (§10).
- War-effects tooltip: Demographics can show a Refugees row through
globalThis.EmigrationData.refugeesCumFor. - Timeline-detail note: the Network tab warns when snapshots are coarser than one turn.
EmigrationData exposes per-civ net, gross in/out, the internal (within-civ) share of that movement, refugees, deaths,
and cause breakdowns. Without Demographics, registration is a silent no-op.
On the network diagram, clicking a city highlights just its migrant flows: arrows are drawn only for moves into or out of that city (even with "Migrant flows" off), a gold ring marks it, and its residents, arrivals and leavers stay lit while everything else dims; clicking it again or empty space clears the selection, and clicking a civilization's outer ring isolates the whole civilization. Pressing and dragging a settlement's circle pulls it out of its civilization's circle, which grows to keep it inside; dragging elsewhere in the circle moves the whole group. Flow arrows follow whatever they are attached to, and an arrow between two adjacent city circles shortens and bows to fit the gap.
The network visualization animates only movers. Cross-civ migrants travel from their origin civ or origin sub-cluster to the destination; intra-civ migrants travel between settlements. Home-grown population appears in place. Origin lookup uses nullish coalescing so node index 0 remains valid.
Migration appears through HUD toasts, world news for major events, and the persistent Notifications log. Toasts use Civ VII-style typography and framing, stack vertically, remain on screen for about 11 seconds, and are themed by cause. Green is reserved for gains in your own cities; losses and departures use neutral, amber, or crisis colors.
Counts are shown in both raw population points and scaled people, for example "3 population points (36,000 people)". The default notification mode is intentionally selective; every fired notification still goes to the log.
- Named events (
emigration-naming.js): disasters use base-game event names, wars reuse the war name when available, and conquest names the affected city. - Per-event explanation (
emigration-feedback.js,emigration-causes.js): losses are split into specific source/cause events with accurate counts. Only the largest event may toast during a pass, but all are logged. - City readout (
emigration-city-readout.js): shows pressure mix, current status, likely destination, integration cost, enclave progress, civ net migration, guidance, and trapped/at-risk warnings. It is built from the recomputedcitySnapshotand works without Demographics. - Dashboard window (
emigration-window.js): standalone view of the migration network, cross-civ flows, per-civ ledger, cause breakdown, policy stances, and city pressure. The same render core backs the Demographics integration. - Enclaves: when an enclave in one of your cities is established, is recognized, fades, or is built over, a notification gives the yields it adds or takes away, for example "(+3 Culture)". Enclaves in other civilizations' cities go to the Notifications log only.
- Advice: each loss pop-up ends with one plain sentence in the game's terms ("Raise Roma's Happiness to stop its people leaving"). The Anti-Immigration Stance is suggested only where it helps: people chose to leave for another civilization and the card is not slotted.
- Layering: the lens hover readouts and the enclave tooltip sit above the game's tooltip layer, and the mod's own toast sits above them. While a lens is up, the game's tile tooltip stays hidden until the lens is turned off.
- Anti-spam: disaster severity threshold, refugee milestones, and global
notifyCooldownTurns.notifyMode:0off,1important-only (default),2verbose.
All settings live under Options → Add-ons (the game's tab for mods) in both the main menu and in-game Options. emigration.modinfo loads the options layer in both shell and game scope. Settings use the shared modSettings localStorage store and apply at boot and immediately where supported.
- Emigration: population unit of measurement, intensity preset, the grouped sliders (movement between civilizations; refugees from conflict overall, from major-power wars and from minor-power raids), dashboard data source, timeline detail, dashboard dock button, the on/off switches for notifications, ethnic integration and return migration, the four decision pop-ups (Refugee decisions, Newcomer placement decisions, Call-home offers, Enclave stance decisions), and last, an Advanced settings button. Each decision switch sets the same value as its Advanced setting (
arrivalPlacementask/automatic,callHomeOfferWhenCalm,quarterRecognition0/2), and the two stay in step; with a switch off the city makes that choice itself, and with Call-home offers off the call is not offered. Hovering any option shows a full explanation: what it does, what each choice or slider position means (with the limits behind Low, Medium and High), when a change takes effect, and whether it changes the simulation or only the display. The full text is English; other languages show their shorter text. - Advanced settings window: every individual setting from
emigration-tunables.js(121), in collapsible sections: Pacing, Scope, Border policies, Prosperity model, War & violence, Disasters, Geography & movement, Integration costs, Balance brakes, Arrivals & departures, Cultural enclaves, Calling people home, Attrition, Notifications, Readouts & rankings, and Visuals. Sections start collapsed and the ones you open stay open. Values show their units (55%, ×1.5, 3 turns) or a word (Off, Instant); a value set between the choices by a slider is listed exactly. The window also has search, a changed-setting marker, per-setting reset and Reset all.
Game-speed scaling is automatic. Internal flags (gameSpeedTuningEnabled on; gameSpeedScalePopulation off) exist for rollback and QA rather than player tuning.
Simulation scope and visibility are separate. By default, every alive major civilization is simulated from turn one so migration topology does not depend on exploration. Scope can be reduced to met civilizations for performance. Dashboard and lens visibility follow a separate analytics policy: All / Met-only / Own-civ / Disabled, host-authoritative in multiplayer and met-only by default.
Defaults live in emigration-config.js. Population-scaling constants are not exposed because they must remain aligned with Demographics.
Reference material on architecture, persistence, localization, development, and compatibility.
Modules are kept small and single-purpose (≤500-line gate). emigration.modinfo's ImportFiles manifest is tested as the deployed UI inventory. Key modules:
ui/emigration-main.js: UIScript entry point, turn hook, costs, events, reporting, feedback, dev dock, boot.ui/emigration-config.js/-config-types.js: defaults, scaling constants, andEmigrationConfig.ui/emigration-game-speed.js: speed scalar andspeedTurns/speedBar/speedDecay/speedScaleTurn.ui/emigration-causes.js: cause taxonomy and labels, permanence, hints, refugee classification.ui/emigration-tunables.js: exposed settings and Low/Medium/High presets.ui/emigration-cities.js: buildsCitySignalrecords.ui/emigration-prosperity.js: prosperity, shaped happiness, overcrowding, anddistress.ui/emigration-violence.js/-violence-signals.js: violence accumulation/decay, siege escalation/cap, and polled combat signals.ui/emigration-disasters.js: disaster distress and optional plague carry.ui/emigration-geography.js: distance, flee vector, aggressor preference, Open Borders bonus.ui/emigration-civ-tuning.js/-war.js: leader/civ tuning and aggressor map.ui/emigration-borders.js: immigration openness, retention, attraction yields, asylum state.ui/emigration-effects.js/-dividend.js/-migrant-units.js: assimilation, congestion, dividends, migrant-unit costs.ui/emigration-departure-tile.js: chooses and removes the source rural improvement after a committed move; deaths use the same path where possible.ui/emigration-arrival-placement.js: local-player arrival handling, automaticEXPAND, newcomer pop-up, and Migrant-unit mode.ui/emigration-quarter.js/-quarter-state.js/-quarter-registry.js/-quarter-bonuses.js/-diaspora.js: Cultural Enclave staging, dwell, recognition, fade, contested state, persistence, and civ-specific stances.ui/emigration-enclave-place.js/-enclave-skins.js/-enclave-markers.js: enclave tile selection, placement, skinning, removal, yields, and map markers.scripts/gen-enclave-improvements.mjs: generates never-buildable per-civ enclave improvement data and text; Village fallback data lives indata/emigration-enclave-village*.xml.ui/emigration-engine.js: main pass, ranking, concurrent tracks, budgets, transit, and attrition outlet.ui/emigration-arrivals.js: delayed arrival processing.ui/emigration-pull.js: destination scoring and migration cause.ui/emigration-state.js: pressure, cooldown, scaling-turn, and population-total persistence.ui/emigration-population.js: population reads/writes and Demographics scaling.ui/emigration-migration-stats.js/-migration-records.js: tallies, recent moves,EmigrationData, and record types.ui/emigration-city-readout-data.js/-city-readout.js: city snapshot and readout UI.ui/emigration-views.js/-ledger-view.js/-window.js: shared dashboard renderer and standalone window.ui/emigration-network-viz.js: animated network; movers animate from origin, residents appear in place.ui/emigration-migration-page.js/-demographics.js: Demographics registration and graph definitions.ui/emigration-prosperity-lens.js/-prosperity-tooltip.js/-tile-score.js: Prosperity lens, tooltip, and the per-tile point scores.ui/emigration-built.js: the built environment (wonders and civic building kinds) as a reason to stay.ui/emigration-ethnicity-lens.js/-ethnicity-tooltip.js/-composition.js: origin ledger, Ethnic Composition lens, and tooltip.ui/emigration-ethnicity-distribution.js/-ethnicity-tiles.js/-ethnicity-colour.js: the lens's per-tile distribution, engine reads, and blended color.ui/emigration-lens-hover-panel.js/-plot-tooltip-suppress.js: the shared cursor readout for both lenses and the base tooltip suppression.ui/emigration-enclave-tooltip.js/-enclave-tooltip-data.js/-enclave-yields.js: the enclave's own tooltip and its per-source yields.ui/emigration-call-home.js/-call-home-action.js/-call-home-view.js/-return-quotes.js: calling people home (rules and odds, the paid action, the dialog, the epigraphs).ui/emigration-internal-tally.js: the internal (within-civ) tallies behind the Net Migration table's Internal / External columns.ui/emigration-naming.js/-feedback.js/-events.js/-report.js/-log.js: event naming, notifications, event handling, reporting, and dev logs.ui/emigration-notifications.js/-notifications-view.js: persistent notification log.ui/emigration-settings.js/-options.js/ui/options/*: settings, presets, options UI, and sharedmodSettingsstore.data/emigration-policies-*.xml,-policies-gameeffects.xml,-policy-icons.xml,-civilopedia.xml: policy cards, native effects, icons, and Civilopedia content.devtools/migration-probe.js: dev-only API probe.devtools/engine-probe/: automated in-game probe harness and recorded engine verdicts.scripts/tile-transfer-stress.mjs: synthetic balance/stress harness for tile movement, disasters, composition, and enclave recognition.text/<locale>/ModText.xml+scripts/i18n_*.mjs: localized strings and localization tooling (§14).
emigration.modinfo loads options in shell and game scope, the UI modules through ImportFiles, Civilopedia and native policy effects through <UpdateDatabase>, icons through <UpdateIcons>, and age-specific policy databases through three AgeInUse groups. Age-specific loading is required because Civ VII rebuilds the gameplay database each age.
The Civilopedia adds an Emigration section, grouped like the game's Game Concepts: the overview, FAQ and About pages; How People Move (Prosperity, departures and arrivals, war and refugees, disasters, crisis deaths, integration, ethnicity, enclaves, calling people home, the Chronicle and decisions, leader and civilization tuning); Policy & Diplomacy (stances, attraction and asylum, talent raids, and a list of every card by age); Interface & Options (dashboard, lenses, notifications and readouts, visibility, options, Demographics); and Voices of the Displaced, one page per origin civilization listing every quotation the mod can show for that people and who the speaker was. The single-body pages live in ModText.xml and its translations (§14). The chaptered pages are en_us-only in text/en_us/PediaText.xml, and the Voices pages are generated into data/emigration-civilopedia-voices.xml + text/en_us/PediaVoicesText.xml by node --loader ./tests/loader.mjs scripts/gen-pedia-voices.mjs from the quote registries and scripts/pedia-voices-people.json (one note per speaker; the generator fails when a speaker has none).
The mod runs in Civ VII's UI VM (GameFace JS) and applies migration, costs, and reporting during play. Companion probe notes record the engine behavior behind these choices.
city.addRuralPopulation(±1): cross-civ population-counter write.+1creates a real pending placement.−1lowers only the counter and leaves the tile working, so departures useDESTROY_ELEMENT.DESTROY_ELEMENTon a constructible: removes a rural improvement and its population point together, including in foreign cities. This is the departure write.Game.CityCommands.sendRequest(..., EXPAND, ...): places a pending point on a valid local-player plot.CREATE_ELEMENTwithKind:"UNIT"can create a Migrant unit for the local player only.CREATE_ELEMENTfor constructibles/districts: can place improvements and rural districts in any civilization's city, including never-buildable custom types and foreign unique improvements. Terrain and age restrictions still apply.PlayerOperations.canStartis not a reliable validity check.- Per-plot yields:
GameplayMap.getYields(plotIndex, playerId)returns yield pairs. Native improvement yields appear on the plot and in city output shortly after placement. - Constructible damage: probe tests found no script path to damage an improvement. Script can create or destroy it, but not pillage it directly.
- Rural-district cleanup: destroying an improvement leaves its rural district, which then blocks
EXPAND. Destroying that empty district restores the plot without changing ownership or population. - Unknown player IDs: some engine calls can crash natively. Every player-keyed call is guarded with
Players.get(pid). - Autoplay: multi-turn
Autoplay.setTurns(N)skips local turn activation, so the migration pass does not run. One-turn autoplay and script-ended turns do. Players.grantYield: supports cross-civ positive and negative yield writes.YIELD_HAPPINESSchanges the civilization's Celebration stockpile, not local settlement happiness.- War aggressor:
DiplomacyDeclareWarexposes declarer and target. - Game speed: read through
Configuration.getGame().gameSpeedTypeandGameInfo.GameSpeeds.lookup(type).CostMultiplier. - Fog-independent reads: district health, infection, active traditions, random-event definitions, leader/civ type, per-plot yields, and per-civ population are readable for all players.
- Economic yields include unhappiness penalties:
city.Yields.getNetYieldandgetYieldreturn the post-penalty values observed in probes. - Happiness economy: population itself has zero happiness upkeep;
OVERCROWDING_THRESHOLD = 2, which underpins Algorithm B.
The UI VM cannot create units for other civilizations or raise a new clickable engine notification type without a database row. Policy cards are the only system here that requires database content; most gameplay behavior uses direct runtime mutators.
Per-game state is stored in GameConfiguration and survives save/reload:
EmigrationState_v1: voluntary/crisis pressure, cooldowns, and scaling turnEmigrationViolence_v2: violence, decay, siege tenure, onset population, cumulative war lossEmigrationDisaster_v1: disaster distress and decayEmigrationAssim_v1: assimilation load and tick turnEmigrationDividend_v1: carried-dividend poolsEmigrationWar_v1: victim → aggressors mapEmigrationEthnos_v1: settlement origin-composition ledger, with the turn of its latest pass (passTurn)EmigrationEthnosStamp_v1: a counter bumped on every ledger save, which the lens and readouts use to notice a new pass without re-reading the ledgerEmigrationMigStats_v1: net/gross migration, refugees, deaths, cause breakdowns, sample watermarks, and capped city-pair flow matricesEmigrationNews_v1: world-news milestones and last-toast turnEmigrationNotif_v1: capped persistent notification log with cause, turn, summary, count, source, and destination
Missing fields are normalized on load for backward compatibility. Options persist separately in the shared modSettings localStorage key. Gameplay writes are single-player/client-side.
All user-facing strings use LOC keys and are translated into 11 locale families (en, de, es, fr, it, ja, ko, pl, pt, ru, zh)
across 12 locale folders (zh ships Simplified and Traditional), including advanced option labels. Non-English XML is generated:
node scripts/i18n_extract.mjs # text/en_us/ModText.xml → i18n/i18n-source.json
node scripts/i18n_apply.mjs # i18n/<locale>.json → text/<locale>/ModText.xmlAuthor English in text/en_us/ModText.xml; translations live in i18n/<locale>.json and fall back to English when a key is missing. npm run verify includes a parity test that fails if any English key is absent from a locale. Placeholders and code tokens are preserved verbatim.
The mod is typed JavaScript with JSDoc and no build step; shipped code is the source code. See CONTRIBUTING.md.
Before committing:
npm install
npm run verifyverify runs TypeScript checking, ESLint, modularization gates, and the Node test harnesses. Coverage includes game-speed scaling, notifications, network animation, end-to-end engine passes, pull/routing, causes, city readout, views, Demographics integration, population scaling, prosperity, geography, violence/siege caps, tunables, migration statistics, flow history, composition, visibility masking, world scope, effects, civ tuning, war, disasters, borders, naming, feedback, dividends, raid handling, modinfo/import closure, localization parity, Civilopedia page resolution, and empty-catch checks.
Generated content is regenerated, never hand-edited:
node scripts/gen-enclave-improvements.mjs # enclave improvements + icons + EnclaveText.xml
node --loader ./tests/loader.mjs scripts/sync-quote-rows.mjs # the quote LOC rows in en_us
node --loader ./tests/loader.mjs scripts/gen-pedia-voices.mjs # the Voices of the Displaced Civilopedia pagesAll three read the quote and bonus registries, so adding a quotation is a registry edit plus a regeneration. gen-pedia-voices.mjs also needs a one-line note on the speaker in scripts/pedia-voices-people.json and refuses to run without one. tests/pedia-pages.mjs then walks every Civilopedia page the way the engine does and fails on one that would render as a bare title.
./release.sh creates the debug-muted, allow-listed Workshop zip with readable, unminified JavaScript.
migration-probe is a separate dev-only mod used to verify engine APIs and data behavior. Its console commands test war names, happiness writes, save-state size, and raw war-event payloads.
The only dependency is base-standard. Shared surfaces are handled additively:
- Database inserts only: policy and Civilopedia data use namespaced
<Row>inserts; no base/shared rows are replaced, updated, or deleted. - Shared settings store: options use the community
modSettingslocalStorage key. The store self-heals stray top-level keys without disturbing compliant mods such as Demographics (ui/options/mod-options.js). - Cooperative globals and events: globals are namespaced (
globalThis.emigration,EmigrationData). Demographics integration joinsDemographicsMetricsAPIrather than replacing it. Engine event subscriptions are multicast. - Demographics-owned strings stay there: Emigration avoids duplicating shared war/refugee labels.
- Additive plot tooltips: Prosperity and Ethnic Composition append content through a
MutationObserverrather than replacing the base tooltip, allowing coexistence with tooltip mods. - Adaptive reads: prosperity uses live yields, happiness, and Influence each turn, so balance mods feed into the model naturally. Population/yield changes from other mods stack with Emigration. Game-speed scaling uses the active
GameSpeedrow, including custom speeds with aCostMultiplier.
- Measured at scale: on a seven-civ Exploration save, 30 turns with default settings produced a largest gainer 18% above the no-mod population, a volcano-hit settlement at 96% of its no-mod size, and the largest civilization's world share within 0.5 percentage points of baseline. At maximum cross-civ movement, those figures shifted to +37% and 84%. An 80-turn run through an age transition completed without crashes and kept world population within 1% of baseline.
Open source on GitHub: https://github.com/tmtmiller1/civilizationvii-emigration
- Tower, for design and Civilization VII implementation.
- Tomahawk, Mk Z, and Tim_The_Texan, creators of the Civilization V Emigration mod that inspired this project.
- Potato McWhisky, for bringing me back to Civilization through Civ VI after growing up with Civilization II, IV, and V. This mod is partly an act of faith that the community can help make Civilization VII as good as the earlier entries.
MIT. See LICENSE.













