kosmo111 Posted February 13, 2016 Share Posted February 13, 2016 So I want to add several new pieces of data to all soldiers that persist across saves and are accessible from my mod. It seemed the best place to do was was in a custom gamestate. I have tried to look through the LongWarStudios Officer Mod since I know Amineri does this exact thing there, but I have run into an issue. In the Officer mod, it looks like they overrode one of the facilities such that when a soldier was trained to be an officer, that was when the custom game state was added. In my situation I simply want it to be applied to all soldiers always. I thought about just making a UIScreenListener, but I do not know a good way to get access to a soldier list from this position. I also thought about making a TacticalHUD listener that registered for some common event based around a unit, and every time that event triggers check/add the gamestate accordingly-- but this seemed like a hacky solution. Does anyone have any thoughts on the best way to go about doing this? Thanks Link to comment Share on other sites More sharing options...
tracktwo Posted February 13, 2016 Share Posted February 13, 2016 It depends a little on what kind of data you want to store. Soldiers state is stored in XComGameState_Unit, which records all the info about a particular unit. This info is always persisted in the save, although some of it (e.g. the list of available abilities, current applied effects like burning, unconscious, overwatch, etc.) is stripped out at the end of a tactical mission cause it makes no sense to keep around post-mission. So: if what you want to do fits inside the model of one of the existing state kinds, like a new ability, you can model it like that. If it's something completely new, you'll need to invent a new game state class. That I haven't done, I've only worked on adding new abilities. But in general, the UI hooks are one of the main "entry points" into the game logic and are very flexible and helps keep your mod isolated to avoid conflicts with others. You can either perform your tasks directly in the ui listener (as I do for the evac all mod but will probably change to a different approach to fix a minor bug) or you can set up event handlers in the UI listener that will be called later when particular events occur. Even if you don't really have anything UI related to do, they at least let you set things up to do things later. Link to comment Share on other sites More sharing options...
kosmo111 Posted February 13, 2016 Author Share Posted February 13, 2016 Right, I essentially want to add a new XComGameState to the XComGameState_Unit for each soldier-- similar to how the LW Officer mod adds officer levels in its own gamestate (and sets up event listeners that persist across saves). I just dont know a good way to get access to all XComGameState_Unit objects for all soldiers. Link to comment Share on other sites More sharing options...
tracktwo Posted February 13, 2016 Share Posted February 13, 2016 (edited) The easiest way is to use the XComGameState_HeadquartersXCom object, which has your basic avenger game info. This has two relevant fields: Squad, which holds the IDs of the currently selected soldiers for a mission, and Crew, which has everyone on the avenger, including scientists and engineers. For my evac all mod I use the squad: XComHQ = class'UIUtilities_Strategy'.static.GetXComHQ(); for (i = 0; i < XComHQ.Squad.Length; ++i) { EnsureAbilityOnUnit(XComHQ.Squad[i], AbilityTemplate); } Where EnsureAbilityOnUnit gets the current game state for the unit and then constructs a new unit state with the evac all ability if they don't have it. UnitState = XComGameState_Unit(`XCOMHISTORY.GetGameStateForObjectId(UnitStateRef.ObjectID)); // Search for the evac all ability and if it's not there create and submit a new state that has it added to the unit's ability list. You can see the full source code for evac all if you download the mod. All of this is in the UIScreenListener_TacticalHUD_EvacAll class Edited February 13, 2016 by tracktwo Link to comment Share on other sites More sharing options...
Amineri Posted February 14, 2016 Share Posted February 14, 2016 If you just want it to be always created, probably the best place for your gamestate creation code is in X2DownloadableContentInfo (your extension of it). This will run exactly once when a new campaign is started, or the first time a save is loaded with the mod installed. However, it wouldn't handle adding your component gamestate to newly created units (new recruits generated each "month", or reward soldiers). You'll likely have to set up an event listener of some sort to handle new soldiers being added. The event handler has to it in some sort of gamestate, but I think you should be able to create a gamestate in your initialization code that isn't linked to anything -- only purpose is to receive the listener events and add your component gamestate to newly created units. Link to comment Share on other sites More sharing options...
davidlallen Posted February 14, 2016 Share Posted February 14, 2016 Related question, can you answer if you have a chance? I think it is an easier version of this thread. Several mods seem to have this bug. http://forums.nexusmods.com/index.php?/topic/3800435-making-downloadablecontentinfo-add-content-once-only/ Link to comment Share on other sites More sharing options...
tracktwo Posted February 14, 2016 Share Posted February 14, 2016 (edited) That's interesting. I haven't looked at this but it sounds like the comments and the visible code for OnLoadedSaveGame are wrong and that it triggers for every load of the game. That, or another mod, like maybe the one that lets you load saves with mods you no longer have is interfereing with this logic causing it to trigger on every load. The actual code that figures out which OnLoadedSaveGame functions to call is in native so we can't see it. But if that mod works by stripping the list of dlc associated with a save I could definitely see that causing the event to fire for every mod on every load. Either way, the solution would be to walk the entire game history in your OnLoadedSaveGame to look for *any* entry that you recognize. E.g. in the linked thread you'd look for a unit state with that particular name, and if you find it don't add a new one. That should work even if the unit later dies, cause the history should all still be there. Edited February 14, 2016 by tracktwo Link to comment Share on other sites More sharing options...
kosmo111 Posted February 15, 2016 Author Share Posted February 15, 2016 Thanks for the help so far everyone, its really put me on the right track. I still seem to be having an issue adding the modified GameState to the history -- I keep getting a RedScreen to that effect. I have the following code (mostly just test code ATM until I can get it all working properly) in a UIScreenListener listening to UITacticalHUD. event OnInit(UIScreen Screen) { local XComGameState_HeadquartersXCom HQ; local XComGameStateHistory History; local XComGameState UpdateState; local XComGameState_Unit Unit; local XComGameState_Unit UpdatedUnit; local XComGameStateContext_ChangeContainer ChangeContainer; local XComGameState_UnitStats UnitStats; local int i; History = `XCOMHISTORY; HQ = class'UIUtilities_Strategy'.static.GetXComHQ(true); if( HQ == none ) return; `log("=====ONINIT CALLED======="); for(i = 0; i < HQ.Crew.Length && i < 1; i++) { unit = XComGameState_Unit( `XCOMHISTORY.GetGameStateForObjectId(HQ.Crew[i].ObjectID) ); UnitStats = XComGameState_UnitStats(unit.FindComponentObject(class'XComGameState_UnitStats')); if( UnitStats == none ) { ChangeContainer = class'XComGameStateContext_ChangeContainer'.static.CreateEmptyChangeContainer("Adding UnitStats to Unit"); UpdateState = History.CreateNewGameState(true, ChangeContainer); UpdatedUnit = XComGameState_Unit(UpdateState.CreateStateObject(class'XComGameState_Unit', unit.ObjectID)); UnitStats = XComGameState_UnitStats(UpdateState.CreateStateObject(class'XComGameState_UnitStats')); UnitStats.InitComponent(unit.ObjectID); UpdatedUnit.AddComponentObject(UnitStats); UpdateState.AddStateObject(UpdatedUnit); Updatestate.AddStateObject(UnitStats); `XCOMGAME.GameRuleset.SubmitGameState(UpdateState); //History.CleanupPendingGameState(UpdateState); } } } I was following the LWOfficer code, however I also tried the slightly different code in the EvacAll mod (which doesn't explicitly create a ChangeContainer). Any insight would be appreciated! Link to comment Share on other sites More sharing options...
kosmo111 Posted February 15, 2016 Author Share Posted February 15, 2016 As usual, as soon as you ask you find the issue. I changed the way I was submitting the game state to: History.AddGameStateToHistory(UpdateState); I would however be interesting in hearing why this is working for me while most examples I find are using other ways to submit changes (`XCOMGAME, `GAMERULES). Is there anywhere to get more information about how to use GameStates and what exactly is wrong with my original code? Link to comment Share on other sites More sharing options...
tracktwo Posted February 15, 2016 Share Posted February 15, 2016 What exactly did the redscreen say? The only thing I can think of is that maybe you only needed to add the new unit state to the new state before submitting, cause the stats state is attached to the unit? That's just a guess though. I still don't know exactly the rules about what needs to be added where before submission, but fortunately "fiddle with it until it stops redscreening" seems to work until I internalize the rules. Link to comment Share on other sites More sharing options...
Recommended Posts