Skip to content

Settlements

Find Settlement

SettlementHelper:
    FindNearestSettlement
    FindNearestHideout
    FindNearestTown
    FindNearestFortification
    FindNearestCastle
    FindNearestVillage
    FindNextSettlementAroundMapPoint
    FindRandomSettlement
    FindRandomHideout
    GetRandomTown
    GetBestSettlementToSpawnAround

Gold

Settlement.SettlementComponent.Gold

Current Settlement we are at

Settlement.CurrentSettlement

Owner

Settlement.Owner (hero)

The settlement hero is at currently

Hero.CurrentSettlement

Notables

Settlement.Notables
if (town.Notables.Contains(notable)) return true;
Notables amount by Prosperity

Militia

float Settlement.Militia

Settlement.Militia += 1

Garrison

settlement.Town.GarrisonParty

SettlementHelper.IsGarrisonStarving

Create garrison:

if (settlement.Town.GarrisonParty == null) settlement.AddGarrisonParty();

Add troops to garrison:

settlement.Town.GarrisonParty.MemberRoster.AddToCounts(settlement.Culture.BasicTroop, 1, false, 0, 0, true, -1);

Type of Settlement

bool value
.IsTown
.IsCastle
.IsVillage
.IsHideout
.IsFortification (Town OR Castle)

Tournament

Campaign.Current.TournamentManager.GetTournamentGame(town) != null

Siege

bool        IsUnderSiege [get]
SiegeEvent  SiegeEvent [get, set]

Walls

Fix ~4% of damaged walls:

AccessTools.Method(typeof(Town), "RepairWallsOfSettlementDaily").Invoke(settlement.Town, null);

Get % of walls hitpoints:

float wallHealthPercent = settlement.SettlementWallSectionHitPointsRatioList.Average() * 100f;

Last Conquest Time

C#:
public static CampaignTime GetLastConquestTime(Settlement settlement)
{
    for (int i = Campaign.Current.LogEntryHistory.GameActionLogs.Count - 1; i >= 0; i--)
    {
        LogEntry logEntry = Campaign.Current.LogEntryHistory.GameActionLogs[i];
        ChangeSettlementOwnerLogEntry changeLog = logEntry as ChangeSettlementOwnerLogEntry;

        if (changeLog != null && changeLog.Settlement == settlement)
        {
            // Check if this was a true conquest (between different factions)
            IFaction previousFaction = changeLog.PreviousClan?.MapFaction;
            IFaction newFaction = changeLog.NewClan?.MapFaction;

            if (previousFaction != null && newFaction != null && previousFaction != newFaction)
            {
                return changeLog.GameTime; // This is a true conquest
            }
        }
    }
    return CampaignTime.Never;
}

Granary/food

How much food in the granary:

Settlement.CurrentSettlement.Town.FoodStocks

Max granary amount:

Settlement.CurrentSettlement.Town.FoodStocksUpperLimit()

Build projects / buildings

Town

Applies to towns AND castles

TradeBoundVillages

MBReadOnlyList<Village> Settlement.Town.TradeBoundVillages

Wall Level

int Settlement.Town.GetWallLevel()

Governor

Hero Town.Governor

ChangeGovernorAction.RemoveGovernorOf(Hero governor);
ChangeGovernorAction.RemoveGovernorOfIfExists(Town town);
ChangeGovernorAction.Apply(Town fortification, Hero governor);      // assign new

Village

Village States

public enum VillageStates
{
    Normal,
    BeingRaided,
    ForcedForVolunteers,
    ForcedForSupplies,
    Looted
}
  • Looted (same as .IsDeserted)?

Events

OnVillageStateChanged(Village village, Village.VillageStates oldState, Village.VillageStates newState, MobileParty raiderParty)
OnVillageBecomeNormal(Village village)
OnVillageBeingRaided(Village village)
OnVillageLooted(Village village)

CampaignEvents.VillageStateChanged.AddNonSerializedListener(this, new Action<Village, Village.VillageStates, Village.VillageStates, MobileParty>(this.OnVillageStateChanged));
CampaignEvents.VillageBeingRaided.AddNonSerializedListener(this, new Action<Village>(this.OnVillageBeingRaided));
CampaignEvents.VillageLooted.AddNonSerializedListener(this, new Action<Village>(this.OnVillageLooted));

GetItemPrice

int GetItemPrice(ItemObject item, MobileParty tradingParty = null, bool isSelling = false)
    return this.TradeBound.Town.MarketData.GetPrice(itemRosterElement, tradingParty, isSelling, null);

TradeBound

Returns Town to which the Village is trade bound.

Levels

Village has 3 visual levels. Level depends on hearths:

  • Lvl 1 - hearths 1..199
  • Lvl 2 - 200-599
  • Lvl 3 - 600+
settlement.Village.Hearth
Change village hearths method example
public static void ChangeVillageHearths(Settlement settlement, float hearthsChange)
{
    if (hearthsChange == 0.0 || settlement == null || settlement.Village == null || !settlement.IsVillage || settlement.IsUnderRaid || settlement.IsRaided) return;
    settlement.Village.Hearth += hearthsChange;
}

XML

To be able to change the owner of the settlement in your mod, you need such files:

  • settlements.xslt
  • settlements.xml

settlements.xslt - to delete native settlements.xml

settlements.xml - to add your settlements here

SubModule.xml should include:
    <XmlNode>
        <XmlName id="Settlements" path="settlements"/>
        <IncludedGameTypes>
            <GameType value = "Campaign"/>
            <GameType value = "CampaignStoryMode"/>
            <GameType value = "CustomGame"/>
            <GameType value = "EditorGame"/>
        </IncludedGameTypes>
    </XmlNode>

Village Production

VillageType.silk_plant
VillageType.cattle_farm
VillageType.silver_mine
VillageType.iron_mine
VillageType.lumberjack
VillageType.wheat_farm
VillageType.fisherman
VillageType.europe_horse_ranch
VillageType.sheep_farm
VillageType.flax_plant
VillageType.vineyard
VillageType.date_farm
VillageType.olive_trees
VillageType.swine_farm
VillageType.clay_mine
VillageType.trapper
VillageType.desert_horse_ranch
VillageType.sturgian_horse_ranch
VillageType.salt_mine
VillageType.steppe_horse_ranch
VillageType.vlandian_horse_ranch
VillageType.battanian_horse_ranch

background_mesh

Size: 100x100
SpriteCategory Name="ui_fullbackgrounds"

Note: name of the sprite is with the '_t' at the end: PNG file gui_bg_village_baltic_t.png but in the settlements.xml it's gui_bg_village_baltic

Image for the settlement small icon in the Encyclopedia:

From v1.3 you need to add a Brush for each icon.

wait_mesh

Size: 445x805
SpriteCategory Name="ui_fullbackgrounds"
Image for the settlement menu and in the Encyclopedia:

castle_background_mesh

Not used

Village
public string CastleBackgroundMeshName { get; protected set; }

base.CastleBackgroundMeshName = node.Attributes["castle_background_mesh"].Value;
and CastleBackgroundMeshName is not used anywhere else.

Settlements Distance Cache

If you add new settlements, or relocate existing ones, you will need to recalculate settlements_distance_cache.bin which the game uses to optimise AI path finding between different settlements.

From my observation: when adding new settlements it's not necessary to recalculate it, game works as it is. It is necessary to recalculate at the end (when all settlements are added) for performance.

The game crashes if settlements.xml and settlements_distance_cache.bin are incompatible.

settlements_distance_cache.bin

Editor does not generate it by default on map save.

If settlements_distance_cache.bin is missing:

  • v1.3+ - game regenerates it automatically if missing

Can be regenerated with the Editor using SettlementPositionScript

Load time can be long with wrong settlements_distance_cache.bin. Regenerate to solve it.