Jump to content

TUTORIAL : Using GameState Components


Amineri

Recommended Posts

Amineri - Sorry to keep coming back to this, but am I able to recover saved games for users who have these un-parented state objects hanging around? I had thought that if they loaded with the mod disabled, the state objects wouldn't be able to be deserialized and included in the history, and moving forward from there they would be fine. Is this not the case, by which I mean to ask do the serialized objects also hang around in the save file going forward despite being un-readable or something, and I will have to find a more aggressive solution to purging the history?

Link to comment
Share on other sites

Amineri - Sorry to keep coming back to this, but am I able to recover saved games for users who have these un-parented state objects hanging around? I had thought that if they loaded with the mod disabled, the state objects wouldn't be able to be deserialized and included in the history, and moving forward from there they would be fine. Is this not the case, by which I mean to ask do the serialized objects also hang around in the save file going forward despite being un-readable or something, and I will have to find a more aggressive solution to purging the history?

 

In my experience this is the case. Users have reported, and I have confirmed, that disabling the Officer mod prior to ending a Tactical mission which would have triggered the CTD prevents it from occurring. Alternatively, adding the gamestate component to the transient gamestate list also prevents the CTD. The downside is that it (in my case) interferes with normal operation of the mod.

 

When I tried to search the History for the offending objects, I was unable to find them. I suspect this is because they are components of a gamestate that has been obliterated, and the IterateByClass native accessor I used couldn't handle that. When I used the same IterateByClass method to search just after dismissing a unit (when the unit has not been obliterated, only had its bRemoved flag set), I was able to find the component gamestate, mark it for removal, and unlink it.

 

In short, I haven't figured out a way to recover a tactical save that is about to break, short of draconian measure that involve breaking/uninstalling the mod.

Link to comment
Share on other sites

So in theory (regarding just the effect gamestates in my mod) the disable/transition/enable should purge the gamestates and leave the campaign good to go forward with the fixed mod loaded?

 

Also in theory, including in XComGame.ini:

 

+TransientTacticalClassNames=XComGameState_Effect_ZoneOfControl
; etc
; etc
; etc
should purge the gamestates on a tactical -> strategy transition with the mod loaded and leave it good to go forward? (Is there any header needed there?)
Edited by Lucubration
Link to comment
Share on other sites

 

Thanks for the tutorial. I actually have been playing around with this already and have a component GameState added to the Unit GameState much like you describe here. One thing that I think it might also be nice to go over is how to officially add/update these GameStates from the History object-- as that is something I am still not totally clear on.

 

There seem to be a number of locations where you can 'submit' a GameState and the rules for which GameStates (pre-existing, modified, components) you need to add is also a little vague.

 

For example, in the Officer mod UIArmor_LWOfficerPromotion script you use `GAMERULES.SubmitGameState. However you also seem to be able to do this via `TACTICALRULES and `HISTORY.AddGameStateToHistory. For my mod, the only one of these that ever seemed to work was `HISTORY.AddGameStateToHistory

 

I don't suppose you could go into a bit more detail as to the differences of these techniques?

 

Thanks!

 

Unfortunately, I'm also a little unclear on when each of the macros applies, and when it doesn't. I copy/paste a lot of Firaxis code, and there are a variety of methods for accessing History for submitting gamestates.

 

`TACTICALRULES.SubmitGameState really only seems to work when in a tactical mission, which makes sense give the name.

`GAMERULES.SubmitGameState seems to work in almost every situation, except....

 

when adding things during a OnLoadedSave in X2DownloadableContentInfo, neither of these macros work, and the only way I found to submit (copied from the ExampleWeapon) is using :

`XCOMHISTORY.AddGameStateToHistory

 

 

First off - thanks for this write-up Amineri! This is a good overview of the component system for game state objects. Regarding when to use the rule sets and when not to when submitting game state changes...

 

The rule sets provide special handling that are specific to XCOM 2's game rules - but are only active when the game world has come up. Specifically the rule sets include triggers for the event managers, rule set observers, and logic for processing the in-game concept of interrupts. So:

 

If you are submitting game states that care about the rules of the game, use `GAMERULES. You shouldn't need to use the tactical variant of this macro unless you need something specifically from the tactical rules class.

 

If the change you are making is technically outside the rules of the game, then that is where you might just directly submit changes to the history ( `XCOMHISTORY.AddGameStateToHistory ). In the use case above when responding to the load save event you are doctoring the history with whatever you want prior to entering the saved game world. Even if you wanted to use the game rules here, it wouldn't be advisable since the rule set that is active would depend on what game mode you were in when the saved game was loaded. In the shell menu, there is no rule set so that is probably the most common use case.

 

The order of events for loading a save is:

 

1. The saved game data loaded into the history ( existing history is cleared ).

2. OnLoadedSave is called for any mods new to the save ( we're adding an additional event that triggers for *every* load not just the first ... )

3. UE3 level load process is initiated ( open map command ). This loads up the "plot" map for tactical games, and the avenger skeleton for strategy.

4. The level load creates all the supporting actors required by that game mode, including the rule set actor. These actors then organize streaming in all the maps that form the bulk of the procedural levels ( backgrounds for both game modes, parcels for tactical, rooms for strategy ).

5. Load movie is lifted when all the maps and processes are situated.

 

Hopefully that clears up when / where / how to submit new game states.

Link to comment
Share on other sites

  • 3 weeks later...
local X2_SkyrangerCustomSettings RangerHolder,newRangerHolder;
local XComGameState newGameState;
local XComGameStateContext_ChangeContainer changeContainer;
local XComGameStateHistory History;
local XComGameState_HeadquartersXCom XComHQ;

History = `XCOMHISTORY;
XComHQ = `XCOMHQ;

changeContainer = class'XComGameStateContext_ChangeContainer'.static.CreateEmptyChangeContainer("Adding Skyranger Settings"); 
newGameState = History.CreateNewGameState(true, changeContainer); 
newRangerHolder=new class'X2_SkyrangerCustomSettings'; 
newRangerHolder.InitComponent(); 
XComHQ.AddComponentObject(newRangerHolder); 
RangerHolder=X2_SkyrangerCustomSettings(XComHQ.FindComponentObject(class'X2_SkyrangerCustomSettings')); 
newGameState.AddStateObject(XComHQ); 
`log("Creating Holder" @ newRangerHolder @ RangerHolder); 
History.AddGameStateToHistory(newGameState);

I'm being stumped here. If I run this I get 'Creating Holder: X2_SkyrangerCustomSettings_0 none' in the logs. Either addcomponentobject isnt working, or findcomponent isnt, or XCOMHQ is a bad thing to attach info to. Ugh.

 

CaveRat to the rescue!

http://forums.nexusmods.com/index.php?/topic/3927540-what-am-i-doing-wrong-everything-setmaterial-finding-actors-and-other-woes/?p=36013545

Edited by Synthorange
Link to comment
Share on other sites

Question about using GetParentGameState. I've seen people use GetParent(*class*), but never for GameStates, presumably because GameStates are a more exclusive and delicate bunch.

 

Ideally, I have a situation that you mention; I need to access XComGameState_Unit data, so that I can temp save it and also change it. The Firaxis use of GetParentGameState had me extremely confused and I have no clue how to go about it. Most of them use it with GetContext, but researching GetContext added more to my confusion.

Link to comment
Share on other sites

GetParentGameState doesn't appear to be related to the component system. Instead, it's more involved with the XComGameStateHistory.

 

Because of how the XComGameStateHistory works, there is more than a single instance of XCGameState_Unit for a particular unit. In fact, there is (in general) one instance of it for each change to a particular unit. In this way it's much like a VersionControlSystem -- a history of past changes to each gamestate is maintained within the history. "Uniqueness" of an object is maintained via it's ObjectID. So there might be a bunch of copies of XComGameState_Unit floating around with the same ObjectID -- these are the same unit at different points in time during play. This is why things are accessed via pointer to XComGameState_BaseObjects -- because a pointer might reference an out-of-date on in the past. Instead, the ObjectID is used and the most recent version (typically) is retrieved from the History.

 

GetParentGameState is somewhat unfortunately named -- it's not a parent in the sense of "object oriented class inheritence", nor is it a parent in the sense of "owner of this component". Instead it's the previous version of the gamestate in the history -- a parent in the sense of time. This is why it's often used within contexts. Contexts are basically a sort of "series of planned gamestate changes". They aren't all entered at once to allow for interruptions.

 

A very typical instance of this is unit movement. Each movement to a different tile constitutes a change to the unit gamestate. So a unit that is dashing 12 tiles is going to get 12 gamestate updates (at least), one for each new tile position. The context wraps up all of these planned gamestate changes into a single container. After each move to a new tile, there is a MovementObserver that has a change to insert a new gamestate change into the sequence, possibly disrupting the planned series. A typical example of this is reaction fire, which can interrupt a move, resulting in the unit never making it to the planned final destination tile.

 

So the reason you see GetParentGameState getting frequently used in Contexts is that they are checking the current versus previous (in the history) to see what changed, in order to implement either some gameplay logic or visualization.

Link to comment
Share on other sites

I gotcha, though I'm still am unsure on how to access my own XComGameState_Unit data. Is it a bad practice to have the component, rather than a helper function, access the data? Also, because my component is completely reliant on XComGameState_Unit, should I extend XComGameState_Unit instead?

Link to comment
Share on other sites

  • Recently Browsing   0 members

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