Jump to content

Question: Templates vs Classes


aggies11

Recommended Posts

 

In terms of Templates you can't actually extend a template in the game

 

Actually, technically, you can actually extend a template. E.g. :

class X2AbilityTemplate_MyMod extends X2AbilityTemplate;

However, the new template you define won't retroactively apply to all of the existing templates created by the base-game, nor created by other mods. Since those templates are created with new class'X2AblityTemplate', you won't be able to cast to your extension -- Unreal will return a null if you try that.

 

However, if you write NEW code someplace, you can try and cast templates to your new class, and when it succeeds, then you can use new fields/functionality as defined in your template. It's kind of an edge case, but is something I'd contemplated for allowing modded-in new ability to set config-value-based localized strings. For example a localized string "Ability does +X Damage", where the X is replaced at run-time by a value defined in config. The base game accomplishes this through a gargantuan separate switch/case statement, but it could be handled within an AbilityTemplate extension.

 

 

It really is a thankless task but

 

By thanking me you've actually negated your statement here. But I'm glad the guides I'm able to provide have been of some help :D

Link to comment
Share on other sites

 

 

In terms of Templates you can't actually extend a template in the game

 

Actually, technically, you can actually extend a template. E.g. :

 

Ah, I'm guessing I'm confusing the fact that you can't extend a native class, vs the fact that you can't class override a native class? I'm hoping I didn't make up either of those things..? :sad:

 

 

By thanking me you've actually negated your statement here. But I'm glad the guides I'm able to provide have been of some help

 

Spoken like a true programmer :tongue: Mod tools are great, and they definitely have set up quite the system here, but it also means there is a ton more to absorb. As probably the most useful source of "documentation" out there, your work has been absolutely essential. Someone should definitely go about collecting all your many posts in the various threads, into a single location as I'm sure it's probably out numbered the guides at this point just in terms of quantity of useful information.

 

I'm almost ready to put the final touches on my mod. What would have been a single line change back in 2012 for Enemy Unknown, has balooned into 60KB of source files... but it's been an entertaining ride. I'd imagine we are going to be seeing a good amount of new and surprising things for this game coming out of the community over the next months and longer. :smile: As is the case with many modding endeavors, I've actually only been able to play about two missions so far, and that was mostly for testing purposes. Such is the life ;)

Edited by aggies11
Link to comment
Share on other sites

 

Ah, I'm guessing I'm confusing the fact that you can't extend a native class, vs the fact that you can't class override a native class? I'm hoping I didn't make up either of those things..? :sad:

 

 

 

I'm pretty sure that both of these are false. And here are my counterexamples (I'm actually more of a mathematician by training than a programmer...).

 

1) Extending native classes

 

In the LW_OfficerPack mod I extend the class :

class X2AbilityMultiTarget_AllAllies extends X2AbilityMultiTarget_AllUnits native(Core);

And the parent of that definitely has a bunch of native functions inside of it :

class X2AbilityMultiTarget_AllUnits extends X2AbilityMultiTarget_Radius native(Core);
...
simulated native function GetMultiTargetOptions(const XComGameState_Ability Ability, out array<AvailableTarget> Targets);
simulated native function GetMultiTargetsForLocation(const XComGameState_Ability Ability, const vector Location, out AvailableTarget Target);
simulated native function GetValidTilesForLocation(const XComGameState_Ability Ability, const vector Location, out array<TTile> ValidTiles);

My class extension looks like this :

class X2AbilityMultiTarget_LWOfficerCommandRange extends X2AbilityMultiTarget_AllAllies;

Note that I'm not including the native class keyword, because my class doesn't define any native functions -- THAT is something we can't do as modders, because native code has to live in the exe. However, we can override the native functions defined in the parent class using unrealscript functions. That was my primary purpose in creating the class.

 

One thing to watch out for is that native-to-native function calls can't be intercepted by unrealscript virtual machine. So I wanted only to functionally override GetValidTilesForLocation, but because I had to override other native stuff in order to intercept all calls to it.

 

Having created this new extended class (extending native classes), we proceeded to utilize them in Ability Template definitions, as usual :

//add command range
Template.AbilityMultiTargetStyle = new class'X2AbilityMultiTarget_LWOfficerCommandRange';

-------------------------------------

 

 

2) Overriding native classes

 

A really popular mod (which is also included as a sample mod in the SDK) overrides a native class :

class XComTacticalInput extends XComInputBase dependson(X2GameRuleset)
	native(UI);     // TODO - move into separate native base class

This class is overridden in order to allow definition of the camera rotation angles. And this uses the actual "class override" as configured in the Engine.ini, as well as being an extension of the class.

 

Again, the correct class extension definition doesn't include the native keyword :

class XComTacticalInput_LW extends XComTacticalInput;

And this has been used by plenty of people, so it seems pretty stable...

Link to comment
Share on other sites

*edit* Before I forget - thank you SO MUCH for taking the time to explain this! :)

 

Hmm... I follow you, somewhat. This puts in perspective why you were advising me to only mess with templates during the Strategic part of the game, since GameStates get generated for the Tactical part and then discarded when returning back to Strategy. But I think I follow what you're saying, if not specifically which function names are called when. At game launch, the executable itself will create datasets which search out and create a single instance of all the templates they're responsible for, then put those into data managers for later retrieval. These get used when a tactical game needs to create actors and populate their ability lists for actual spawning in the "level." I'm speaking loosely for the most part, just because I need to internalise the concept before I try to deal with the specific code, but this much I think I can follow. That makes sense. It also explains why changing an ability template in the strategic part would work better, as we don't have gamestate objects created from those templates yet.

 

The bit about history does concern me, though, as it's using a bit of a... Strange naming convention. Originally I thought that StateObjectReference was literally just a memory pointer, what C++ would refer to as a *name to use in lower-level OOP, but that doesn't seem to be it. In fact, the way you describe history in XCOM really reminds me of the Java AWT event dispatch thread in an odd way that I can't fully articulate. This whole system of allowing gamestates to be changed independently of their containing structures and then "updated" upon need is an almost exact fit for a presentation I had to give on multi-threaded programming back in University. Thinking of History as an event dispatch thread managing potentially asynchronous events and ensuring consistent behaviour through on-demand updates vs. content locks actually gives me more of a footing of what it actually is. Sadly, I know comparatively little of race condition handling via events, but that's fixable - it's something I can read up on, and should on account of it being part of my real-life job. Just because it hasn't come up yet doesn't mean it won't, especially with UI design via Java Swing. But I think I follow what you mean up to a point.

 

I get why they use of XComGameState_BaseObject is crucial. It's the root of the gamestate tree, so it's safest to always expect that out of all of the basic actions, meaning you can always cast "up" to the actual object you're expecting to get. It seems to me that UnrealScript doesn't throw a lot of exceptions, so a failed cast will likely just produce a null pointer or some other such item which doesn't require the kind of try/catch block that I'm not convinced UnrealScript even supports (Google says "no"), so I can kind of wrap my head around it. I know my back-explanations probably don't make a lot of sense to you, but they do - for the first time - make sense to me, which is actually a pretty nice feeling :smile: I'm not entirely sure where StateObjectReference comes into play if it's not a pointer address value, though...

 

However, this still leaves the question of my experience with replacing abilities in the main menu, though. It plain and simple doesn't work correctly, but it works half-way. I was able to get the ability to change temporarily (and corrupted its icon, among probably other things), but it would eventually reset itself. If gamestates get serialised into a save game file, then how does altering an ability template at game startup alter the ability's gamestate in a tactical game in progress? My Blademaster Enhancement mod uses a dirt simple method of "code injection" by creating a child of X2Ability_RangerAbilities with one method overridden, yet that's nevertheless enough to alter Blademaster in a tactical game already in progress. Previously, I was attempting to pull an ability template out of the ability manager, replace it with a new one and put replace the old one with the new one in the ability manager itself. This worked up to a point, but I was clearly not doing something correctly because the event became corrupted during the strategic layer and then failed to have a gamestate for it created ever again. But the mere fact that simply adding a new ability template with the same name as an old one into the ability manager is able to alter an ability's behaviour in a previously-saved tactical game is very odd to me, given what I think I understand about gamestates.

 

And this also brings up the other question of exactly what I'm doing with this approach. I know that creating a child of X2Ability_RangerAbilities will get that class picked up by the executable and its CreateTemplates method called, but all that does is put new abilities in the ability manager's array, near as I can tell. Does my class execute after the original class? Do my ability templates replace those of the original class, or do they get added as "doubles" in the list? Am I recreating ALL of the Ranger ability templates? That would suck, since it would be unfortunate for compatibility... Although I could also override the CreateTemplates function to only create the one I want. I'm able to override Blademaster pretty easily using this method, but I'm still confused as to exactly WHY this works and what it's doing that I'm not aware of. Because it really is dirt simple class extension and overwriting one solitary function.

Edited by SteelRook
Link to comment
Share on other sites

  • Recently Browsing   0 members

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