Campaign Behavior - TheOldRealms/TOW_Core GitHub Wiki

A CampaignBehavior is a script that controls some aspect of the game on the Campaign map. It can be anything, ranging from applying the effects of dialogues or engagements to controlling the AI for individual parties. The menu systems and prompts are usually controlled separately from #missing reference.

CampaingBehaviors are usually implemented by registering listener methods for certain types of CampaignEvents and filtering to react on specific objects that appear in those event streams.

Registering Behaviors

Campaign behaviors should derive from the CampaignBehaviorBase class.

public class YourNewCampaignBehavior : CampaignBehaviorBase

And be registered in SubModule class from the SubModule.cs file.

public class SubModule : MBSubModuleBase
{
    ...
    protected override void OnGameStart(Game game, IGameStarter gameStarterObject)
    {
        ...
        CampaignGameStarter starter = gameStarterObject as CampaignGameStarter;
        starter.AddBehavior(new YourNewBehaviour());
        ...
    }
    ...
}

Listening to campaign events

The CampaignEvents class is the static source object for registering listeners in a CampaignBehavior. It is used inside the RegisterEvents method which is part of the CampaignBehaviorBase abstraction.

Here is an example for adding a listener that listens to daily ticks on settlements.

public class YourNewCampaignBehavior : CampaignBehaviorBase {

    public override void RegisterEvents()
    {
       CampaignEvents.DailyTickSettlementEvent.AddNonSerializedListener(this, DailyTickSettlement);
    }
    
    private void DailyTickSettlement(Settlement settlement)
    {
    // Your code for doing something involving a settlement.
    }
}

Note that a specific settlement is never specified in the example above. This means that you will be listening to ticks on all settlements in the game. If you need the specify the behavior for a specific settlement or type of settlements, filter for them inside the method. This is necessary for all CampaignEvents.

Look into the CampaignEvents class by TaleWorlds to see the different events available for registration. All of them have different inputs which need to be available in the implementing method.

Saving data

You might have noticed that one of the abstract methods inherited from the base is a function called

public override void SyncData(IDataStore dataStore)

This function is used to store the value of variables between game saves and load. If you do not use this function then all values of variables in this class will be reset to their initial values on game save and load.

Here is an example

public override void SyncData(IDataStore dataStore)
{
    dataStore.SyncData<Dictionary<string, CustomUnit>>("_custom_units", ref CustomUnits);
    dataStore.SyncData<bool>("_gear_restriction", ref _disable_gear_restriction);
    dataStore.SyncData<bool>("_skill_total_restriction", ref _disable_skill_total_restriction);
    dataStore.SyncData<bool>("_skill_cap_restriction", ref _disable_skill_cap_restriction);
}

The syncData function for the datastore can store any types including custom classes, enumerations, and containers. The two arguments are an unique key string to store the data under and the variable in the class you are storing passed in as a reference

Save definer

Do note if you are storing custom types or containers that are not used by the base game you will need to create a save definer class. If you are getting a save error in game, then the causes is mostly likely due to the lack of a save definer for your custom types

    public class SaveDefiner : SaveableTypeDefiner
    {
        public SaveDefiner() : base(1436500005)
        {
        }

        protected override void DefineClassTypes()
        {
            this.AddClassDefinition(typeof(CustomUnit), 1);
        }

        protected override void DefineContainerDefinitions()
        {
            this.ConstructContainerDefinition(typeof(Dictionary<string, CustomUnit>));
        }
    }

The int passed to the base constructor just has to be an unique value but chances are if you type in a value at random it mostly will not conflict with anything else

⚠️ **GitHub.com Fallback** ⚠️