Jump to content

Is there an elegant way of adding enemies to missions without using the XComMissions.ini?


LeaderEnemyBoss

Recommended Posts

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 by LeaderEnemyBoss
Link to comment
Share on other sites

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 by LeaderEnemyBoss
Link to comment
Share on other sites

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 by LeaderEnemyBoss
Link to comment
Share on other sites

  • Recently Browsing   0 members

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