Jump to content

Template Modification Without ScreenListeners


eXecator

Recommended Posts

One popular way to modify Templates has been to use a ScreenListener as a hook to trigger the modifications. This UI workaround always bugged me, so I was stubborn and tried to find another way. I found one that works for me. It's not field-tested or anything. I'd like to share and I'm interested in feedback. So here we go...
The two main problems have been
  1. find a reliable hook, preferably early
  2. find a way to modify templates for all difficulties
1)
During application start the engine will make all the CreateTemplates() calls it can find to collect templates. It then creates variants for each difficulty if appropriate and stores them into their corresponding TemplateManagers. After that comes a validation step in form of ValidateTemplate() calls. So the earliest time we can hope to intercept all templates would be right after they got created, but still before validation. And that's where we want to hook in. Luckily there is one easy place to do this. Template creation itself.
Code in our beloved CreateTemplates() functions will be among the earliest things executed and is guaranteed to be executed pre-validation. Hurrey! The only thing we have to make sure now is that our sneaky not at all template creating code will get executed last, so that all templates are already waiting for us. Using some generous logging I found that
  • custom templates of a certain type will get created after base game templates of that type
  • the X2AmbientNarrativeCriteriaTemplates are the ones that get created last
With that in mind a tried this as a hook
class HookingTemplate extends X2AmbientNarrativeCriteria;

static function array<X2DataTemplate> CreateTemplates()
{
  local array<X2DataTemplate> Templates;

  `log("@@@@@@@@@@@@@@@@@@@@@@@@ HookingTemplate @@@@@@@@@@@@@@@@@@@@@@@@");

  DoStuff();

  `log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");

  return Templates;
}
I guess we can get into trouble here if we want to do something with a custom template derived from X2AmbientNarrativeCriteriaTemplate. But they aren't that famous right now and if we can live with that, we got a freaking early place to hook in.
2)
The of the shelve solution here is to loop through all the difficulties to get all templates. Sadly I haven't found a way to circumvent that. The issue with our hook is (as is with listeners on Shell I heard) that we don't have a campaign to adjust difficulty settings yet and Managers will default to veteran difficulty. But what we can do is, create the XComGameState_CampaignSettings ourself. I'll pull up my post of joy after getting it to work...

eXecator, on 20 Feb 2016 - 6:15 PM, said:
It is done.
I was able to access and modify templates for all the difficulties during application startup (even pre-TemplateValidation, which is kinda nice). Convincing XCom to accept CampaignSettings that early was like running through a game crash minefield for me though. This is what finaly worked for me.
local XComGameStateHistory History;
local XComGameStateContext_StrategyGameRule StrategyStartContext;
local XComGameState StartState;
local XComGameState_CampaignSettings Settings;

local X2CharacterTemplateManager Characters;
local X2CharacterTemplate soldier;
local int DifficultyIndex;

Characters = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager();
History = `XCOMHISTORY;

StrategyStartContext = XComGameStateContext_StrategyGameRule(class'XComGameStateContext_StrategyGameRule'.static.CreateXComGameStateContext());
StrategyStartContext.GameRuleType = eStrategyGameRule_StrategyGameStart;
StartState = History.CreateNewGameState(false, StrategyStartContext);
History.AddGameStateToHistory(StartState);

Settings = new class'XComGameState_CampaignSettings'; // Do not use CreateStateObject() here
StartState.AddStateObject(Settings);

for( DifficultyIndex = `MIN_DIFFICULTY_INDEX; DifficultyIndex <= `MAX_DIFFICULTY_INDEX; ++DifficultyIndex )
{
  Settings.SetDifficulty(DifficultyIndex);
  `log("Difficulty:"@string(class'XComGameState_CampaignSettings'.static.GetDifficultyFromSettings()));

  soldier = Characters.FindCharacterTemplate('Soldier');
  soldier.CharacterBaseStats[eStat_HP] = 10 + DifficultyIndex;
}

History.ResetHistory(); // yeah, lets do that, nothing happend anyway...
The key parts here are newing up the Settings instead of asking kindly for StateObject creation, since that always ended in proccess termination for me. And of course as Amineri mentioned, we have to mop up the History aferwards. We do not want anybody downstream to ask serious questions about that house of cards we build here to fool the TemplateManagers.

 

 

It's only messing with soldiers and BaseStats can be modified easier, but you should be able to get the idea. So in a nutshell this is the way I'm looking forward to modify my templates in the near future. I don't think this solution is much prettier then the stock one, but at least it doesn't have UI dependencies. If it's hacky, but it has to be done, then it might be a good idea to do it only once and in a single place. So I'm thinking of developing a mod which tries to expose easy to access ways to fiddle with templates. http://forums.nexusmods.com/index.php?/topic/3830880-mod-easy-template-modifications/
Edited by eXecator
Link to comment
Share on other sites

I'm not sure I follow... Are you overriding the X2AmbientNarrativeCriteria class? Or does simply extending it do the same job? This is very interesting sutff, but it also makes me very very nervous to read through :) Hopefully, Firaxis themselves will implement a sane way of messing with base templates at some point, as that seems like the majority of what people want to do. In either case, thank you for the explanation.

Link to comment
Share on other sites

There is no class overriding going on here. Its just extending. The engine will call CreateTemplates() on classes that derive from X2DataSet. The class X2AmbientNarrativeCriteria is one of those and happens to be last in line.

 

Cultivate your nervousness, it might keep you save and sane. =)

 

By the way I'm all open for Firaxis to better support template modifications.

Link to comment
Share on other sites

Right so I *think* even I understand this a bit, which is pretty good going considering. In the code where your stating the solder edit, whats the command for specific items? I actually think I get how to edit the rest of it to suit, but that last bit has be a bit perplexed since I dont know what to add.

 

I mean this bit;

 

 

{
  Settings.SetDifficulty(DifficultyIndex);
  `log("Difficulty:"@string(class'XComGameState_CampaignSettings'.static.GetDifficultyFromSettings()));

  soldier = Characters.FindCharacterTemplate('Soldier');
  soldier.CharacterBaseStats[eStat_HP] = 10 + DifficultyIndex;
}
 
Edited by Xcomhadrian
Link to comment
Share on other sites

At that point you can basicaly do whatever you want with the soldier template. If you want to look at the class defninition, so you can see whats there to edit, you're going to be interested in X2CharacterTemplate.uc.

 

Or are you looking for anything specific to modifiy?

Link to comment
Share on other sites

There is no class overriding going on here. Its just extending. The engine will call CreateTemplates() on classes that derive from X2DataSet. The class X2AmbientNarrativeCriteria is one of those and happens to be last in line.

 

Ah, I see. That actually explains a lot, and also explains why attempts to override static functions nevertheless "seem" to work but only sometimes. And it only makes me nervous because I'm naturally skittish of doing things software wasn't intended to do. It's great when it works, but I'm usually not smart enough to figure out why it works and why it fails :)

 

In either case, thank you kindly.

Link to comment
Share on other sites

That could be the solution i've been waiting for (directly from some SDK update/extra Tools that may well never come our ways, sadly)...

 

I just want an easiest process to reproduce what TexMod was able to achieve by snatching runtime Hash-Tags and looping in its custom TPF files aiming to replace simply Vanilla assets. Without the whole complex tragedy of searching each instances of target objects -- be they supplied by core functions or embedded in various gfx components.

 

To be precise (currently), Ranks Icons.. everywhere they're getting dispatched on the UI -- all at once.

 

And maybe some more interesting HUD elements mainly archived into UILIbrary_Common.UPK, etc.

 

PS; Indirectly this is a formal request to any genius coders out there for a specific XCom2 IDE that would perform the same tasks but within compatible limits inherent to things like the Steam-Launcher or Flash resources -- just to name these two.

Edited by Zyxpsilon
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...