LeaderEnemyBoss Posted May 7, 2016 Share Posted May 7, 2016 (edited) Hi everyone, I am trying to add my enemy to the final mission without using the XcomMissions.ini (because of mod compatibility). At the moment i got a mostly working solution that uses the a tacticalhud screenlistener. However there is a minor and a major issue: The minor issue: The enemy obviously does not show up in the shadow chamber information window on the strategy map.The major issue: When the player issues a restart of the mission, the game crashes after spawning my units. This also happens after loading an ongoing mission , however i dont want the enemy to spawn after loading a game anyway, so its no big issue in this case. my code basically looks like this: event OnInit(UIScreen Screen) { [huge pile of irrelevant code] `SPAWNMGR.CreateUnit(DesiredSpawnVector, [unitname] , eTeam_Alien, false ,,,,); } As I said, this works perfectly fine when I start the mission (doesnt matter if actual campaign or tql), but using the restart feature/console command crashes the game. There is also no useful information in the launchlog, the only lines that are different from a normal mission start are immediatly before the game shuts down: [0276.01] Log: Dumping name table: (2055384)[0276.01] Log: Dumping name table: 0x2b924000 (2055384) Anyway, maybe someone here has a better idea, how to insert new units into missions. Edited May 7, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
LeaderEnemyBoss Posted May 7, 2016 Author Share Posted May 7, 2016 (edited) Looks like i found a good way of doing this myself! This may help some modders, especially the ones that want to add new enemies via dark events. It should be perfectly compatible with other mods, since it just takes the already cached mission data, and adds encounters to it. This is probably a good way to circumvent compatibility problems that arise when adding encounters to mission schedules in the XComMissions.ini.The way i do it now is add the new enemies after the caching of the mission is completed. This always happens as soon as one selects a mission on the worldmap with the shadow chamber built. Since i only add enemies to the final mission, the shadow chamber is always present anyway. If you want to use this method and add enemies to missions where no shadow chamber may have been built yet, you need to use the CacheSelectedMissionData function of XComGameState_MissionSite before you start! My code is probably not very good but it does the job. If you want to use it for another usecase, you probably have to rewrite some stuff. If you need help, just ask :wink:. //This class adds one or more Riftkeepers to the two final rooms of the last mission class FortressMissionSite_Listener_Riftkeeper extends UIScreenListener config (Riftkeeper); var config int RKinEndBattle, RKinMidBoss; event OnInit(UIScreen Screen) { local XComGameState_MissionSite Mission; local ConfigurableEncounter Encounter; local X2SelectedEncounterData NewEncounter, EmptyEncounter; local array<X2CharacterTemplate> SelectedCharacterTemplates; local int LeaderForceLevelMod, i; local array<X2CharacterTemplate> TemplatesToSpawn; local X2CharacterTemplate TemplateToSpawn; local XComTacticalMissionManager TacticalMissionManager; local float AlienLeaderWeight, AlienFollowerWeight; local XComAISpawnManager SpawnManager; local XComGameStateHistory History; local XComGameState_HeadquartersAlien AlienHQ; local name RKtoSpawn; local bool RKadded; if(UIMission(Screen) != none) { Mission = UIMission(Screen).GetMission(); if (Mission.Source == 'MissionSource_Final') //check if the selected mission is the final mission { `log("Final Mission, trying to add Riftkeeper!"); History = `XCOMHISTORY; AlienHQ = XComGameState_HeadquartersAlien(History.GetSingleGameStateObjectForClass(class'XComGameState_HeadquartersAlien')); RKadded = false; //check if the Riftkeeper was already added TemplatesToSpawn.Length = 0; GetEnemies(Screen, TemplatesToSpawn); foreach TemplatesToSpawn(TemplateToSpawn) { if (InStr(TemplateToSpawn.strCharacterName, "Riftkeeper") > -1) { `log("Riftkeeper already added!"); RKadded = true; break; } } //if the riftkeeper wasnt added yet, and the forcelevel is high enough, add it! if (!RKadded && AlienHQ.GetForceLevel()>16) { //choose the weaker or stronger variant, depending on forcelevel if (AlienHQ.GetForceLevel()>16) { RKtoSpawn = 'LonelyRK'; } if (AlienHQ.GetForceLevel()>18) { RKtoSpawn = 'LonelyRK2'; } TacticalMissionManager = `TACTICALMISSIONMGR; TacticalMissionManager.GetConfigurableEncounter(RKtoSpawn, Encounter); //final room encounter for ( i=0; i<RKinEndBattle; i++) { NewEncounter = EmptyEncounter; NewEncounter.SelectedEncounterName = RKtoSpawn; //Name of the predefined encounter (XComMissions.ini) //Since i only want to spawn a riftkeeper, spawnweights are irrelevant for me, so iI just assign a random value AlienLeaderWeight = 1.00f; AlienFollowerWeight = 1.00f; SpawnManager = `SPAWNMGR; LeaderForceLevelMod = SpawnManager.GetLeaderForceLevelMod(); //I set the Forcelevel to 20 because I always want my Riftkeeper to spawn SpawnManager.SelectSpawnGroup(NewEncounter.EncounterSpawnInfo, Mission.GeneratedMission.Mission, Encounter, 20, Mission.GetMissionDifficulty(), SelectedCharacterTemplates, AlienLeaderWeight, AlienFollowerWeight, LeaderForceLevelMod); //location and patrol zone of the pod NewEncounter.EncounterSpawnInfo.EncounterZoneWidth = 20; NewEncounter.EncounterSpawnInfo.EncounterZoneOffsetAlongLOP = 5; Mission.SelectedMissionData.SelectedEncounters.AddItem(NewEncounter); } //last midboss encounter for ( i=0; i<RKinMidBoss; i++) { NewEncounter = EmptyEncounter; NewEncounter.SelectedEncounterName = RKtoSpawn; //Name of the predefined encounter (XComMissions.ini) //Since i only want to spawn a riftkeeper, spawnweights are irrelevant for me, so iI just assign a random value AlienLeaderWeight = 1.00f; AlienFollowerWeight = 1.00f; SpawnManager = `SPAWNMGR; LeaderForceLevelMod = SpawnManager.GetLeaderForceLevelMod(); //I set the Forcelevel to 20 because I always want my Riftkeeper to spawn SpawnManager.SelectSpawnGroup(NewEncounter.EncounterSpawnInfo, Mission.GeneratedMission.Mission, Encounter, 20, Mission.GetMissionDifficulty(), SelectedCharacterTemplates, AlienLeaderWeight, AlienFollowerWeight, LeaderForceLevelMod); //location and patrol zone of the pod NewEncounter.EncounterSpawnInfo.EncounterZoneWidth = 20; NewEncounter.EncounterSpawnInfo.EncounterZoneOffsetAlongLOP = 38; Mission.SelectedMissionData.SelectedEncounters.AddItem(NewEncounter); } } //Add the Riftkeeper to the shadow chamber string UpdateShadowStrings(Screen, Mission); } //some logging, nothing important happening here `log("schedule:" @Mission.SelectedMissionData.SelectedMissionScheduleName); TemplatesToSpawn.Length = 0; GetEnemies(Screen, TemplatesToSpawn); foreach TemplatesToSpawn(TemplateToSpawn) { `log("enemies:" @TemplateToSpawn.strCharacterName); } } } function UpdateShadowStrings(UIScreen Screen, XComGameState_MissionSite Mission) { local string Crewstring, Crewcount; local int NumUnits; local array<X2CharacterTemplate> TemplatesToSpawn; local X2CharacterTemplate TemplateToSpawn; local XComGameState_HeadquartersXCom XComHQ; XComHQ = XComGameState_HeadquartersXCom(`XCOMHISTORY.GetSingleGameStateObjectForClass(class'XComGameState_HeadquartersXCom')); TemplatesToSpawn.Length = 0; Mission.GetShadowChamberMissionInfo(NumUnits, TemplatesToSpawn); Crewcount = string(NumUnits); Crewstring = ""; foreach TemplatesToSpawn(TemplateToSpawn) { if( TemplateToSpawn.bIsCivilian ) { continue; } if(Crewstring != "") { Crewstring = Crewstring $ ", "; } if(XComHQ.HasSeenCharacterTemplate(TemplateToSpawn)) { Crewstring = Crewstring $ TemplateToSpawn.strCharacterName; } else { Crewstring = Crewstring $ Class'UIUtilities_Text'.static.GetColoredText(Mission.m_strEnemyUnknown, eUIState_Bad); } } UIMission(Screen).ShadowChamber.MC.BeginFunctionOp("UpdateShadowChamber"); UIMission(Screen).ShadowChamber.MC.QueueString(UIMission(Screen).m_strShadowChamberTitle); UIMission(Screen).ShadowChamber.MC.QueueString(UIMission(Screen).m_strEnemiesDetected); UIMission(Screen).ShadowChamber.MC.QueueString(Crewstring); UIMission(Screen).ShadowChamber.MC.QueueString(Crewcount); UIMission(Screen).ShadowChamber.MC.EndOp(); } function GetEnemies(UIScreen Screen, out array<X2CharacterTemplate> UnitTemplatesThatWillSpawn) { local int EncounterIndex, CharacterIndex; local X2CharacterTemplate SelectedTemplate; local Name CharTemplateName; local X2CharacterTemplateManager CharTemplateManager; CharTemplateManager = class'X2CharacterTemplateManager'.static.GetCharacterTemplateManager(); UnitTemplatesThatWillSpawn.Length = 0; `log("enclength" @UIMission(Screen).GetMission().SelectedMissionData.SelectedEncounters.Length); for(EncounterIndex = 0; EncounterIndex < UIMission(Screen).GetMission().SelectedMissionData.SelectedEncounters.Length; ++EncounterIndex ) { for( CharacterIndex = 0; CharacterIndex < UIMission(Screen).GetMission().SelectedMissionData.SelectedEncounters[EncounterIndex].EncounterSpawnInfo.SelectedCharacterTemplateNames.Length; ++CharacterIndex ) { CharTemplateName = UIMission(Screen).GetMission().SelectedMissionData.SelectedEncounters[EncounterIndex].EncounterSpawnInfo.SelectedCharacterTemplateNames[CharacterIndex]; SelectedTemplate = CharTemplateManager.FindCharacterTemplate(CharTemplateName); UnitTemplatesThatWillSpawn.AddItem(SelectedTemplate); } } } defaultproperties { ScreenClass = none; } EDIT: Improved the shadow chamber implementation to ensure compatibility with mods that do the same thing. Edited May 7, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
TeamDragonpunk Posted May 7, 2016 Share Posted May 7, 2016 Sorry you had to work on this by yourself :( Would you mind adding some in-line documentation though, so other modders can understand what you're doing exactly. You're iterating through the templates to spawn and then... I lost you on the colored text. Link to comment Share on other sites More sharing options...
LeaderEnemyBoss Posted May 7, 2016 Author Share Posted May 7, 2016 (edited) Sorry you had to work on this by yourself :sad: Would you mind adding some in-line documentation though, so other modders can understand what you're doing exactly. You're iterating through the templates to spawn and then... I lost you on the colored text. Sorry, i don't really know how to fix the messy colors. The forums codeviewer recognizes the ` - Character as comment apparently. Basically at first I check if the selected mission is the final mission, and if there are already riftkeepers present (the enemy i want to add). The important code for adding encounters is basically this: TacticalMissionManager = `TACTICALMISSIONMGR; TacticalMissionManager.GetConfigurableEncounter(RKtoSpawn, Encounter); NewEncounter = EmptyEncounter; NewEncounter.SelectedEncounterName = RKtoSpawn; //Name of the predefined encounter (XComMissions.ini) //Since i only want to spawn a riftkeeper, spawnweights are irrelevant for me, so iI just assign a random value AlienLeaderWeight = 1.00f; AlienFollowerWeight = 1.00f; SpawnManager = `SPAWNMGR; LeaderForceLevelMod = SpawnManager.GetLeaderForceLevelMod(); //I set the Forcelevel to 20 because I always want my Riftkeeper to spawn SpawnManager.SelectSpawnGroup(NewEncounter.EncounterSpawnInfo, Mission.GeneratedMission.Mission, Encounter, 20, Mission.GetMissionDifficulty(), SelectedCharacterTemplates, AlienLeaderWeight, AlienFollowerWeight, LeaderForceLevelMod); //location and patrol zone of the pod NewEncounter.EncounterSpawnInfo.EncounterZoneWidth = 20; NewEncounter.EncounterSpawnInfo.EncounterZoneOffsetAlongLOP = 5; Mission.SelectedMissionData.SelectedEncounters.AddItem(NewEncounter); Its mostly a copy of the code found in the CacheSelectedMissionData function of XComGameState_MissionSite. I think it may also be possible to add single enemies to already existing encounters/pods. After im finished with adding the encounters, i update the shadow chamber string so they get displayed in the corresponding window. Edited May 7, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
Recommended Posts