Adding new settlements without modifying sandbox module

Users who are viewing this thread

EDIT:
Since I couldn't get it to work the "intended" way I made a workaround.

I wrote a Patcher that allows adding new settlements (villages, towns and castles) to the game through separate modules. This is a slightly dirty solution. Source code, brief explanation of how it works, caveats and an example module can be found on the github page. Let me know if you have any questions via DM on discord (Trucker#3350)



EDIT END.
-------------------------
EDIT #2
NEW UPDATE (coming) - NOW (later) WITHOUT MODIFIYNG SANDBOX MODULE AT ALL
Some new information has come to light. There is a way to override or add to the original settlements of the game. I added how here:
I will update this patcher (prob on or before 25.04.20) to work more cleanly, making it truly how you expect a mod to work (being able to load/unload the module without any caveats related to crashes). The reason why some C# code is still needed is to be able to generate a new distance cache, plus update compatibility.
What this means for you is that for now you can keep working with this the way it is and then just get the updated version on the weekend. No further changes will be needed.
-------------------------
I have been trying for about 10 hours now to add settlements to the game without needing to modify the sandbox module.
I wanted to summarize my findings and current challenges in the hopes that others may have overcome them or are inspired by my "struggles" to find and share a solution.

Adding a settlement consists of two things:
  • An XML-based definition of a Settlement in e.g. ModuleData/custom_settlements.xml. Lets say this settlement has the name Frugal and the id town_M1
  • An XML-based definition for what this specific settlement looks like on the Main_map/world map. Sandbox does this in SceneObj/Main_Map/scene.xscene
    • All vanilla settlements and other points of interests such as hideout camps are defined in there.
    • The scene.xscene structure is something along the lines of <scene><entities><game_entity name="town_M1"></scene></entities></game_entity>
    • These game_entities are just prefabs in terms of the game
  • The Settlement xml can be referenced in the submodules.xml in order to be loaded by the game, but there seems to be no good way to load prefabs
If you add new entries in those two files within Sandbox you can in fact add new fiefs to the game. I added a town Frugal and two bound villages named Frulil and Frulong. It even automatically shows up in the Encyclopedia. In the example given I made Frugal the main town have the prefabs of the training field, so it looks a bit strange being up in the air.

afcEHJ0.png

wOzOAlv.jpg


Now you might think how can one add a new settlement to the game without modifying Sandbox, when all fiefs are defined hardcoded under the Main_Map scene? Good question! For this have a look at the StoryMode module. The training field the player starts at is not defined in Sandbox but in fact in StoryMode. For this two XML files are important:
  • StoryMode\ModuleData\story_mode_settlements.xml
  • StoryMode\Prefabs\storymode_prefabs.xml
Neither of these two files are mentioned in the submodule.xml. The file story_mode_settlements.xml defines the training field the same way a fief or hideout was defined in Sandbox\ModuleData\settlements.xml, while file storymode_prefabs.xml defines <prefabs><game_entity name="tutorial_training_field" old_prefab_name="">(...)</game_entity></prefabs>, so essentially the same way game_entities are defined within the aforementioned scene.scene file, except wrapped in the prefabs type.

So how does StoryMode get the training field into the game? Well, purely by hardcoded file references. The most important class for this is "CampaignStoryMode : Campaign" within StoryMode.dll namespace StoryMode.CampaignStoryMode.

Within this, three functions are called:
  • MBObjectManager.Instance.LoadOneXmlFromFile(BasePath.Name + "Modules/StoryMode/ModuleData/story_mode_settlements.xml", (string) null, (Type) null, true);
    • This seems to load one settlement definition from a given xml file and registers it with the object manager.
  • Settlement settlement = Settlement.Find("tutorial_training_field");
    • Saves the reference to the training field "settlement" in a variable, proving that the aforementioned line is enough to register a settlement in the game
  • this.MapSceneWrapper.AddNewEntityToMapScene(settlement.StringId, settlement.Position2D);
    • This adds the settlement to the actual map scene. From what I can gather this function is what reads the prefab/game_entity. For this it is important that the ids of the prefab and settlement line up.
    • This function also calculates the z position by getting the terrain height at the given position
So what is the problem? It seems everything is evident. That's what I thought and why I have spent over 10 hours now trying to figure this out.

What I tried so far:
  1. First I tried a "pure" XML-based approach.
    1. Define my settlement Frugal and its game_entity in a settlements XML and prefab XML file. I then tried to include them in the submodule to have the game load them autonomously. This does not work and results in a crash, from what I can tell the reason is that while the game does load the settlements.xml it does not load the prefabs and tries to display something on the main_map without knowing the visuals.
  2. Try creating my own gamemode that essentially works exactly like the StoryMode module, but loads my XML files
    1. I was struggling to get a good reference to the mapscenewrapper to call the AddNewentityToMapScene function, so I figured I needed to somehow obtain the instance of CampaignStoryMode
    2. I eventually made it and had my code execute as part of the game's regular structures during the game loading sequence (when a new game object is created/loaded)
    3. Results in crashes apart from the nightmare of getting the game running with a custom singleplayer gamemode
  3. Patching the CampaignStoryMode function that contains the cod ethat loads the tutorial_training_field
    1. Using harmony to do a Postfix on "DoLoadingForGameType" and essentially copy the code 1 to 1, except load my custom fief and prefab.
    2. The code runs, the postfix does not cause any crashes...
    3. ...unfortunately the game crashes without even opening the crash reporter afterwards and I do not know why
    4. From what I can tell the way StoryMode adds the training field is no different to how I try to add Frugal. Not sure why it is crashing, maybe because the prefab is not properly loaded but that's just a completely wild guess.
  4. Almost giving up and deciding to post here to describe what I know and see if anyone has any ideas.

If you guys have any questions about what I described please let me know. My source code is attached. It contains a custom FoodModel that I was playing around with but you can ignore that. My Bannerlord module is also attached. It contains a .txt file with custom debug output that I populate in the patched function. This is needed since the regular crash reporter does not work...

Other stuff

  • it is possible that for the AI to be able to travel to custom villages properly it is necessary to add an entry for it under settlements_distance_cache.bin . The game does this in the Sandbox module: Sandbox.view SettlementPositionScript.cs . The code in there seems to be designed to work in combination with an Editor where the user can add new settlements and the code automatically populates the data in the appropriate files.

Bannerlord module https://we.tl/t-zgKV0G5Qgq
Module source code https://we.tl/t-7ldSilrbxP
 
Last edited:
Thank you very much for your input. I moved your thread to The Smithy as you are not asking for a question but sharing your researches.

Thanks. I wasn't sure where to post it as it was a sort of question, i.e. "how do make work" and it seemed that Q&A was the better place for it.
 
Just an addendum (but we already reached this conclusion on discord, mostly for other readers): if you put your code from Prefbab settlement xml into sandbox's main_map xscene, it works, so the TL;DR of the issue is: the game isn't loading the xml on the prefabs folder
 
Just an addendum (but we already reached this conclusion on discord, mostly for other readers): if you put your code from Prefbab settlement xml into sandbox's main_map xscene, it works, so the TL;DR of the issue is: the game isn't loading the xml on the prefabs folder

If there is any discord server regarding Bannerlord Modding I would love to be a part of it ^^
 
@RapidFire

I think you are right there, but it is a bit annoying in this case since all the actual information for populating cities in the game comes purely from XML and is just loaded in code. I think that ultimately they do plan to have at least this functionality XML only based but they just haven't gotten around to it yet. The way they handle the StoryMode feels very temporary/hacked. The fact that there is no easy way to do it at all just shows that its not done very well.

What confuses me the most is that I repeat the same steps the StoryMode does at the same time and get crashes. Even if I try to just load the training field myself with their code by overriding their classess and loading my custom classes I get crashes. I can't figure out what is the deciding factor why it works for them and not for me.

I even tried loading my module in stead of StoryMode with custom classes but no luck...I think the harmony patch is the best solution but it crashes the same way my custom classes did, showing there is some piece of the puzzle that is missing.
 
Harmony Postfix runs after the method you are patching. If you are trying to replace the method, use a Prefix and return false. Your crashing might be caused by effectively running the replaced method twice.
 
Hey @Splintert thank you for your reply. I am not trying to replace the function but just append code to it.

Wait wait wait I only now understood what you are saying. If I run a postfix it runs the function twice??
 
Last edited:
Hey @Splintert thank you for your reply. I am not trying to replace the function but just append code to it.

Wait wait wait I only now understood what you are saying. If I run a postfix it runs the function twice??

No, but if you copy the whole code of the method you are postfixing then it will. I noticed you said you did that in section 3.1 of your troubleshooting attempts.
 
@Splintert Ah maybe I didn't explain very well. I tried replacing the classes altogether without patching. I unloaded the story module and copied the story module code into my own project and made sure to unload the story module completely, taking classess from it as needed. Essentially I emulated the story mode module code but while making my own custom classes that added behaviour I wanted.

I THEN tried to patch the story module when my first attempt did not work.
 
so, if I understand this correctly - I ONLY modified some city names in the settlements xml (which worked fine when replacing settlements.xml in SandBox) and then tried to make it a standalone module, which just does nothing.

that is because the custom xml from the module is just not being used at all?
 
@FallOfRome The submodule.xml is being used, but the way the game code loads xmlnodes is a bit strange. It seems to only automatically load some defined xmlnodes.

The more reliable method is to load xml files in code as you can always be sure it actually loads it. There's a lot of hardcode in the game from what I can tell, so it's not good to rely on these automated-loading-processing systems especially with no official documentation.
 
Have you tried editing something in SettlementNameplateItem.xml ? I'm asking because i am trying to edit the settlement name colors to follow factions colors, in stead of all being that light beige ( i.e brush.color="@FactionColor"), without needing a new widget, but to no avail.
 
Last edited:
Hey guys. I wrote a Patcher that allows adding new settlements (villages, towns and castles) to the game through separate modules. This is a slightly dirty solution. Source code, brief explanation of how it works, caveats and an example module can be found on the github page. Let me know if you have any questions via DM on discord (Trucker#3350)

 
Back
Top Bottom