From f3fb5af9c8d650109830b76bbb4e517169f43a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=BCcahit=20Tanac=C4=B1o=C4=9Flu?= Date: Wed, 25 Aug 2021 16:25:56 +0300 Subject: [PATCH 1/6] ADD how to alter behavior via game model --- ...tering_existing_behavior_via_gamemodels.md | 86 +++++++++++++++++++ ...sting_behavior_via_reregistering_events.md | 1 + 2 files changed, 87 insertions(+) create mode 100644 docs/_tutorials/altering_existing_behavior_via_gamemodels.md create mode 100644 docs/_tutorials/altering_existing_behavior_via_reregistering_events.md diff --git a/docs/_tutorials/altering_existing_behavior_via_gamemodels.md b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md new file mode 100644 index 0000000..5b7b76c --- /dev/null +++ b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md @@ -0,0 +1,86 @@ +Sometimes you might wanna alter some of existing behaviors.Here we try to do this wit 2 different aproach. +### 1-With GameModels +GameModels derived from TaleWorlds.Core.GameModel class,there is naming convention like this *Something*Model(abstract class derived from GameModels) and *DefaultSomething*Model(derived from *Something*Model) + +Some behaviors has related game models which includes key factors for behaviors.You need to check whether your need in model(if there is) or not. + +**Note:**Before we start these are what I could do with my knowledge,there is alot way to do this(probably better ways) but once you get the idea you can do as you need. +For this tutorial I want to edit SettlementLoyaltyModel and do followings: + +1.Add +2 Loyality Buff for Settlement belongs to Vlandian Kingdom lets say as Culturel Buff +2.Change the Owner Culture Debuff from -3 to -1 +3.Change the DailyNotableRelationBonus from 1-->2 and threshold for this to happen from 75-->55 + +first extend model +```csharp +class ModifiedDefaultSettlementLoyaltyModel : DefaultSettlementLoyaltyModel +``` + +add new model with gameStarter +```csharp + protected override void InitializeGameStarter(Game game, IGameStarter starterObject) + { + (starterObject as CampaignGameStarter).AddModel(new ModifiedDefaultSettlementLoyaltyModel()); + } +``` +Note:It does remove/(not add at all) original DefaultSettlementLoyaltyModel while creating GameModelManager in AddGameModelsManager() method in TaleWorlds.Core.Game class.If someone else figure it out please edit here. + +lets override things we want to edit + +```csharp + public override int DailyNotableRelationBonus + { + get + { + return 2; + } + } + public override float ThresholdForNotableRelationBonus + { + get + { + return 55f; + } + } + + public override ExplainedNumber CalculateLoyaltyChange(Town town, bool includeDescriptions = false) + { + ExplainedNumber myResult = base.CalculateLoyaltyChange(town, includeDescriptions); + + EditLoyalityFactors(ref myResult,"Owner Culture",-1f,includeDescriptions); + VlandianLoyalityBuff(town, ref myResult); + return myResult; + } +``` +here the function to Edit existing factor(most of the part are private in models so we cant override method that calculate factor,instead create another ExplaineNumber and edit while copying from other one) +```csharp + public void EditLoyalityFactors(ref ExplainedNumber result,string descriptionOfFactor,float newValue,bool includeDescriptions) + { + List<(string name, float value)> listOfFactors = result.GetLines(); + ExplainedNumber temp = new ExplainedNumber(0f, includeDescriptions, null); + foreach (var (name,value) in listOfFactors) + { + if (name.Equals(descriptionOfFactor)) + { + temp.Add(newValue,new TextObject(name,null),null); + continue; + } + temp.Add(value,new TextObject(name,null),null); + } + + result = temp; + } +``` +and finally adding new factor +```csharp + public void VlandianLoyalityBuff(Town town,ref ExplainedNumber explainedNumber) + { + if(town.OwnerClan != null && town.OwnerClan.Kingdom != null && town.OwnerClan.Kingdom.Name.ToString().Equals("Vlandia")) + { + TextObject VlandianCultureLoyalityBuffTO = new TextObject("Vlandian culture buff", null); + explainedNumber.Add(2f, VlandianCultureLoyalityBuffTO, null); + } + } +``` +### 2-Altering Behavior Class +[Modify Campaign Behavior With ReRegistering Event](./altering_existing_behavior_via_reregistering_events.md) \ No newline at end of file diff --git a/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md new file mode 100644 index 0000000..e61ef7b --- /dev/null +++ b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md @@ -0,0 +1 @@ +aa From 696a57a6ef601ae2094077e6d61d8cf12ea3039b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=BCcahit=20Tanac=C4=B1o=C4=9Flu?= Date: Wed, 25 Aug 2021 20:29:08 +0300 Subject: [PATCH 2/6] ADD modifiying campaign behavior vias reregister event --- README.md | 3 +- ...tering_existing_behavior_via_gamemodels.md | 2 +- ...sting_behavior_via_reregistering_events.md | 212 +++++++++++++++++- 3 files changed, 214 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 31e4347..271b9c5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ * [Modding Gauntlet UIs Without C#](docs/_tutorials/modding-gauntlet-without-csharp.md) (Easy) * [Packing your Mods for Vortex](docs/_tutorials/packing_mods_for_vortex.md) (Easy) * [Modifying/Adding Settlements](docs/_tutorials/new_settlements.md) (Easy) - +* [Modifying CampaignBehaviors-GameModels](docs/_tutorials/altering_existing_behavior_via_gamemodels.md)(Easy) +* [Modifying CampaignBehaviors-Reregister Event](docs/_tutorials/altering_existing_behavior_via_reregistering_events.md)(Medicore) ## [C# API Documentation](docs/_csharp-api/) * [CampaignSystem](docs/_csharp-api/campaignsystem/) diff --git a/docs/_tutorials/altering_existing_behavior_via_gamemodels.md b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md index 5b7b76c..98810e6 100644 --- a/docs/_tutorials/altering_existing_behavior_via_gamemodels.md +++ b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md @@ -1,6 +1,6 @@ Sometimes you might wanna alter some of existing behaviors.Here we try to do this wit 2 different aproach. ### 1-With GameModels -GameModels derived from TaleWorlds.Core.GameModel class,there is naming convention like this *Something*Model(abstract class derived from GameModels) and *DefaultSomething*Model(derived from *Something*Model) +[GameModels](../_csharp-api/core/gamemodel.md) derived from TaleWorlds.Core.GameModel class,there is naming convention like this *Something*Model(abstract class derived from GameModels) and *DefaultSomething*Model(derived from *Something*Model) Some behaviors has related game models which includes key factors for behaviors.You need to check whether your need in model(if there is) or not. diff --git a/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md index e61ef7b..6072d00 100644 --- a/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md +++ b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md @@ -1 +1,211 @@ -aa +### Adding new location to settlement +Sometimes what you wanna add/modify not be in model or there is no model,it can be even hardcoded in private methods/fields.Editing existing things hard but adding generally easy. +In this tutorial I want to walkthrough adding new interior scenes/districts to villages by playing around PlayerTownVisitCampaignBehavior + +First of all let me try to explain how the settlement interactions work as far as I know. + +When you go to settlement `TaleWorlds.CampaignSystem.PlayerEncounter` class creates `TaleWorlds.CampaignSystem.LocationEncounter` base on settlement you just visit.There is 3 type of encounter Town,Village,Castle and they derived from TaleWorlds.CampaignSystem.LocationEncounter +Important part here they override `CreateAndOpenMissionController()` method which creates [mission](../_csharp-api/mountandblade/mission.md)/scene.This is first thing we need to modify.These are happens in PlayerEncounter.EnterSettlement() method afterwards which calls `EnterSettlementAction` +and next part come in. + +`PlayerTownVisitCampaignBehavior` class is the where most of the interactions defined.Its derived from [CampaignBehaviorBase](../_csharp-api/campaignsystem/campaignbehaviorbase.md) like other behaviors.Here we need to know about [GameMenus](../_csharp-api/campaignsystem/gamemenu.md) and [GameMenuOptions](../_csharp-api/campaignsystem/gamemenu.md). +When you enter settlement first `on_init` method of current GameMenu called then `on_condition` of method of GameMenuOptions called.Let say you click the take a walk around town center option it will call `on_consuqence` method of that option which going to call +`TownEncounter.CreateAndOpenMissionController()` and it will create town_center scene.Let say you walk around town and went to tavern door when you use door `SandBox.Source.Objects.SettlementObjects.PassageUsePoint.OnUse()` will be called and it will assign `Campaign.Current.GameMenuManager.NextLocation` to its toLocation field which you can edit with editor(modding tools) and calls `Mission.Current.EndMission()`. +Then we again start from town menu's on_init method and it will check `Campaign.Current.GameMenuManager.NextLocation` if its not null it will create another mission.For villages there is no check like this and `VillageEncouter.CreateAndOpenMissionController()` will return null for locations different than **"village_center"**. + + +#1.Create Scene(s) + +Make sure you have passage point with correct id(location id on xml file) on toLocation parameter for both inside/outside of scene.Save them inside your [SceneObj](../_intro/folder-structure.md) folder + +#2.Override settlement.xml and location_complex_templates.xml + +Just copy and paste both xml to your [ModuleData](../_intro/folder-structure.md) folder. +I will just add tavern for village_complex and will name it new_complex here how it looks +``` +xml + + + + + + + +``` +for village I choose Polisia since its close to starting point as empire culture,we just need to edit Locations part here how it looks +``` +xml + + + + +``` +you need to add these 2 xmls to `SubModule.xml` and also need to add .xslt to override native xmls.[See here](http://docs.modding.bannerlord.com/bestpractices/xslt_usage_tutorial/) + +#3. +Modify Village Encounter +``` +csharp +class ModifiedVillageEncouter : VillageEncouter +{ + public override IMission CreateAndOpenMissionController(Location nextLocation, Location previousLocation = null, CharacterObject talkToChar = null, string playerSpecialSpawnTag = null) + { + + IMission result = null; + if (nextLocation.StringId == "village_center") + { + result = CampaignMission.OpenVillageMission(nextLocation.GetSceneName(1), nextLocation, talkToChar); + } + else if (nextLocation.StringId == "tavern") + { + + result = CampaignMission.OpenIndoorMission(nextLocation.GetSceneName(1), 1, nextLocation, talkToChar); + } + return result; + } +} +``` +#4. +Lets write modified behavior +``` +csharp +class ModifiedPlayerTownVisitCampaignBehavior : PlayerTownVisitCampaignBehavior +{ + public ModifiedPlayerTownVisitCampaignBehavior(IGameStarter gameStarter) + { + //Removing original Behavior + CampaignBehaviorBase behaviorInstance = (gameStarter as CampaignGameStarter).CampaignBehaviors.ToList().Find(x => x.GetType()==typeof(PlayerTownVisitCampaignBehavior)); + (gameStarter as CampaignGameStarter).RemoveBehavior(behaviorInstance as PlayerTownVisitCampaignBehavior); + + } +} +``` +#5. +Lets add [GameMenuOption](../_csharp-api/campaignsystem/gamemenu.md) and its delegates,here you can add another GameMenu as distirct and stuff I will keep it simple.Since GameMenuOption's on_condition called after GameMenu's on_init I will use it with same manner.Its really up to you. +``` +csharp +protected new void AddGameMenus(CampaignGameStarter campaignGameSystemStarter) +{ + campaignGameSystemStarter.AddGameMenuOption("village", "tavern", "{=l9sFJawW}Visit the local inn!!", + new GameMenuOption.OnConditionDelegate(this.game_menu_village_village_inn_on_condition), + new GameMenuOption.OnConsequenceDelegate(this.game_menu_village_village_inn_on_consequence), false, 0); +} + +public bool game_menu_village_village_inn_on_condition(MenuCallbackArgs args) +{ + //check if player try to go an interior scene + //you can just just return false and option will not be visible also you can enable it for specific settlement etc. here like: + //if(!Settlement.CurrentSettlement.LocationComplex.GetListOfLocations().Any((Location x) => x.StringId == "tavern")) return false + if (this.CheckAndOpenNextLocation(args)) + { + return false; + } + //these part I believe possible quest and helper icons inside scene when you press ALT + bool shouldBeDisabled; + TextObject disabledText; + bool canPlayerDo = Campaign.Current.Models.SettlementAccessModel.CanMainHeroAccessLocation(Settlement.CurrentSettlement, "tavern", out shouldBeDisabled, out disabledText); + List currentLocations = Settlement.CurrentSettlement.LocationComplex.FindAll((string x) => x == "tavern").ToList(); + args.OptionIssueType = Campaign.Current.IssueManager.CheckIssueForMenuLocations(currentLocations); + args.OptionQuestStatus = Campaign.Current.QuestManager.CheckQuestForMenuLocations(currentLocations); + args.optionLeaveType = GameMenuOption.LeaveType.Submenu; + + return MenuHelper.SetOptionProperties(args, canPlayerDo, shouldBeDisabled, disabledText); +} + +private void game_menu_village_village_inn_on_consequence(MenuCallbackArgs args) +{ + //it doesnt work setting next location and call CheckAndOpenNextLocation due to MapState basicially same code in CheckAndOpenNextLocation probably can be reduced to 1 method + Settlement currentSettlement = PlayerEncounter.EncounterSettlement; + PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement);//here we use our modified encounter + PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(LocationComplex.Current.GetLocationWithId("tavern"), + LocationComplex.Current.GetLocationWithId("village_center"), null, null); + + Campaign.Current.GameMenuManager.NextLocation = null; + Campaign.Current.GameMenuManager.PreviousLocation = null; +} +``` +#6. +Lets add CheckAndOpenNextLocation method, the methods have same naming convention(if not same) with parent class to when you check it you can recognize. +``` +csharp +public bool CheckAndOpenNextLocation(MenuCallbackArgs args) +{ + /check if player try to go an interior scene + if (Campaign.Current.GameMenuManager.NextLocation != null && GameStateManager.Current.ActiveState is MapState) + { + Settlement currentSettlement = PlayerEncounter.EncounterSettlement; + string stringId = Campaign.Current.GameMenuManager.NextLocation.StringId; + + + PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement); + + PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(Campaign.Current.GameMenuManager.NextLocation, + Campaign.Current.GameMenuManager.PreviousLocation, null, null); + //its important to set these 2 to null because after mission end game switch to menu and if Next location not null it will loop here(passage points(Doors) does assign next points) + Campaign.Current.GameMenuManager.NextLocation = null; + Campaign.Current.GameMenuManager.PreviousLocation = null; + + //this is which menu will be game showing after mission end(using passage points(door) also ends mission) + //hard coded it will go village menu every time when player leave area + Campaign.Current.GameMenuManager.SetNextMenu("village"); + + return true; + } + return false; +} +``` +There is some stuffs in parent class for Game Menu indexes etc. + +#7. +Finally this part is important,modifying existing behaviors you need to understand how it works partially if not whole but at the end you can do this for every one of them(if the event did not occur before here) +Till here all we did add a menu option and edit VillageEncounter here we'll first call parent's register event then delete listeners on the function we edited(AddGameMenu in this situation) and add listener again. +``` +csharp +public new void OnAfterNewGameCreated(CampaignGameStarter campaignGameStarter) +{ + base.OnAfterNewGameCreated(campaignGameStarter);//Parent class call add game menu here + this.AddGameMenus(campaignGameStarter); +} +public override void RegisterEvents() +{ + //ReRegister orignal events + base.RegisterEvents(); + //Get behavior, it is this class now(removed on constructor) + CampaignBehaviorBase baseToModify = Campaign.Current.CampaignBehaviorManager.GetBehavior(); + //Remove listener on modified method + CampaignEvents.OnNewGameCreatedEvent.ClearListeners(baseToModify); + CampaignEvents.OnGameLoadedEvent.ClearListeners(baseToModify); + //Add listener back with new method + CampaignEvents.OnNewGameCreatedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); + CampaignEvents.OnGameLoadedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); +} +``` +#8. +Add modified behavior to gameStarter +``` +csharp +public class VillageInteriorScene : MBSubModuleBase +{ + protected override void InitializeGameStarter(Game game, IGameStarter starterObject) + { + (starterObject as CampaignGameStarter).AddBehavior(new ModifiedPlayerTownVisitCampaignBehavior(starterObject)); + } +} +``` \ No newline at end of file From c32cb1e256dd01a8eea3187b5d04ceede1708c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=BCcahit=20Tanac=C4=B1o=C4=9Flu?= Date: Wed, 25 Aug 2021 20:35:27 +0300 Subject: [PATCH 3/6] UPDATE code parts was not working --- ...sting_behavior_via_reregistering_events.md | 301 +++++++++--------- 1 file changed, 146 insertions(+), 155 deletions(-) diff --git a/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md index 6072d00..931f723 100644 --- a/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md +++ b/docs/_tutorials/altering_existing_behavior_via_reregistering_events.md @@ -22,190 +22,181 @@ Make sure you have passage point with correct id(location id on xml file) on toL Just copy and paste both xml to your [ModuleData](../_intro/folder-structure.md) folder. I will just add tavern for village_complex and will name it new_complex here how it looks -``` -xml - - - - - - - +```xml + + + + + + + ``` for village I choose Polisia since its close to starting point as empire culture,we just need to edit Locations part here how it looks -``` -xml - - - - +```xml + + + + ``` you need to add these 2 xmls to `SubModule.xml` and also need to add .xslt to override native xmls.[See here](http://docs.modding.bannerlord.com/bestpractices/xslt_usage_tutorial/) #3. Modify Village Encounter -``` -csharp -class ModifiedVillageEncouter : VillageEncouter -{ - public override IMission CreateAndOpenMissionController(Location nextLocation, Location previousLocation = null, CharacterObject talkToChar = null, string playerSpecialSpawnTag = null) - { - - IMission result = null; - if (nextLocation.StringId == "village_center") - { - result = CampaignMission.OpenVillageMission(nextLocation.GetSceneName(1), nextLocation, talkToChar); - } - else if (nextLocation.StringId == "tavern") - { - - result = CampaignMission.OpenIndoorMission(nextLocation.GetSceneName(1), 1, nextLocation, talkToChar); - } - return result; - } -} +```csharp + class ModifiedVillageEncouter : VillageEncouter + { + public override IMission CreateAndOpenMissionController(Location nextLocation, Location previousLocation = null, CharacterObject talkToChar = null, string playerSpecialSpawnTag = null) + { + + IMission result = null; + if (nextLocation.StringId == "village_center") + { + result = CampaignMission.OpenVillageMission(nextLocation.GetSceneName(1), nextLocation, talkToChar); + } + else if (nextLocation.StringId == "tavern") + { + + result = CampaignMission.OpenIndoorMission(nextLocation.GetSceneName(1), 1, nextLocation, talkToChar); + } + return result; + } + } ``` #4. Lets write modified behavior -``` -csharp -class ModifiedPlayerTownVisitCampaignBehavior : PlayerTownVisitCampaignBehavior -{ - public ModifiedPlayerTownVisitCampaignBehavior(IGameStarter gameStarter) - { - //Removing original Behavior - CampaignBehaviorBase behaviorInstance = (gameStarter as CampaignGameStarter).CampaignBehaviors.ToList().Find(x => x.GetType()==typeof(PlayerTownVisitCampaignBehavior)); - (gameStarter as CampaignGameStarter).RemoveBehavior(behaviorInstance as PlayerTownVisitCampaignBehavior); - - } -} +```csharp + class ModifiedPlayerTownVisitCampaignBehavior : PlayerTownVisitCampaignBehavior + { + public ModifiedPlayerTownVisitCampaignBehavior(IGameStarter gameStarter) + { + //Removing original Behavior + CampaignBehaviorBase behaviorInstance = (gameStarter as CampaignGameStarter).CampaignBehaviors.ToList().Find(x => x.GetType()==typeof(PlayerTownVisitCampaignBehavior)); + (gameStarter as CampaignGameStarter).RemoveBehavior(behaviorInstance as PlayerTownVisitCampaignBehavior); + } + } ``` #5. Lets add [GameMenuOption](../_csharp-api/campaignsystem/gamemenu.md) and its delegates,here you can add another GameMenu as distirct and stuff I will keep it simple.Since GameMenuOption's on_condition called after GameMenu's on_init I will use it with same manner.Its really up to you. -``` -csharp -protected new void AddGameMenus(CampaignGameStarter campaignGameSystemStarter) -{ - campaignGameSystemStarter.AddGameMenuOption("village", "tavern", "{=l9sFJawW}Visit the local inn!!", - new GameMenuOption.OnConditionDelegate(this.game_menu_village_village_inn_on_condition), - new GameMenuOption.OnConsequenceDelegate(this.game_menu_village_village_inn_on_consequence), false, 0); -} - -public bool game_menu_village_village_inn_on_condition(MenuCallbackArgs args) -{ - //check if player try to go an interior scene - //you can just just return false and option will not be visible also you can enable it for specific settlement etc. here like: - //if(!Settlement.CurrentSettlement.LocationComplex.GetListOfLocations().Any((Location x) => x.StringId == "tavern")) return false - if (this.CheckAndOpenNextLocation(args)) +```csharp + protected new void AddGameMenus(CampaignGameStarter campaignGameSystemStarter) { - return false; - } - //these part I believe possible quest and helper icons inside scene when you press ALT - bool shouldBeDisabled; - TextObject disabledText; - bool canPlayerDo = Campaign.Current.Models.SettlementAccessModel.CanMainHeroAccessLocation(Settlement.CurrentSettlement, "tavern", out shouldBeDisabled, out disabledText); - List currentLocations = Settlement.CurrentSettlement.LocationComplex.FindAll((string x) => x == "tavern").ToList(); - args.OptionIssueType = Campaign.Current.IssueManager.CheckIssueForMenuLocations(currentLocations); - args.OptionQuestStatus = Campaign.Current.QuestManager.CheckQuestForMenuLocations(currentLocations); - args.optionLeaveType = GameMenuOption.LeaveType.Submenu; - - return MenuHelper.SetOptionProperties(args, canPlayerDo, shouldBeDisabled, disabledText); -} + campaignGameSystemStarter.AddGameMenuOption("village", "tavern", "{=l9sFJawW}Visit the local inn!!", + new GameMenuOption.OnConditionDelegate(this.game_menu_village_village_inn_on_condition), + new GameMenuOption.OnConsequenceDelegate(this.game_menu_village_village_inn_on_consequence), false, 0); + } + + public bool game_menu_village_village_inn_on_condition(MenuCallbackArgs args) + { + //check if player try to go an interior scene + //you can just just return false and option will not be visible also you can enable it for specific settlement etc. here like: + //if(!Settlement.CurrentSettlement.LocationComplex.GetListOfLocations().Any((Location x) => x.StringId == "tavern")) return false + if (this.CheckAndOpenNextLocation(args)) + { + return false; + } + //these part I believe possible quest and helper icons inside scene when you press ALT + bool shouldBeDisabled; + TextObject disabledText; + bool canPlayerDo = Campaign.Current.Models.SettlementAccessModel.CanMainHeroAccessLocation(Settlement.CurrentSettlement, "tavern", out shouldBeDisabled, out disabledText); + List currentLocations = Settlement.CurrentSettlement.LocationComplex.FindAll((string x) => x == "tavern").ToList(); + args.OptionIssueType = Campaign.Current.IssueManager.CheckIssueForMenuLocations(currentLocations); + args.OptionQuestStatus = Campaign.Current.QuestManager.CheckQuestForMenuLocations(currentLocations); + args.optionLeaveType = GameMenuOption.LeaveType.Submenu; + + return MenuHelper.SetOptionProperties(args, canPlayerDo, shouldBeDisabled, disabledText); + } -private void game_menu_village_village_inn_on_consequence(MenuCallbackArgs args) -{ - //it doesnt work setting next location and call CheckAndOpenNextLocation due to MapState basicially same code in CheckAndOpenNextLocation probably can be reduced to 1 method - Settlement currentSettlement = PlayerEncounter.EncounterSettlement; - PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement);//here we use our modified encounter - PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(LocationComplex.Current.GetLocationWithId("tavern"), - LocationComplex.Current.GetLocationWithId("village_center"), null, null); - - Campaign.Current.GameMenuManager.NextLocation = null; - Campaign.Current.GameMenuManager.PreviousLocation = null; -} -``` -#6. -Lets add CheckAndOpenNextLocation method, the methods have same naming convention(if not same) with parent class to when you check it you can recognize. -``` -csharp -public bool CheckAndOpenNextLocation(MenuCallbackArgs args) -{ - /check if player try to go an interior scene - if (Campaign.Current.GameMenuManager.NextLocation != null && GameStateManager.Current.ActiveState is MapState) + private void game_menu_village_village_inn_on_consequence(MenuCallbackArgs args) { + //it doesnt work setting next location and call CheckAndOpenNextLocation due to MapState basicially same code in CheckAndOpenNextLocation probably can be reduced to 1 method Settlement currentSettlement = PlayerEncounter.EncounterSettlement; - string stringId = Campaign.Current.GameMenuManager.NextLocation.StringId; - - - PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement); - - PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(Campaign.Current.GameMenuManager.NextLocation, - Campaign.Current.GameMenuManager.PreviousLocation, null, null); - //its important to set these 2 to null because after mission end game switch to menu and if Next location not null it will loop here(passage points(Doors) does assign next points) + PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement);//here we use our modified encounter + PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(LocationComplex.Current.GetLocationWithId("tavern"), + LocationComplex.Current.GetLocationWithId("village_center"), null, null); + Campaign.Current.GameMenuManager.NextLocation = null; Campaign.Current.GameMenuManager.PreviousLocation = null; - - //this is which menu will be game showing after mission end(using passage points(door) also ends mission) - //hard coded it will go village menu every time when player leave area - Campaign.Current.GameMenuManager.SetNextMenu("village"); - - return true; + } +``` +#6. +Lets add CheckAndOpenNextLocation method, the methods have same naming convention(if not same) with parent class to when you check it you can recognize. +```csharp + public bool CheckAndOpenNextLocation(MenuCallbackArgs args) + { + /check if player try to go an interior scene + if (Campaign.Current.GameMenuManager.NextLocation != null && GameStateManager.Current.ActiveState is MapState) + { + Settlement currentSettlement = PlayerEncounter.EncounterSettlement; + string stringId = Campaign.Current.GameMenuManager.NextLocation.StringId; + + + PlayerEncounter.LocationEncounter = new ModifiedVillageEncouter(currentSettlement); + + PlayerEncounter.LocationEncounter.CreateAndOpenMissionController(Campaign.Current.GameMenuManager.NextLocation, + Campaign.Current.GameMenuManager.PreviousLocation, null, null); + //its important to set these 2 to null because after mission end game switch to menu and if Next location not null it will loop here(passage points(Doors) does assign next points) + Campaign.Current.GameMenuManager.NextLocation = null; + Campaign.Current.GameMenuManager.PreviousLocation = null; + + //this is which menu will be game showing after mission end(using passage points(door) also ends mission) + //hard coded it will go village menu every time when player leave area + Campaign.Current.GameMenuManager.SetNextMenu("village"); + + return true; + } + return false; } - return false; -} ``` There is some stuffs in parent class for Game Menu indexes etc. #7. Finally this part is important,modifying existing behaviors you need to understand how it works partially if not whole but at the end you can do this for every one of them(if the event did not occur before here) Till here all we did add a menu option and edit VillageEncounter here we'll first call parent's register event then delete listeners on the function we edited(AddGameMenu in this situation) and add listener again. -``` -csharp -public new void OnAfterNewGameCreated(CampaignGameStarter campaignGameStarter) -{ - base.OnAfterNewGameCreated(campaignGameStarter);//Parent class call add game menu here - this.AddGameMenus(campaignGameStarter); -} -public override void RegisterEvents() -{ - //ReRegister orignal events - base.RegisterEvents(); - //Get behavior, it is this class now(removed on constructor) - CampaignBehaviorBase baseToModify = Campaign.Current.CampaignBehaviorManager.GetBehavior(); - //Remove listener on modified method - CampaignEvents.OnNewGameCreatedEvent.ClearListeners(baseToModify); - CampaignEvents.OnGameLoadedEvent.ClearListeners(baseToModify); - //Add listener back with new method - CampaignEvents.OnNewGameCreatedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); - CampaignEvents.OnGameLoadedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); -} +```csharp + public new void OnAfterNewGameCreated(CampaignGameStarter campaignGameStarter) + { + base.OnAfterNewGameCreated(campaignGameStarter);//Parent class call add game menu here + this.AddGameMenus(campaignGameStarter); + } + public override void RegisterEvents() + { + //ReRegister original events + base.RegisterEvents(); + //Get behavior, it is this class now(removed on constructor) + CampaignBehaviorBase baseToModify = Campaign.Current.CampaignBehaviorManager.GetBehavior(); + //Remove listener on modified method + CampaignEvents.OnNewGameCreatedEvent.ClearListeners(baseToModify); + CampaignEvents.OnGameLoadedEvent.ClearListeners(baseToModify); + //Add listener back with new method + CampaignEvents.OnNewGameCreatedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); + CampaignEvents.OnGameLoadedEvent.AddNonSerializedListener(this, new Action(this.OnAfterNewGameCreated)); + } ``` #8. Add modified behavior to gameStarter -``` -csharp -public class VillageInteriorScene : MBSubModuleBase -{ - protected override void InitializeGameStarter(Game game, IGameStarter starterObject) +```csharp + public class VillageInteriorScene : MBSubModuleBase { - (starterObject as CampaignGameStarter).AddBehavior(new ModifiedPlayerTownVisitCampaignBehavior(starterObject)); + protected override void InitializeGameStarter(Game game, IGameStarter starterObject) + { + (starterObject as CampaignGameStarter).AddBehavior(new ModifiedPlayerTownVisitCampaignBehavior(starterObject)); + } } -} ``` \ No newline at end of file From 7338908a3e192fd5ff44b83952442b582b19e947 Mon Sep 17 00:00:00 2001 From: Vitaly Mikhailov Date: Wed, 25 Aug 2021 21:52:46 +0300 Subject: [PATCH 4/6] Update README.md Added new entries so they will be visibly on the site --- docs/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/README.md b/docs/README.md index 7c69ca6..9a09bd6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,8 @@ * [Modding Gauntlet UIs Without C#](_tutorials/modding-gauntlet-without-csharp.md) (Easy) * [Packing your Mods for Vortex](_tutorials/packing_mods_for_vortex.md) (Easy) * [Modifying/Adding Settlements](_tutorials/new_settlements.md) (Easy) +* [Modifying CampaignBehaviors - GameModels](_tutorials/altering_existing_behavior_via_gamemodels.md) (Easy) +* [Modifying CampaignBehaviors - Reregister Event](_tutorials/altering_existing_behavior_via_reregistering_events.md) (Medicore) ## [C# API Documentation](_csharp-api/) From 2671d906cfcabbe68d1ca3a69885fbc1130b96c3 Mon Sep 17 00:00:00 2001 From: Vitaly Mikhailov Date: Wed, 25 Aug 2021 21:53:30 +0300 Subject: [PATCH 5/6] Update README.md Consistent formatting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 271b9c5..27ee55f 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ * [Modding Gauntlet UIs Without C#](docs/_tutorials/modding-gauntlet-without-csharp.md) (Easy) * [Packing your Mods for Vortex](docs/_tutorials/packing_mods_for_vortex.md) (Easy) * [Modifying/Adding Settlements](docs/_tutorials/new_settlements.md) (Easy) -* [Modifying CampaignBehaviors-GameModels](docs/_tutorials/altering_existing_behavior_via_gamemodels.md)(Easy) -* [Modifying CampaignBehaviors-Reregister Event](docs/_tutorials/altering_existing_behavior_via_reregistering_events.md)(Medicore) +* [Modifying CampaignBehaviors - GameModels](docs/_tutorials/altering_existing_behavior_via_gamemodels.md) (Easy) +* [Modifying CampaignBehaviors - Reregister Event](docs/_tutorials/altering_existing_behavior_via_reregistering_events.md) (Medicore) ## [C# API Documentation](docs/_csharp-api/) * [CampaignSystem](docs/_csharp-api/campaignsystem/) From a989428c4d321f993a0c2cfa2f74279a9b73ccb4 Mon Sep 17 00:00:00 2001 From: Vitaly Mikhailov Date: Wed, 25 Aug 2021 23:58:05 +0300 Subject: [PATCH 6/6] Update altering_existing_behavior_via_gamemodels.md Better formatting, grammar fixes --- ...tering_existing_behavior_via_gamemodels.md | 120 +++++++++--------- 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/docs/_tutorials/altering_existing_behavior_via_gamemodels.md b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md index 98810e6..9d00e74 100644 --- a/docs/_tutorials/altering_existing_behavior_via_gamemodels.md +++ b/docs/_tutorials/altering_existing_behavior_via_gamemodels.md @@ -1,86 +1,80 @@ -Sometimes you might wanna alter some of existing behaviors.Here we try to do this wit 2 different aproach. -### 1-With GameModels -[GameModels](../_csharp-api/core/gamemodel.md) derived from TaleWorlds.Core.GameModel class,there is naming convention like this *Something*Model(abstract class derived from GameModels) and *DefaultSomething*Model(derived from *Something*Model) +Sometimes you might want to alter some of the existing behaviors. Here we try to do this with 2 different aproaches. + +### 1. With GameModels +[GameModels](../_csharp-api/core/gamemodel.md) that are derived from the `TaleWorlds.Core.GameModel` class. +There is a naming convention like `*Something*Model` (when it's an abstract class derived from the GameModels) and `*DefaultSomething*Model` (when it's derived from `Something*Model`). -Some behaviors has related game models which includes key factors for behaviors.You need to check whether your need in model(if there is) or not. +Some behaviors have related game models which include key factors for behaviors. You need to check whether your need them in model (if there are some) or not. -**Note:**Before we start these are what I could do with my knowledge,there is alot way to do this(probably better ways) but once you get the idea you can do as you need. -For this tutorial I want to edit SettlementLoyaltyModel and do followings: +**Note:** Before we start these are what I could do with my knowledge, there is alot way to do this (probably better ways) but once you get the idea you can do as you need. +For this tutorial I want to edit `SettlementLoyaltyModel` and do the followings: +1. Add +2 Loyality Buff for Settlement belongs to Vlandian Kingdom, lets say as Culturel Buff. +2. Change the Owner Culture Debuff from -3 to -1. +3. Change the `DailyNotableRelationBonus` from 1 to 2 and the threshold for this to happen from 75 to 55. -1.Add +2 Loyality Buff for Settlement belongs to Vlandian Kingdom lets say as Culturel Buff -2.Change the Owner Culture Debuff from -3 to -1 -3.Change the DailyNotableRelationBonus from 1-->2 and threshold for this to happen from 75-->55 - -first extend model +First, extend the model: ```csharp class ModifiedDefaultSettlementLoyaltyModel : DefaultSettlementLoyaltyModel ``` -add new model with gameStarter +Then add the new model via the `gameStarter` in `InitializeGameStarter`: ```csharp - protected override void InitializeGameStarter(Game game, IGameStarter starterObject) - { - (starterObject as CampaignGameStarter).AddModel(new ModifiedDefaultSettlementLoyaltyModel()); +protected override void InitializeGameStarter(Game game, IGameStarter starterObject) +{ + if (starterObject is CampaignGameStarter campaignGameStarter) + { + campaignGameStarter.AddModel(new ModifiedDefaultSettlementLoyaltyModel()); } -``` -Note:It does remove/(not add at all) original DefaultSettlementLoyaltyModel while creating GameModelManager in AddGameModelsManager() method in TaleWorlds.Core.Game class.If someone else figure it out please edit here. - -lets override things we want to edit +} +``` +Note: It does remove (doesn't duplicate) the original `DefaultSettlementLoyaltyModel` when the `GameModelManager` is creating the models in `AddGameModelsManager()` method in `TaleWorlds.Core.Game` class. If someone else figure it out please edit here. +Lets override the things we want to edit: ```csharp - public override int DailyNotableRelationBonus - { - get - { - return 2; - } - } - public override float ThresholdForNotableRelationBonus - { - get - { - return 55f; - } - } +public override int DailyNotableRelationBonus => 2; +public override float ThresholdForNotableRelationBonus => 55f; - public override ExplainedNumber CalculateLoyaltyChange(Town town, bool includeDescriptions = false) - { - ExplainedNumber myResult = base.CalculateLoyaltyChange(town, includeDescriptions); - - EditLoyalityFactors(ref myResult,"Owner Culture",-1f,includeDescriptions); - VlandianLoyalityBuff(town, ref myResult); - return myResult; - } +public override ExplainedNumber CalculateLoyaltyChange(Town town, bool includeDescriptions = false) +{ + var myResult = base.CalculateLoyaltyChange(town, includeDescriptions); + + EditLoyalityFactors(ref myResult, "Owner Culture", -1f, includeDescriptions); + VlandianLoyalityBuff(town, ref myResult); + return myResult; +} ``` -here the function to Edit existing factor(most of the part are private in models so we cant override method that calculate factor,instead create another ExplaineNumber and edit while copying from other one) + +Here the function to edit an existing factor (most of the parts are private in the models so we can't override the method that calculates the factor, instead we create another `ExplaineNumber` and change it while copying from the original model) ```csharp - public void EditLoyalityFactors(ref ExplainedNumber result,string descriptionOfFactor,float newValue,bool includeDescriptions) +public void EditLoyalityFactors(ref ExplainedNumber result,string descriptionOfFactor,float newValue,bool includeDescriptions) +{ + var listOfFactors = result.GetLines(); + var temp = new ExplainedNumber(0f, includeDescriptions, null); + foreach (var (name, value) in listOfFactors) { - List<(string name, float value)> listOfFactors = result.GetLines(); - ExplainedNumber temp = new ExplainedNumber(0f, includeDescriptions, null); - foreach (var (name,value) in listOfFactors) + if (name.Equals(descriptionOfFactor)) { - if (name.Equals(descriptionOfFactor)) - { - temp.Add(newValue,new TextObject(name,null),null); - continue; - } - temp.Add(value,new TextObject(name,null),null); + temp.Add(newValue,new TextObject(name, null), null); + continue; } - - result = temp; + temp.Add(value,new TextObject(name, null), null); } + + result = temp; +} ``` -and finally adding new factor + +And finally adding the new factor: ```csharp - public void VlandianLoyalityBuff(Town town,ref ExplainedNumber explainedNumber) +public void VlandianLoyalityBuff(Town town, ref ExplainedNumber explainedNumber) +{ + if(town.OwnerClan != null && town.OwnerClan.Kingdom != null && town.OwnerClan.Kingdom.Name.ToString().Equals("Vlandia")) { - if(town.OwnerClan != null && town.OwnerClan.Kingdom != null && town.OwnerClan.Kingdom.Name.ToString().Equals("Vlandia")) - { - TextObject VlandianCultureLoyalityBuffTO = new TextObject("Vlandian culture buff", null); - explainedNumber.Add(2f, VlandianCultureLoyalityBuffTO, null); - } + var VlandianCultureLoyalityBuffTO = new TextObject("Vlandian culture buff", null); + explainedNumber.Add(2f, VlandianCultureLoyalityBuffTO, null); } +} ``` -### 2-Altering Behavior Class -[Modify Campaign Behavior With ReRegistering Event](./altering_existing_behavior_via_reregistering_events.md) \ No newline at end of file + +### 2. Altering Behavior Class +[Modify Campaign Behavior With ReRegistering Event](./altering_existing_behavior_via_reregistering_events.md)