Jump to content

Can you add lines to a static function without overriding its class? (Feat: Infodump about voices.)


Evilslug

Recommended Posts

I ask, because I have been digging through the source to map all the voice triggers to actual sounds and now realize that some expected and available soundcues were simply left out of vanilla entirely. I've successfully added new voice triggers and fixed broken or omitted ones by overriding the classes in which changes must occur, but obviously this would cause other people's mods to break if people use my voicepacks with anything else that overrides the same classes. (And they said the game was mod friendly...?)

TL/DR: Is there a convenient way to modify a static function to add extra lines (similar to ini files), or do you have to override the whole class that contains it if you need to alter an existing event/function?

 

Example of things I'm doing: Battle Scanners. These had a soldier bark in the last game, but I've never heard a solder say a word in Xcom 2 when tossing one. Upon adding the appropriate soundcue trigger, I've found that the default X2 voice sets don't even have a sound file for the event. That's not the point. Adding a trigger where vanilla voicebanks lack a response does not break the standard voicebank functionality. The point is that by adding the proper trigger to the following function, I can get my own voicepacks to trigger upon tossing a Battle Scanner just fine. With this knowledge, I can generate a 'Voicepack Extension Mod' that would allow anybody who creates voicepack mods in the future to make use of my expanded soldier bark functionality without actually being dependent on it...but at a cost of incompatibility with any other mod that altered the same classes.

//Here's a slice of X2Ability_ItemGrantedAbilitySet.uc that contains the relevant code

static function X2AbilityTemplate BattleScanner()
{

//SNIPPED down to excerpt for forum readability

	Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_item_battlescanner";
	Template.AbilitySourceName = 'eAbilitySource_Item';
	Template.eAbilityIconBehaviorHUD = eAbilityIconBehavior_AlwaysShow;
	Template.Hostility = eHostility_Neutral;
	Template.bDisplayInUITacticalText = false;
	Template.bHideWeaponDuringFire = true;
	Template.ActivationSpeech = 'BattleScanner';  //added needed line here so sound fires when appropriate


The same can also be accomplished with a different type of call that prompts soldier barks. These things can both be wrapped into if statements to change what fires...or the odds of a specific cue firing...etc. This is all pretty useful, because there are a number of things that either aren't used (Aid Protocol soundcue) or are entirely broken (Scanning Protocol is triggered, but never defined as a valid cue). It leaves a lot of room for additional sound triggers and functionality, but only if there's a way to implement changes without wrecking compatibility with practically everything else.

 

Out of 197 defined sound triggers, they are only actually using around 80-90 in vanilla (still finalizing my spreadsheet). Many cues seem to be omitted deliberately as duplicates (Target Killed versus Kill, the latter of which is used for all 'soldier-killed-single-something' events). Others are likely absent because they were rolled into an existing soundcue (Order Confirmed sounds were all seemingly rolled into the Moving cue, for example). Some are re-used because they didn't want to make additional cues or assets. (Launch Grenade calls Frag Out just like a thrown frag grenade because comments indicate there was no separate cue for the grenade launcher activation and apparently no intention of ever creating one.)

 

So that's what I'm up to and why I ask the question. I feel the game desperately needs my voice because I do not sound like I am from Kansas like the rest of American XCom. Also, I'm new here. Greetings. (Nothing against Kansas and their utter lack of an accent.)

Link to comment
Share on other sites

 

Is there a convenient way to modify a static function to add extra lines (similar to ini files), or do you have to override the whole class that contains it if you need to alter an existing event/function?

 

 

 

From what I know, even overriding the class will not work, since the function is static. I'm not 100% certain, because unrealscript offers this very weird concept of virtual static functions and I don't know if overriding X2DataSet classes like your mentioned X2Ability_ItemGrantedAbilitySet would work or not. However if would, it will be hell on earth since your Mod will be the only one allowed to touch item granted abilities in that way.

 

What you realy want to do is modify the ability template used for the battlescanner.

 

Would the Template.ActivationSpeech be the only thing oyu need to change in that template definition?

Edited by eXecator
Link to comment
Share on other sites

From what I know, even overriding the class will not work, since the function is static. I'm not 100% certain, because unrealscript offers this very weird concept of virtual static functions and I don't know if overriding X2DataSet classes like your mentioned X2Ability_ItemGrantedAbilitySet would work or not. However if would, it will be hell on earth since your Mod will be the only one allowed to touch item granted abilities in that way.

 

What you realy want to do is modify the ability template used for the battlescanner.

 

Would the Template.ActivationSpeech be the only thing oyu need to change in that template definition?

 

 

Overriding the class actually works fine in this case, it just introduces the issue of breaking any other mod that touches the same part of that class. That's what I'm trying to avoid. :( I have successfully gotten missing/broken voice triggers to fire by overriding a class to insert my own version of a static function like the one mentioned in my original post. (In case it wasn't clear, the mentioned file and function hold the full ability template for the Battle Scanner, I just cut most of it out for brevity.) To answer your question, though; a single-line addition isn't always the case but in many instances, it is all that I need to do. If there's a way to drop that line into the Battle Scanner's ability function without overwriting the class to insert the modified function, that's what I'd like to know about.

 

They built a lot of the events/functions without modding in mind, which is easy to tell by looking at the ones that were built with modding in mind.

 

For another example, there's a sound cue/trigger specifically for concealed unit movement speech that was obviously intended for such, but it cannot be used because they baked the Concealed Move Whisper switch into the very AKBanks that you have to override to get your own sounds into the game. Voicepack concealed movement triggers could be enabled, but it would involve adding statements to some existing class functions. Again...back to breaking classes for anyone else who wished to change them. :(

Link to comment
Share on other sites

I'm planning to publish a Mod which might help you out.

http://forums.nexusmods.com/index.php?/topic/3830880-mod-easy-template-modifications/

 

I dont have much time for it now, so it wont be before sunday evening...

 

And I would have to add your specific usecase Abbility + SetActivationSpeech or enable some callback functionality but this will take more time.

Link to comment
Share on other sites

 

 

They built a lot of the events/functions without modding in mind, which is easy to tell by looking at the ones that were built with modding in mind.

Sounds like you have a lot of experience here we can learn from. One factor in an unmoddable function is the use of private variables, which aren't visible to a subclass. Are there other factors you have seen which are bad from a modding standpoint? As you see there is some information, some wrong, about which functions are unmoddable at all. We need to build a correct way to tell, so intermediate modders don't waste their time.

 

Similarly what are the factors that make a class very good from a modding standpoint? Using "var config" a lot makes the variables accessible to ini-file-only modders, which I guess is good.

Link to comment
Share on other sites

eXecator, just as a reference, the three most common instances of sound trigger involve the following:

 

Template.OnThrowBarkSoundCue = 'Foo';
//or
Template.ActivationSpeech = 'Foo';
//or
Unit.UnitSpeak('Foo');
//That last is awesome, but tricky. The sounding 'Unit' can be in a vast number of states. Ex:
UnitWhoStubbedHisToe.UnitSpeak('OW Dammit')

 

 

They built a lot of the events/functions without modding in mind, which is easy to tell by looking at the ones that were built with modding in mind.

Sounds like you have a lot of experience here we can learn from. One factor in an unmoddable function is the use of private variables, which aren't visible to a subclass. Are there other factors you have seen which are bad from a modding standpoint? As you see there is some information, some wrong, about which functions are unmoddable at all. We need to build a correct way to tell, so intermediate modders don't waste their time.

 

Similarly what are the factors that make a class very good from a modding standpoint? Using "var config" a lot makes the variables accessible to ini-file-only modders, which I guess is good.

 

I'm far from an expert in UE script stuff, but I've done some extensive digging in my quest to map the sound triggers. Static functions can indeed be unmoddable if you forget to include the 'dependson' and 'config' statements from the file you're extending. I think that's probably where some people have gone wrong and that the others may have run afoul of a 'native' or 'core' within the function they were attempting to change. For one example:

class X2Ability_ItemGrantedAbilitySet_Slug extends X2Ability_ItemGrantedAbilitySet dependson(XComGameStateContext_Ability) config(GameCore);

static function X2AbilityTemplate BattleScanner()
{

The override script I wrote to test missing sound barks was only adding a single line to the function in question and a lot of people would think it doesn't depend on anything but the master file it is altering. I accidentally left the 'config' statement off when I first wrote it and the script bombed. It would not compile or function properly without inheriting both the 'dependson' and 'config' settings of the original X2Ability_ItemGrantedAbilitySet, as seen above. That may not always be the case, but it is worth remembering as a potential source of problems.

 

I'd say outside the private variables, the other big unfriendly factor is naming conventions versus usage. Do not ever assume that the name of a function, variable, or class even remotely represents what you think it should. Do not assume that the first instance you found is the instance you need to act upon. Always look at the actual code and try a CTRL+SHFT+F to see where else it appears. You'd think "Engaging Hostiles" would be a soldier bark for attacking something or scattering a pod, but you'd be wrong. From looking at the code, it only ever fires if an alien kills something. Same with "Hidden Movement". It looks like a soldier bark for hearing something and even the animation chain of events points toward that, but I am pretty certain the Vox file it uses is alien chitter.

 

The biggest factors in making a class good from a modding standpoint are externalized (config) variables, minimized or well documented calls to elsewhere, and an ability to easily add new items. If the class you're looking at doesn't have what you need, then make certain what you want isn't available in a different way before you start overwriting classes. The 'Bleeding Out' ability is referenced over 120 times in some 20+ files. People often try to brute force their idea on the first seemingly valid instance of a thing they find. if the programmer(s) responsible for a class ever stopped to consider the project from outside the full-build environment; it shows in regular comments, verbose descriptions, and externalized variables. Those are the places to start from. You can also see it in places where an unnecessary IF/THEN statement accounts for the possibility that a modder might one day wish to override part of the standard functionality.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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