Spawn Points - XCOM:EU 2012
Contents
Overview
The following is derived from the Nexus Forums threads Modding EXALT and R&D XCOM Map Alterations.
Spawn points (objects of XComSpawnPoint class) are used to mark positions of XCOM soldiers, civilians and dynamic aliens on map. (See the wiki article Maps - XCOM:EU 2012 for more about maps in general.) XCOM soldiers and civilians use base XComSpawnPoint class and aliens use XComSpawnPoint_Alien class.
XComSpawnPoint_Alien inherits from XComSpawnPoint and is used specifically for dynamic alien spawns (See the wiki article Units deployment - XCOM:EU 2012 for more about the Wave System that populates these spawn points.)
Spawn points contain (mostly) the location of a specific point in space on the current map. (See #TheWorld.PersistentLevel.XComSpawnPoint_Alien_40 in the Separate Content section.)
Programs and Tools
- Modding Tools - XCOM:EU 2012
- UPKDecompressor
- UPKUtils
Details
Spawn Points
Technically a single spawn point is a single tile position, and so is only valid for a single unit. However, pawns are placed in a 3x3 grid centered on the defined map spawn point. In general usage the six spawn points for XCOM Soldiers (albeit multiple sets of six for the multiple possible PlayerStart positions) have come to be referred to as if a single 'spawn point' circle, with the "circle" implied and the term used for both XCOM soldiers and aliens.
XComAlienPodManager creates additional spawn points in the 3x3 grid centered on the defined map spawn point, and then places the alien pawns around the newly created spawn point. For 2 aliens they are at 180º ends, while for 3 aliens they are at 120º. Hence things can start to get a bit crowded once more than 8 aliens are getting spawned. Experiments with placing the first alien at the center point and the remaining aliens around the center point have allowed up to 9 aliens to be placed, completely filling out the 3x3 grid.
There is a bunch of code present to try and place aliens in "good" locations, including moving the point up the Z-axis and then snapping it to the floor. However attempts to array aliens in a larger circle radius extending beyond the 3x3 grid started to have problems. (In theory this should be okay, though.)
Aliens are spawned from the definitions in the strategy game (e.g. XGStrategyAI.DetermineAbductionSquad or XGStrategyAI.DetermineSpecialMissionSquad) are created in XComAlienPodManager.OvermindSpawn. However this doesn't appear to be how dynamic alien stats are initialized, which makes it more difficult to "beef up" the alien stats for things like the final Temple Ship mission.
The spawn point is retrieved separately for each alien placed :
kSpawnPt = kPod.GetSpawnPoint(iNum, vLoc, true);
This function is in XComAlienPod.
XComAlienPod.GetSpawnPoint has a couple of branches. If the third (optional) parameter is left blank or is false, then the code uses CustomStartLocations (but only if the array has non-zero length). Code such as :
vLoc_Out = XComTacticalGRI(WorldInfo.GRI).GetClosestValidLocation(vLoc_Out, none,, false);
is used liberally to try and ensure a valid spawn location.
If the 3rd parameter is true, then the "circle method" of generating additional spawn points around the center point is used:
if(iAlien < NumAliens) { vLoc_Out = GetDistributedLocationAround(Location, iAlien, Rotation, true); kSpawnPoint = Spawn(class'XComSpawnPoint_Alien',,, vLoc_Out, rotator(Location - vLoc_Out),, true); return kSpawnPoint; }
The GetDistributedLocationAround function selects a position in an adjacent tile (in the 3x3 grid surrounding the given spawn point location), and the next line spawns a new XComSpawnPoint_Alien at the designated location, and returns that spawn point to place the alien into.
A similar method was modded to dynamically create additional generic spawn points near the existing six soldier spawn points in order to handle spawning more than six soldiers (without having to alter the mapfile).
Actually, a map package doesn't provide the info on individual spawn points. It provides only XComAlienPod info, which holds default properties on the whole "Spawn" and location is just one of those. XComAlienPod map objects serve as "templates" for creating pods of a specified type at a specified location. They deal with the pod as a whole.
There are NumAliens_Min and NumAliens_Max properties. They are used in PodManager to create test pods for debug purposes.
Council Missions and EXALT Missions use different dynamic aliens spawn methods. All EXALT maps, except HQ, use the XComWaveSystem to add dynamic aliens in waves. Some of the alien stats are defined in map objects, and alien types are chosen randomly from the list of available aliens in the current squad. (See the wiki article Units deployment - XCOM:EU 2012. The XCOM HQ Assault map uses the XComWaveSystem for alien waves deployment too, and it is BIG.
Council Missions and the Temple Ship mission use triggers (zones on map or global events) to spawn a group of aliens. For example, there are a couple of SeqAct_AlienContent_Request and SeqAct_SpawnAlien objects in MainSequence of the Temple Ship map package, in which alien type and some other properties are defined.
XComAlienPod is a base class for all alien pods. It has three child classes: XComAlienPod_Abduction, XComAlienPod_Hunter and XComAlienPod_Terror.
It looks like XComAlienPod can either contain a single Location (in which case all of the aliens in the pod are distributed around that location);
OR
The XComAlienPod can contain a CustomStartLocations array, in which case the alien is placed at CustomStartLocations[iAlien].Location. The CustomStartLocations array is prototyped as a class variable in XComAlienPod as:
var() array<XComSpawnPoint> CustomStartLocations;
which is indeed a dynamic array of XComSpawnPoint's. (The "()" after the var tag that UE Explorer is displaying means it can be edited directly in Unreal Editor.)
XComAlienPod is ... a "Pod". It literally is, in that pods are already placed on maps via XComAlienPod_X archetypes. They are not only locations; although in most cases only location info is used.
Example: (See #XComAlienPod'TheWorld.PersistentLevel.XComAlienPod_2' in Separate Content section.)
Pre-placed pods usually describe some generic pod, so the actual pod is spawned from kAlienSquad array. But you can set all the details inside a map object, i.e. embed a pod in a map directly. You can set pod type, pod main alien ... all the info which is settable in a script, as every class property can be initialized in the archetype.
XComAlienPod map info is used in XComAlienPodManager in functions:
- LoadInit
- InitPods
- SelectSpecialtyPods
- GenerateSpawnList
- DebugLogAllPods
- HasPodsInGroup
- and in XGAIPlayer_Animal (for civilians): GetGoodCoverPoints_Civilian function.
The Wave System
The following is derived from investigations in the Nexus Mod Talk Forum thread Modding Exalt, by wghost81.
XGOvermind.Init function initializes all map spawns and contains InitWaveSystem call (along with DeployPods call).
InitWaveSystem requests WorldInfo on AllActors of class XComWaveSystem and calls XComWaveSystem.Init function. This function checks if wave is valid and calls InitScalableList function.
InitScalableList iterates through all Pods in AlienSquad and calls AddCharTypeToScalableList for eMain, eSupport1 and eSupport2 types.
AddCharTypeToScalableList sorts aliens by type and adds them to three lists:
- m_arrScalableList contains all the types available,
- m_arrScalableFlyInList contains only flying units,
- m_arrScalableDropInList contains only character with HasTraversal property (i.e. those, who can climb ladders).
When comes the time for another wave, SpawnGroup function is called, which in turn calls to SetScalingAliens function.
If alien group is Scalable (which is set in properties of alien wave), SetScalingAliens initializes AlienType with random alien from one of the Scalable Groups, described above. If group is not scalable, it forces ExaltOperatives for Exalt maps and ThinMan for the other maps.
In DefaultMaps.ini AlienType is defined explicitly for Council Missions via DynamicAliens array, but not defined (eChar_None) for Exalt maps. I think it's done to allow for two different Exalt types (regular and elite) for the same maps.
Interesting note: if we'll try and remove all forced ThinMan for Council Missions, we'll be able to change dynamic aliens by altering SetScalingAliens function.
See also the wiki article Units deployment - XCOM:EU 2012.
Separate Content
TheWorld.PersistentLevel.XComSpawnPoint_Alien_40
FindObjectEntry Name to find: TheWorld.PersistentLevel.XComSpawnPoint_Alien_40 Found Export Object: 0x000001D6 (470): XComSpawnPoint_Alien'TheWorld.PersistentLevel.XComSpawnPoint_Alien_40' TypeRef: 0xFFFFFFB0 -> XComSpawnPoint_Alien ParentClassRef: 0x00000000 -> OwnerRef: 0x0000000C -> PersistentLevel NameIdx: 0x000001EC (Index) 0x00000029 (Numeric) -> XComSpawnPoint_Alien_40 ArchetypeRef: 0x00000000 -> ObjectFlagsH: 0x00000000 ObjectFlagsL: 0x02070001 0x00000001: Transactional 0x00010000: LoadForClient 0x00020000: LoadForServer 0x00040000: LoadForEdit 0x02000000: HasStack SerialSize: 0x000000B3 (179) SerialOffset: 0x00059B94 ExportFlags: 0x00000000 NetObjectCount: 0 GUID: 00000000000000000000000000000000 Unknown1: 0x00000000 Attempting deserialization: UObject: PrevObjRef = 0xFFFFFFB0 -> XComSpawnPoint_Alien UDefaultPropertiesList: UDefaultProperty: NameIdx: 0x000000D1 (Index) 0x00000000 (Numeric) -> Location TypeIdx: 0x000001A8 (Index) 0x00000000 (Numeric) -> StructProperty PropertySize: 0x0000000C ArrayIdx: 0x00000000 InnerNameIdx: 0x000001D0 (Index) 0x00000000 (Numeric) -> Vector Vector (X, Y, Z) = (0x441BFFFD, 0x4536FFFF, 0x4203FFF8) = (624, 2928, 33) UDefaultProperty: NameIdx: 0x00000156 (Index) 0x00000000 (Numeric) -> Rotation TypeIdx: 0x000001A8 (Index) 0x00000000 (Numeric) -> StructProperty PropertySize: 0x0000000C ArrayIdx: 0x00000000 InnerNameIdx: 0x00000157 (Index) 0x00000000 (Numeric) -> Rotator Rotator (Pitch, Yaw, Roll) = (0x00000000, 0xFFFFC000, 0x00000000) = (0, 4294950912, 0) UDefaultProperty: NameIdx: 0x00000018 (Index) 0x00000000 (Numeric) -> bDirtyComponents TypeIdx: 0x0000002B (Index) 0x00000000 (Numeric) -> BoolProperty PropertySize: 0x00000000 ArrayIdx: 0x00000000 Boolean value: 0x00 = false UDefaultProperty: NameIdx: 0x000001AA (Index) 0x00000000 (Numeric) -> Tag TypeIdx: 0x000000F8 (Index) 0x00000000 (Numeric) -> NameProperty PropertySize: 0x00000008 ArrayIdx: 0x00000000 Name: 0x000001EB (Index) 0x00000000 (Numeric) = XComSpawnPoint UDefaultProperty: NameIdx: 0x000000FD (Index) 0x00000000 (Numeric) -> None Stream relative position: 0x000000B3 (179) UObjectUnknown: Object unknown, can't deserialize!
XComAlienPod'TheWorld.PersistentLevel.XComAlienPod_2'
0x000037AF (14255): XComAlienPod'TheWorld.PersistentLevel.XComAlienPod_2' TypeRef: 0xFFFFFF40 -> XComAlienPod ParentClassRef: 0x00000000 -> OwnerRef: 0x00000770 -> PersistentLevel NameIdx: 0x00000E74 (Index) 0x00000003 (Numeric) -> XComAlienPod_2 ArchetypeRef: 0x00000000 -> ObjectFlagsH: 0x00000000 ObjectFlagsL: 0x02070001 0x00000001: Transactional 0x00010000: LoadForClient 0x00020000: LoadForServer 0x00040000: LoadForEdit 0x02000000: HasStack SerialSize: 0x00000166 (358) SerialOffset: 0x01D12B90 ExportFlags: 0x00000000 NetObjectCount: 0 GUID: 00000000000000000000000000000000 Unknown1: 0x00000000 UObject: PrevObjRef = 0xFFFFFF40 -> XComAlienPod Can't deserialize stack: skipping! UDefaultPropertiesList: UDefaultProperty: NameIdx: 0x000009F8 (Index) 0x00000000 (Numeric) -> PodIndex TypeIdx: 0x000006BA (Index) 0x00000000 (Numeric) -> IntProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Integer: 0x00000002 = 2 UDefaultProperty: NameIdx: 0x0000073A (Index) 0x00000000 (Numeric) -> LoopType TypeIdx: 0x000002D7 (Index) 0x00000000 (Numeric) -> ByteProperty PropertySize: 0x00000008 ArrayIdx: 0x00000000 InnerNameIdx: 0x000004F7 (Index) 0x00000000 (Numeric) -> EPathLoopType Name: 0x000009BD (Index) 0x00000000 (Numeric) = PATH_TYPE_FORWARD_BACKWARD_LOOP UDefaultProperty: NameIdx: 0x000006FC (Index) 0x00000000 (Numeric) -> LightEnvironment TypeIdx: 0x000008FC (Index) 0x00000000 (Numeric) -> ObjectProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Object: 0x000003D6 = DynamicLightEnvironmentComponent_3538 UDefaultProperty: NameIdx: 0x000008F2 (Index) 0x00000000 (Numeric) -> NumAliens_Min TypeIdx: 0x000006BA (Index) 0x00000000 (Numeric) -> IntProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Integer: 0x00000003 = 3 UDefaultProperty: NameIdx: 0x000008F1 (Index) 0x00000000 (Numeric) -> NumAliens_Max TypeIdx: 0x000006BA (Index) 0x00000000 (Numeric) -> IntProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Integer: 0x00000003 = 3 UDefaultProperty: NameIdx: 0x000009F9 (Index) 0x00000000 (Numeric) -> PodMesh TypeIdx: 0x000008FC (Index) 0x00000000 (Numeric) -> ObjectProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Object: 0x00002FDD = StaticMeshComponent_371 UDefaultProperty: NameIdx: 0x000009BF (Index) 0x00000000 (Numeric) -> PathNodes TypeIdx: 0x0000017F (Index) 0x00000000 (Numeric) -> ArrayProperty PropertySize: 0x00000010 ArrayIdx: 0x00000000 NumElements = 0x00000003 = 3 ArrayInnerType = None PathNodes[0]: Unsafe guess: It's an Integer: 0x00003799 = 14233 or a Reference: 0x00003799 -> XComAlienPathNode_0 PathNodes[1]: Unsafe guess: It's an Integer: 0x000037A5 = 14245 or a Reference: 0x000037A5 -> XComAlienPathNode_2 PathNodes[2]: Unsafe guess: It's an Integer: 0x0000379A = 14234 or a Reference: 0x0000379A -> XComAlienPathNode_1 UDefaultProperty: NameIdx: 0x00000309 (Index) 0x00000000 (Numeric) -> CenterpieceMesh TypeIdx: 0x000008FC (Index) 0x00000000 (Numeric) -> ObjectProperty PropertySize: 0x00000004 ArrayIdx: 0x00000000 Object: 0x00002FDE = StaticMeshComponent_372 UDefaultProperty: NameIdx: 0x0000071A (Index) 0x00000000 (Numeric) -> Location TypeIdx: 0x00000C54 (Index) 0x00000000 (Numeric) -> StructProperty PropertySize: 0x0000000C ArrayIdx: 0x00000000 InnerNameIdx: 0x00000E19 (Index) 0x00000000 (Numeric) -> Vector Vector (X, Y, Z) = (0xC4164382, 0xC3D60B27, 0x43420622) = (-601.055, -428.087, 194.024) UDefaultProperty: NameIdx: 0x00000D1B (Index) 0x00000000 (Numeric) -> Tag TypeIdx: 0x000008D1 (Index) 0x00000000 (Numeric) -> NameProperty PropertySize: 0x00000008 ArrayIdx: 0x00000000 Name: 0x00000E74 (Index) 0x00000000 (Numeric) = XComAlienPod UDefaultProperty: NameIdx: 0x000008EA (Index) 0x00000000 (Numeric) -> None Stream relative position (debug info): 0x00000166 (358)
References
Referred to by this article:
That refer to this article: