Home › Dev CE (experimental) › PlayerCulture

Experimental: needs Dev CE (fork of the Community Extension, release zip and source). A GameCore that adds these Lua methods by calling engine functions directly. Not part of the upstream Community Extension, single-player testing only. Descriptions on these pages were written by an AI assistant from decompiled code and are marked inferred unless a test result confirms them.

PlayerCulture

49 methods (Dev CE test build). this is found with the game's own IPlayerCulture instance resolver (virtual).

Method Signature Purpose (AI-written, inferred) Tested Engine function Current
AddTourismAtPlot PlayerCulture:AddTourismAtPlot(iPlotIndex: integer, iTourism: integer) Adds tourism to a plot's entry in the player's tourism location list, scaled by a percentage modifier (floor -100%). plumbing ✓
this ✓ (oracle)
Player::Culture::AddTourismAtPlot 0x279f60
AddTourismIntoFavor PlayerCulture:AddTourismIntoFavor(iTourism: integer, iFavor: integer) Appends an (iTourism, iFavor) pair to a per-player list at offset 0x6a8 that defines a tourism-into-favor conversion entry. plumbing ✓
this ✓ (oracle)
— 0x27a090
ApplyTourism PlayerCulture:ApplyTourism() For every player in the manager's list that this player has met, computes tourism towards them and adds it to the accumulated per-player tourism array, then recomputes. plumbing ✓
this ✓ (oracle)
— 0x27a0e0
CanFillAllPolicySlots PlayerCulture:CanFillAllPolicySlots() → boolean Checks whether the player has enough unlocked policies to fill every government slot, matching slot types and wildcard slots. Decompilation is truncated before the final result. plumbing ✓
this ✓ (oracle)
— 0x27b790
CanTriggerBoost PlayerCulture:CanTriggerBoost(eCivic: integer) → boolean Returns false if the civic is already known, otherwise finds the boost whose civic type matches and asks Boost::Manager::CanTriggerThisBoost whether the Eureka can fire. plumbing ✓
this ✓ (oracle)
— 0x27be50
ChangeGreatWorkObjectTourismModifier PlayerCulture:ChangeGreatWorkObjectTourismModifier(eGreatWorkObjectType: integer, iModDelta: integer) Adds a delta to the tourism modifier for a specific great work object type, if that type already has a record. plumbing ✓
ran ±1, reversible ✓, no visible effect
this ✓ (oracle)
Player::Culture::ChangeGreatWorkObjectTourismModifier 0x27c0b0
ChangeTourismModifier PlayerCulture:ChangeTourismModifier(iDelta: integer) Adds a delta to the tourism modifier stored at Culture+0x508. plumbing ✓
this ✓ (oracle)
— 0x27c110
ChangeTourismOn PlayerCulture:ChangeTourismOn(ePlayer: integer, xValue: number (fixed point, 1/256 steps)) Adds a delta to this player's accumulated tourism toward another player. plumbing ✓
this ✓ (oracle)
Player::Culture::ChangeTourismOn 0x27c140
CheckForDominance PlayerCulture:CheckForDominance() For each other player, compares this player's tourist count with that player's culture-based threshold to set/clear a dominance flag and turns-until-dominant, sending notifications to both players when dominance is first reached. Decompilation is truncated near the end. plumbing ✓
this ✓ (oracle)
— 0x27c190
ComputeTourismOn PlayerCulture:ComputeTourismOn(ePlayer: integer) → number Computes fixed-point tourism this player generates towards the given player from city religious and non-religious tourism, era differences, trade routes, open borders, government modifiers and monopolies. Decompilation truncated at the end. plumbing ✓
this ✓ (oracle)
— 0x27ca80
ComputeTurnsUntilDominant PlayerCulture:ComputeTurnsUntilDominant(ePlayer: integer) → number Estimates turns until this player becomes culturally dominant over ePlayer from the tourist totals and the other player's culture values; -1 if not progressing. plumbing ✓
this ✓ (oracle)
— 0x27cf40
DoCulture PlayerCulture:DoCulture() Per-turn culture processing: records last culture rate, handles pending government change, computes culture yield, advances civic progress, and for major players runs tourism recording/application/distribution and dominance checks, then era flags, obsolete policies and notifications. Decompilation truncated. plumbing ✓
this ✓ (oracle)
— 0x27d3c0
DoEndTurn PlayerCulture:DoEndTurn() Clears the boolean flag at Culture+0x450 at end of turn. plumbing ✓
this ✓ (oracle)
— 0x27d770
GetFavorPerTurnFromBuilding PlayerCulture:GetFavorPerTurnFromBuilding(eBuilding: integer) → number Looks up a building in a list of (building, favor) pairs at Culture+0x7a8 and returns its favor per turn. plumbing ✓
this ✓ (oracle)
— 0x27e1e0
GetFavorPerTurnFromPolicy PlayerCulture:GetFavorPerTurnFromPolicy(ePolicy: integer) → number Looks up a policy in a list of (policy, favor) pairs at Culture+0x778 and returns its favor per turn. plumbing ✓
this ✓ (oracle)
— 0x27e210
GetGreatWorkObjectNoTourism PlayerCulture:GetGreatWorkObjectNoTourism(eGreatWorkObjectType: integer) → boolean Looks up a great work object type in the 12-byte entry list at Culture+0x748 and returns its no-tourism flag. plumbing ✓
this ✓ (oracle)
— 0x27e240
GetGreatWorkObjectTourismModifier PlayerCulture:GetGreatWorkObjectTourismModifier(eGreatWorkObjectType: integer) → number Looks up a great work object type in the 12-byte entry list at Culture+0x748 and returns its tourism modifier. plumbing ✓
this ✓ (oracle)
— 0x27e270
GetNumCivicsCompleted PlayerCulture:GetNumCivicsCompleted(bIncludeRepeatedCivics: boolean) → number Counts civics that are completed for the player; optionally adds the repeated-civics counter at Culture+0x850. plumbing ✓
this ✓ (oracle)
— 0x27e2e0
GetNumPoliciesEnactedSpecificType PlayerCulture:GetNumPoliciesEnactedSpecificType(eGovernmentSlot: integer) → number Counts currently slotted policies whose definition's slot type equals eGovernmentSlot. plumbing ✓
this ✓ (oracle)
— 0x27e390
GetNumPoliciesUnlocked PlayerCulture:GetNumPoliciesUnlocked() → number Counts policies for which IsPolicyUnlocked is true. plumbing ✓
this ✓ (oracle)
— 0x27e4e0
GetNumPolicySlotsOpen PlayerCulture:GetNumPolicySlotsOpen() → number Counts empty policy slots (entries with policy -1) in the slot array at Culture+0x1a0; 0 if no government. plumbing ✓
this ✓ (oracle)
— 0x27e590
GetOtherGovernmentIntoleranceModifier PlayerCulture:GetOtherGovernmentIntoleranceModifier(GovA: integer, GovB: integer) → number Returns the intolerance modifier at Culture+0x838 when two different governments have equal-sized lists (vector at definition+0x90), else 0. plumbing ✓
this ✓ (oracle)
— 0x27e640
GetSlotType PlayerCulture:GetSlotType(iSlotIndex: integer) → number Returns the government slot type of the slot at the given index. plumbing ✓
this ✓ (oracle)
— 0x27e940
GetTourismNationalParksModifier PlayerCulture:GetTourismNationalParksModifier() → number Returns a fixed-point multiplier 1.0 + (Culture+0x730)/100 for national parks tourism; 1.0 if the value is not positive. plumbing ✓
this ✓ (oracle)
— 0x27fe90
GetTouristsTo PlayerCulture:GetTouristsTo() → number Sums per-player tourist counts (array at Culture+0x5f8) over all other players this player has met. plumbing ✓
this ✓ (oracle)
— 0x2805b0
GrantRandomBoostGoodyHut PlayerCulture:GrantRandomBoostGoodyHut(iNumFreeBoosts: integer, eSource: integer) Grants a number of random civic boosts (from the era of the cheapest not-yet-owned civic) to a player, used by goody huts. plumbing ✓
this ✓ (oracle)
Player::Culture::GrantRandomBoostGoodyHut 0x280860
GrantRandomBoostsByEra PlayerCulture:GrantRandomBoostsByEra(iNumFreeBoosts: integer, eStartEra: integer, eEndEra: integer) Randomly triggers a number of civic boosts for a player, picking among boostable civics whose era is between a start and end era that are not yet researched or boosted. plumbing ✓
this ✓ (oracle)
Player::Culture::GrantRandomBoostsByEra 0x280c80
GrantRandomCivics PlayerCulture:GrantRandomCivics(iNumCivics: integer) Builds a list of civics that can be progressed and are not yet completed, then randomly picks up to iNumCivics of them and grants them via cultural progress at full civic cost. Decompilation truncated. plumbing ✓
this ✓ (oracle)
— 0x280fb0
HasFeaturesModifiedUnlock PlayerCulture:HasFeaturesModifiedUnlock(eFeature: integer) → boolean Searches the (feature, civic) pair list at Culture+0x700 and returns true if a matching feature has its civic completed. plumbing ✓
this ✓ (oracle)
— 0x2815e0
IsCivicRevealed PlayerCulture:IsCivicRevealed(eCivic: integer) → boolean Returns true if the civic is visible to the player: owned, boost triggered, or a prerequisite owned (for flagged civics); true for unflagged civics; false if no definition. plumbing ✓
this ✓ (oracle)
— 0x281c80
IsProgressingCivic PlayerCulture:IsProgressingCivic(eCivic: integer) → boolean Returns true if the civic currently being progressed (first entry of list at Culture+0x408) equals eCivic. plumbing ✓
this ✓ (oracle)
— 0x282210
RecomputeTourismOn PlayerCulture:RecomputeTourismOn(ePlayer: integer) Recomputes how many tourists this player receives from another player's culture and updates the stored value and the other player's tourism-related total by the difference. plumbing ✓
this ✓ (oracle)
Player::Culture::RecomputeTourismOn 0x282cb0
RemoveCultureBombTrigger PlayerCulture:RemoveCultureBombTrigger(eBuilding: integer, bCapturesOwnedTiles: boolean) Finds the first entry in the list at Culture+0x678 matching the given type and capture flag, and erases it. (DWARF lists both eBuilding and eDistrict variants; decompile uses DistrictTypes.) plumbing ✓
this ✓ (oracle)
— 0x282e80
RemoveGovernmentTypeSlotModifier PlayerCulture:RemoveGovernmentTypeSlotModifier(eGovernmentType: integer, eSlotType: integer, bAddSlot: boolean) Finds the (government, slot type) modifier entry and adjusts its count by 1 (opposite of Set); if the player's current government matches, clears policies, rebuilds slots and resets flags. plumbing ✓
this ✓ (oracle)
— 0x282f30
RemoveModifiedPolicySlot PlayerCulture:RemoveModifiedPolicySlot(eGovSlot: integer, bAddSlot: boolean) Removes one entry for the slot type from the added (0x1e0) or removed (0x208) modified slot list depending on bAddSlot, then clears policies of that slot type and refreshes the government. plumbing ✓
this ✓ (oracle)
— 0x283060
RemoveReplacedPolicySlot PlayerCulture:RemoveReplacedPolicySlot(eReplacedSlot: integer, eAddedSlot: integer) Clears all policies, erases entries matching (replaced slot, added slot) from the replaced-slot list at Culture+0x230, and re-applies the current government. plumbing ✓
this ✓ (oracle)
— 0x2831a0
RemoveTourismIntoFavor PlayerCulture:RemoveTourismIntoFavor(iTourism: integer, iFavor: integer) Rebuilds the tourism-into-favor pair list at Culture+0x6a8 from a copy, omitting entries matching (iTourism, iFavor). plumbing ✓
this ✓ (oracle)
— 0x283300
SetAllCivicsByPreviousEra PlayerCulture:SetAllCivicsByPreviousEra(eEraType: integer) Marks as completed every civic whose era is earlier than the given era (era index >1), notifying listeners for each and once overall. plumbing ✓
this ✓ (oracle)
Player::Culture::SetAllCivicsByPreviousEra 0x284060
SetCultureRateChange PlayerCulture:SetCultureRateChange(xNewValue: number (fixed point, 1/256 steps)) If the value differs from Culture+0x448, assigns it (via auto-variable at 0x438) and sends a culture-rate-changed event. plumbing ✓
this ✓ (oracle)
— 0x284990
SetFavorPerTurnFromBuilding PlayerCulture:SetFavorPerTurnFromBuilding(eBuilding: integer, iFavor: integer) Sets the per-turn Favor yield contributed by a building, replacing the existing value or adding a new record. plumbing ✓
this ✓ (oracle)
Player::Culture::SetFavorPerTurnFromBuilding 0x284ca0
SetFavorPerTurnFromPolicy PlayerCulture:SetFavorPerTurnFromPolicy(ePolicy: integer, iFavor: integer) Sets the per-turn Favor yield contributed by a policy, replacing the existing value or adding a new record. plumbing ✓
this ✓ (oracle)
Player::Culture::SetFavorPerTurnFromPolicy 0x284d60
SetFeatureModifiedUnlock PlayerCulture:SetFeatureModifiedUnlock(eFeature: integer, eCivic: integer) Appends a (feature, civic) pair to the list at Culture+0x6f0 so the feature is unlocked once the civic is completed. plumbing ✓
this ✓ (oracle)
— 0x284e20
SetGovernmentTypeSlotModifier PlayerCulture:SetGovernmentTypeSlotModifier(eGovernmentType: integer, eSlotType: integer, bAddSlot: boolean) Finds or creates the (government, slot type) modifier entry and increments or decrements its count; if the current government matches, clears policies, rebuilds slots and resets flags. plumbing ✓
this ✓ (oracle)
— 0x284e90
SetGreatWorkObjectNoTourism PlayerCulture:SetGreatWorkObjectNoTourism(eGreatWorkObjectType: integer, bNoTourism: boolean) Sets a flag that makes a great work object type produce no tourism, if the type has a record. plumbing ✓
this ✓ (oracle)
Player::Culture::SetGreatWorkObjectNoTourism 0x285030
SetModifiedPolicySlot PlayerCulture:SetModifiedPolicySlot(eGovSlot: integer, bAddSlot: boolean) Appends the slot type to the added (0x1e0) or removed (0x208) slot list depending on bAddSlot, then stores previous policies, clears them, rebuilds slots and resets flags. plumbing ✓
this ✓ (oracle)
— 0x285090
SetNumFreeCivicBoostOnNewEra PlayerCulture:SetNumFreeCivicBoostOnNewEra(numBoosts: integer, applyImmediately: boolean) Adds to the number of free civic boosts granted when a new era begins, optionally granting boosts immediately. plumbing ✓
this ✓ (oracle)
Player::Culture::SetNumFreeCivicBoostOnNewEra 0x285190
SetPolicyBanned PlayerCulture:SetPolicyBanned(ePolicy: integer, bBanned: boolean) Marks a policy as banned or unbanned for the player; when banning an active policy it removes it from its slot. plumbing ✓
this ✓ (oracle)
Player::Culture::SetPolicyBanned 0x285230
SetPolicyChangeMade PlayerCulture:SetPolicyChangeMade(bNewValue: boolean) If true stores the current turn in Culture+0x7c8, otherwise stores -1 (marks whether a policy change was made this turn). plumbing ✓
this ✓ (oracle)
— 0x285300
SetReplacedPolicySlot PlayerCulture:SetReplacedPolicySlot(eReplacedSlot: integer, eAddedSlot: integer, bReplacesAll: boolean) Appends a (replaced slot, added slot, replacesAll) entry to the list at Culture+0x230, clears policies and re-applies the current government if any. plumbing ✓
this ✓ (oracle)
— 0x285920

Details

AddTourismAtPlot

Arguments: iPlotIndex — plot index; iTourism — base tourism before modifier

Writes - tourism location vector at this+0x648 (existing entry amount increased or new entry appended)

Input checks: No plot validation; modifier clamped at -100.

Preconditions - game thread

Multiplayer: may-desync: synced vector. Risk: low (append/add only). Confidence of the reading: medium.

Verified from the code - amount = (max(mod,-100)+100)*tourism/100 using integer division - finds entry by plot index with find_if - adds or appends

Inferred - modifier pointer chain is the player's tourism modifier

Open questions - what source is at +0x6f0 -> +0x508

AddTourismIntoFavor

Arguments: iTourism — tourism amount of the entry (inferred); iFavor — favor amount of the entry (inferred)

Writes - vector at Culture+0x6a8 (tourism/favor pair list) gets one 8-byte entry appended

Input checks: No validation; duplicates allowed.

Multiplayer: may-desync, edits synchronised player data. Risk: medium (writes synchronised list used by tourism-to-favor logic). Confidence of the reading: medium.

ApplyTourism

Writes - Culture+0x480 per-player accumulated tourism array (adds ComputeTourismOn result for player ids <22) - Culture+0x510 value set from Diplomacy::GetTourismFromAlliances

Side effects - calls RecomputeTourismOn for each met player

Input checks: Only touches players already met; index guarded to < 22.

Multiplayer: may-desync, accumulates tourism into game state. Risk: medium (changes tourism totals every call). Confidence of the reading: medium.

CanFillAllPolicySlots

Input checks: No input; iterates government slot and policy definitions.

Multiplayer: read-only, only computes from definitions. Risk: low (pure check (truncated decompile)). Confidence of the reading: low.

CanTriggerBoost

Arguments: eCivic — civic type whose boost is tested

Input checks: No range check on eCivic beyond Types::GetIndex.

Multiplayer: read-only, queries boost and civic state. Risk: low (pure getter). Confidence of the reading: medium.

ChangeGreatWorkObjectTourismModifier

Arguments: eGreatWorkObjectType — great work object type; iModDelta — amount added to the modifier

Writes - second field of the matching 12-byte record in the vector at this+0x738

Side effects - none

Input checks: Type -1 returns silently. If no record exists for the type, nothing happens (no insertion).

Preconditions - records for object types pre-populated

Multiplayer: may-desync: player state mutation; must run identically on all clients. Risk: low (bounded linear search, silent miss). Confidence of the reading: high.

Verified from the code - searches vector of 12-byte entries by first field - adds to second field on match, no insert on miss

Inferred - third field is the no-tourism flag set by SetGreatWorkObjectNoTourism

Open questions - where the vector is populated

ChangeTourismModifier

Arguments: iDelta — amount added to the modifier

Writes - Culture+0x508 tourism modifier += iDelta

Input checks: None.

Multiplayer: may-desync, changes synchronised tourism modifier. Risk: medium (modifies tourism output). Confidence of the reading: high.

ChangeTourismOn

Arguments: ePlayer — player the tourism is directed at; xValue — amount to add (raw/256)

Writes - tourism-on-player array entry at this+0x480 (index ePlayer)

Side effects - none beyond marking the game variable edited

Input checks: Silently ignores ePlayer >= 0x16 (22); the check is unsigned so negatives are ignored too.

Preconditions - valid player id < 22 - game thread

Multiplayer: may-desync: edits synchronized state directly. Risk: low (bounds-checked simple add). Confidence of the reading: high.

Verified from the code - bound check param < 0x16 - reads old value, calls edit, writes old+delta - delta is read through a pointer (FixedPoint passed by reference)

Inferred - value is FixedPoint raw int; no clamping

Open questions - why the limit is 22 rather than 64 (likely only majors)

CheckForDominance

Writes - per-player dominance flag array (Culture+0x4a8) - per-player turns-until-dominant array (Culture+0x4d0)

Side effects - sends Reporting::Notification to this player and the dominated player

Input checks: Skips same team and unmet players (clearing their state).

Multiplayer: may-desync, writes dominance state and sends notifications. Risk: medium (drives cultural victory progress). Confidence of the reading: medium.

ComputeTourismOn

Arguments: ePlayer — target player of the tourism

Input checks: No range check on ePlayer; EditPlayer assumed valid.

Multiplayer: read-only, computes from synchronised state. Risk: low (pure getter). Confidence of the reading: medium.

ComputeTurnsUntilDominant

Arguments: ePlayer — player whose culture is compared

Input checks: Does not check ePlayer range; returns -1 if rates are non-positive.

Multiplayer: read-only, no state written. Risk: low (pure getter). Confidence of the reading: high.

DoCulture

Writes - many Culture fields (0x420-0x468, 0xf8, 0x5b0, 0x5b8, 0x5d0) - civic progress via ChangeCulturalProgress - current government via SetCurrentGovernment

Side effects - Reporting::Event::Send and several Notification::Send calls

Input checks: Guards on IsMajor and civic progress state.

Multiplayer: may-desync, runs the turn culture update. Risk: high (executes full per-turn culture logic). Confidence of the reading: medium.

DoEndTurn

Writes - Culture+0x450 flag set to false

Input checks: None.

Multiplayer: may-desync, writes synchronised flag. Risk: low (single flag write). Confidence of the reading: medium.

GetFavorPerTurnFromBuilding

Arguments: eBuilding — building type to look up

Input checks: Returns 0 for -1 or not found.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetFavorPerTurnFromPolicy

Arguments: ePolicy — policy type to look up

Input checks: Returns 0 for -1 or not found.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetGreatWorkObjectNoTourism

Arguments: eGreatWorkObjectType — great work object type

Input checks: Returns false for -1 or not found.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetGreatWorkObjectTourismModifier

Arguments: eGreatWorkObjectType — great work object type

Input checks: Returns 0 for -1 or not found.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetNumCivicsCompleted

Arguments: bIncludeRepeatedCivics — if true adds the repeated civic count

Input checks: Checks index bounds against Culture+0x368 and civic existence.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: medium.

GetNumPoliciesEnactedSpecificType

Arguments: eGovernmentSlot — slot type to count

Input checks: Skips empty slots (-1); policy lookup handles missing definitions.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetNumPoliciesUnlocked

Input checks: None needed.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetNumPolicySlotsOpen

Input checks: Returns 0 when government is -1 or no slots.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetOtherGovernmentIntoleranceModifier

Arguments: GovA — first government type; GovB — second government type

Input checks: Bounds-checks the index but dereferences definitions unchecked.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: medium.

GetSlotType

Arguments: iSlotIndex — slot index

Input checks: Upper bound checked; negative index not checked.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GetTourismNationalParksModifier

Input checks: None.

Multiplayer: read-only, writes only the return slot. Risk: low (pure getter). Confidence of the reading: medium.

GetTouristsTo

Input checks: Excludes self and unmet players.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

GrantRandomBoostGoodyHut

Arguments: iNumFreeBoosts — how many boosts to grant; eSource — source of the boost, passed to TriggerBoost

Writes - triggers civic boosts through TriggerBoost - consumes random numbers

Side effects - calls Culture::TriggerBoost for each chosen civic - advances the game random number generator (label: Choosing random civic boost to grant based on era) - heap-allocates a candidate list when it overflows inline storage

Input checks: Does nothing if count <= 0 or no candidate boosts. Civic ids of -1 or out of range skip the already-boosted checks.

Preconditions - valid player - game thread

Multiplayer: safe-deterministic. Risk: low (uses the game RNG with a fixed label, normal gameplay path). Confidence of the reading: medium.

Verified from the code - picks a lowest-era civic not yet owned/boosted then collects its era's boosts that exist and whose civic is not already owned - draws with Game::Instance::GetRandNum using the label 'Choosing random civic boost to grant based on era' - removes each chosen entry from the candidate list so no repeats - calls TriggerBoost for each selection not already boosted

Inferred - target era is the lowest era among unowned civics - decompiled types are misleading (government/tech names reused for civics)

Open questions - exact era-selection logic is partly garbled by decompiler type confusion - whether the HasBoost check refers to the era's boost objects

GrantRandomBoostsByEra

Arguments: iNumFreeBoosts — how many boosts to grant; eStartEra — lower era bound (inclusive); eEndEra — upper era bound (inclusive)

Writes - calls TriggerBoost on this player (source code 2) for each chosen civic

Side effects - draws random numbers from the game random stream (labelled with a log string including the player id) - triggers civic boosts

Input checks: if either era is not found in definitions the candidate list stays empty and nothing is granted; loop stops when candidates run out

Preconditions - valid owning player - game thread; deterministic RNG state

Multiplayer: safe-deterministic. Risk: medium (consumes game RNG and triggers boosts; fine if run on all clients). Confidence of the reading: medium.

Verified from the code - builds a weighted candidate list of civics with weight 1 - random index chosen via Game::Instance::GetRandNum with a log text including the player id - chosen entry removed from list then TriggerBoost(civic, 2) called - stops at iNumFreeBoosts or when list is empty

Inferred - candidate items are civics (names TechTypes in decompile are reused types) - era filter compares an era index field at +0x1c

Open questions - meaning of TriggerBoost source value 2 - decompile mixes tech/civic type names so list element type is unsure

GrantRandomCivics

Arguments: iNumCivics — how many civics to grant

Writes - cultural progress per granted civic via SetCulturalProgress

Side effects - consumes game random numbers (GetRandNum)

Input checks: Stops when no candidates left; iNumCivics <= 0 does nothing.

Multiplayer: may-desync, uses RNG and grants civics. Risk: high (changes civic progress and RNG state). Confidence of the reading: medium.

HasFeaturesModifiedUnlock

Arguments: eFeature — feature type to test

Input checks: Bounds-checks civic index; no check on eFeature.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

IsCivicRevealed

Arguments: eCivic — civic type to test

Input checks: Returns false when civic definition not found.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: medium.

IsProgressingCivic

Arguments: eCivic — civic type to compare

Input checks: Returns false if the progress list is empty.

Multiplayer: read-only, only reads synchronised state. Risk: low (pure getter). Confidence of the reading: high.

RecomputeTourismOn

Arguments: ePlayer — the other player id

Writes - per-player tourist count array at this+0x5e8 (entry ePlayer) - an AI control/priority-style int on the other player's object (+0x6f0 -> +0x5a0) adjusted by the delta

Side effects - calls ComputeTouristsFrom(ePlayer)

Input checks: no bounds check on ePlayer; EditPlayer result not null-checked

Preconditions - valid player id - gameplay thread

Multiplayer: safe-deterministic. Risk: medium (unchecked index and cross-player write). Confidence of the reading: medium.

Verified from the code - indexes player arrays by ePlayer*4 without bounds check - other player's field at +0x6f0 pointer then +0x5b0 minus the delta is stored back through a MoveAssignment on +0x5a0

Inferred - the +0x6f0 object is the other player's Culture and the field is a running total tied to tourists received (probably lifetime/visiting tourists) - the +0x5a0 type name AI::Control::ControlPriority is likely a mis-resolved template

Open questions - exact meaning of the field updated on the other player

RemoveCultureBombTrigger

Arguments: eBuilding — type matched against the list entry field (district/building) (inferred); bCapturesOwnedTiles — captures-owned-tiles flag that must match

Writes - entry removed from culture bomb trigger list at Culture+0x678

Input checks: Silently does nothing if no entry matches.

Multiplayer: may-desync, edits synchronised list. Risk: medium (removes culture bomb triggers). Confidence of the reading: medium.

RemoveGovernmentTypeSlotModifier

Arguments: eGovernmentType — government whose modifier is changed; eSlotType — slot type being modified; bAddSlot — true if the original modifier added a slot

Writes - modifier entry count in list at Culture+0x258 - Culture+0x7c8 reset to -1, Culture+0x450 set true, policies cleared

Side effects - Reporting::Event::Send(0xa981150) when current government matches

Input checks: If no entry found nothing changes in the list; current government still rebuilt.

Multiplayer: may-desync, rebuilds policy slots. Risk: high (clears all enacted policies on current government). Confidence of the reading: medium.

RemoveModifiedPolicySlot

Arguments: eGovSlot — slot type whose modifier is removed; bAddSlot — selects list: true=added-slots list, false=removed-slots list

Writes - entry erased from slot modifier list (0x1e0 or 0x208) - policies of that slot type cleared

Side effects - SetCurrentGovernment re-applies current government

Input checks: Returns early for slot -1.

Multiplayer: may-desync, edits slots and policies. Risk: high (clears policies). Confidence of the reading: medium.

RemoveReplacedPolicySlot

Arguments: eReplacedSlot — slot type that was replaced; eAddedSlot — slot type that replaced it

Writes - entries removed from list at Culture+0x230 - all policies cleared

Side effects - SetCurrentGovernment re-applies current government if one is set

Input checks: Returns early if either slot is -1.

Multiplayer: may-desync, edits slots and policies. Risk: high (clears all policies). Confidence of the reading: medium.

RemoveTourismIntoFavor

Arguments: iTourism — tourism value of the entry to remove (inferred); iFavor — favor value of the entry to remove (inferred)

Writes - list at Culture+0x6a8 rebuilt without matching entries

Input checks: None; non-matching list stays unchanged.

Multiplayer: may-desync, edits synchronised list. Risk: medium (list rewrite). Confidence of the reading: medium.

SetAllCivicsByPreviousEra

Arguments: eEraType — era; civics from earlier eras are granted

Writes - civic-completed byte array at this+0x348

Side effects - signal (Unit*,uint) per civic granted (game signal +0x498) with the player and civic id - signal (uint) on player object +0x798 with last granted civic

Input checks: Era index looked up with bounds check but result used unchecked when out of range (null deref risk); does nothing if era's index <=1.

Preconditions - game thread - valid era type

Multiplayer: may-desync: grants civics. Risk: medium (bulk grant and unchecked era pointer). Confidence of the reading: medium.

Verified from the code - skips when era value <=1 - only civics whose era value is below target era - dispatches per-civic signal and one final signal if any granted

Inferred - does not trigger boosts or other civic side effects

Open questions - whether already-owned civics also re-trigger the signal (no check seen)

SetCultureRateChange

Arguments: xNewValue — new fixed-point culture rate change

Writes - culture rate change value at Culture+0x438/0x448

Side effects - Reporting::Event::Send(0x8aba2360, player)

Input checks: Skips when unchanged.

Multiplayer: may-desync, writes culture rate. Risk: medium (changes applied culture rate). Confidence of the reading: medium.

SetFavorPerTurnFromBuilding

Arguments: eBuilding — building type; iFavor — favor per turn value (assigned, not added)

Writes - (building, favor) pair vector at this+0x798

Side effects - vector may reallocate

Input checks: Type -1 returns silently. No range check on value.

Preconditions - valid Culture object

Multiplayer: may-desync: player state mutation; must run identically on all clients. Risk: low (simple keyed assignment). Confidence of the reading: high.

Verified from the code - linear search by building; overwrite second int if found, else append 8-byte pair

Inferred - total favor per turn aggregates these entries (Gathering Storm)

Open questions - aggregation point

SetFavorPerTurnFromPolicy

Arguments: ePolicy — policy type; iFavor — favor per turn value (assigned, not added)

Writes - (policy, favor) pair vector at this+0x768

Side effects - vector may reallocate

Input checks: Type -1 returns silently. No range check.

Preconditions - valid Culture object

Multiplayer: may-desync: player state mutation; must run identically on all clients. Risk: low (simple keyed assignment). Confidence of the reading: high.

Verified from the code - linear search by policy; overwrite or append 8-byte pair

Inferred - sums into player's favor income

Open questions - aggregation point

SetFeatureModifiedUnlock

Arguments: eFeature — feature type to unlock; eCivic — civic that unlocks it

Writes - entry appended to list at Culture+0x6f0

Input checks: No validation or duplicate check.

Multiplayer: may-desync, edits synchronised list. Risk: medium (adds unlock condition). Confidence of the reading: medium.

SetGovernmentTypeSlotModifier

Arguments: eGovernmentType — government whose slots are modified; eSlotType — slot type being modified; bAddSlot — true adds a slot (+1), false removes (-1)

Writes - modifier list at Culture+0x258 - Culture+0x7c8 reset to -1, Culture+0x450 set true, policies cleared

Side effects - Reporting::Event::Send(0xa981150) when current government matches

Input checks: No range checks on enums.

Multiplayer: may-desync, rebuilds slots and clears policies. Risk: high (clears all enacted policies on current government). Confidence of the reading: medium.

SetGreatWorkObjectNoTourism

Arguments: eGreatWorkObjectType — great work object type; bNoTourism — true to suppress tourism

Writes - bool flag in the third field of the matching record in the vector at this+0x738

Side effects - none

Input checks: Type -1 returns silently. Missing record: silent no-op.

Preconditions - records for object types pre-populated

Multiplayer: may-desync: player state mutation; must run identically on all clients. Risk: low (flag write with silent miss). Confidence of the reading: high.

Verified from the code - same record vector as ChangeGreatWorkObjectTourismModifier - writes a bool at offset 8 of the 12-byte record

Inferred - flag consulted in tourism calculation

SetModifiedPolicySlot

Arguments: eGovSlot — slot type to add or remove; bAddSlot — true uses the added-slots list, false the removed list

Writes - slot modifier list (0x1e0 or 0x208) - Culture+0x7c8 reset to -1, Culture+0x450 set true, policies cleared

Side effects - Reporting::Event::Send(0xa981150, player)

Input checks: No validation of slot type.

Multiplayer: may-desync, rebuilds slots and clears policies. Risk: high (clears all enacted policies). Confidence of the reading: medium.

SetNumFreeCivicBoostOnNewEra

Arguments: numBoosts — amount added to the stored counter (despite 'Set' name it is additive); applyImmediately — if true and boosts positive, grant random boosts now

Writes - free civic boost counter at this+0x7f0 (via wrapper at this+0x7e0) - random civic boosts granted through GrantRandomBoostsByEra

Side effects - may call GrantRandomBoostsByEra, which uses game randomness and awards boosts

Input checks: No zero check. Immediate grant requires a valid era (not -1) and a positive amount; otherwise only the counter changes.

Preconditions - owning player valid with an era set

Multiplayer: may-desync: random boost granting must be run identically on all clients (uses synchronized RNG presumably). Risk: medium (counter can go negative; immediate grant uses random selection). Confidence of the reading: medium.

Verified from the code - counter = old + param - immediate grant only if bool set, era != -1, param > 0 - calls GrantRandomBoostsByEra with the current era twice

Inferred - boosts are civic (culture) boosts per the name - RNG used inside GrantRandomBoostsByEra

Open questions - meaning of two era arguments - whether counter is consumed or persistent

SetPolicyBanned

Arguments: ePolicy — policy type; bBanned — true to ban

Writes - banned flag array entry at this+0x320 - policy slot cleared via ClearPolicySlot - another tracked value at this+0x7c8 set to -1 after a slot clear

Side effects - removes the active policy from its slot if found; resets a tracked field at this+0x7c8 to -1

Input checks: Policy id is range checked against array size (unsigned compare, so -1 is rejected silently). Out of range does nothing.

Preconditions - valid policy index - player culture initialised

Multiplayer: may-desync: removes active policies; must run identically on all clients. Risk: medium (can evict an active policy and change slots). Confidence of the reading: medium.

Verified from the code - bounds check on policy id - writes banned flag - clears the slot only when banning and the policy is flagged active - scan for slot entry holding that policy

Inferred - flag at +0x308 means 'currently active' - value at +0x7c8 is something like a last-removed-policy marker

Open questions - what +0x7c8 means - if slot not found, +0x7c8 stays unchanged (return before)

SetPolicyChangeMade

Arguments: bNewValue — true marks a change made this turn, false clears it

Writes - Culture+0x7c8 = current turn or -1

Input checks: None.

Multiplayer: may-desync, writes synchronised turn marker. Risk: low (single value write). Confidence of the reading: high.

SetReplacedPolicySlot

Arguments: eReplacedSlot — slot type being replaced; eAddedSlot — slot type taking its place; bReplacesAll — stored flag, whether it replaces all such slots (inferred)

Writes - entry appended to list at Culture+0x230 - all policies cleared

Side effects - SetCurrentGovernment re-applies current government

Input checks: No validation or duplicate check.

Multiplayer: may-desync, edits slots and clears policies. Risk: high (clears all policies). Confidence of the reading: medium.