Jump to content

R&D Dynamic Pod Placement


wghost81

Recommended Posts

Here's working random meld placement code:

 

 

UPK_FILE=XComGame.upk

// convert XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints.arrEasyMeldSpawnPoints to integer
EXPORT_ENTRY=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints.arrEasyMeldSpawnPoints
OBJIDX=Core.IntProperty // new type
REL_OFFSET=8
OBJIDX=XGBattle_SP.InitMeldContainers // new owner
REL_OFFSET=12
NAMEIDX=A // new name
REL_OFFSET=32
UNSIGNED=40 // new serial size

// reset property flags for new var A
OBJECT=XGBattle_SP.InitMeldContainers.A
REL_OFFSET=20
UNSIGNED=0 // set PropertyFlagsL to zero

// convert XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints dynamic array to structure
EXPORT_ENTRY=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints
OBJIDX=Core.StructProperty // new type

// make arrEasyMeldSpawnPoints a vector
OBJECT=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints
REL_OFFSET=20
UNSIGNED=0 // set PropertyFlagsL to zero
REL_OFFSET=40
OBJIDX=Core.Object.Vector

// change inner type of arrHardMeldSpawnPoints array
OBJECT=XGBattle_SP.InitMeldContainers.arrHardMeldSpawnPoints.arrHardMeldSpawnPoints
REL_OFFSET=40
OBJIDX=XComSpawnPoint

// link new var A to XGBattle_SP.InitMeldContainers
OBJECT=XGBattle_SP.InitMeldContainers.kMeldActor_Arc
REL_OFFSET=12
OBJIDX=XGBattle_SP.InitMeldContainers.A // NextRef

OBJECT=XGBattle_SP.InitMeldContainers:AUTO

ALIAS=World:12 20 <Class.XComWorldData> 0A 00 <XComWorldData.GetWorldData.ReturnValue> 00 1B <GetWorldData> 16
ALIAS=numX:19 <!World> 09 00 <XComWorldData.NumX> 00 01 <XComWorldData.NumX>
ALIAS=numY:19 <!World> 09 00 <XComWorldData.NumY> 00 01 <XComWorldData.NumY>
ALIAS=numZ:19 <!World> 09 00 <XComWorldData.NumZ> 00 01 <XComWorldData.NumZ>
ALIAS=PlayerLocation:19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>

//ALIAS=DebugLog:19 2E <Class.XComOnlineEventMgr> 19 2E <Engine.GameEngine> 12 20 <Engine.Engine> 0A 00 <Engine.Engine.GetEngine.ReturnValue> 00 1C <Engine.Engine.GetEngine> 16 09 00 <Engine.GameEngine.OnlineEventManager> 00 01 <Engine.GameEngine.OnlineEventManager> FF 00 <NullRef> 00 1B <DevOnlineMsg>

[REPLACEMENT_CODE]
//kMeldActor_Arc = XComMeldContainerActor(DynamicLoadObject("MeldCanister_MOD.MeldCanister_ARC", class'XComMeldContainerActor', false))
0F 00 <.kMeldActor_Arc> 2E <Class.XComMeldContainerActor> 1C <Core.Object.DynamicLoadObject> 1F <%t "MeldCanister_MOD.MeldCanister_ARC"> 20 <Class.XComMeldContainerActor> 28 16

//arrHardMeldSpawnPoints = GetSpawnPoints(8) // get player start
0F 00 <.arrHardMeldSpawnPoints> 1B <GetSpawnPoints> 24 08 4A 16

/*
<!DebugLog> 70 1F <%t "player_location_x: "> 38 55 
35 <Core.Object.Vector.X> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
<!DebugLog> 70 1F <%t "player_location_y: "> 38 55 
35 <Core.Object.Vector.Y> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
<!DebugLog> 70 1F <%t "player_location_z: "> 38 55 
35 <Core.Object.Vector.Z> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
*/

//A = 0
0F 00 <.A> 2C 00

[#loop1]
07 [@loop1end] 96 00 <.A> 2C 02 16

//arrEasyMeldSpawnPoints = World.FindClosestValidLocation(World.GetPositionFromTileCoordinates(Rand(numX), Rand(numY), Rand(numZ)), false, false, false);
0F 00 <.arrEasyMeldSpawnPoints>
19
<!World> <%s140> <XComWorldData.FindClosestValidLocation.ReturnValue> 00 1B <FindClosestValidLocation>
    19
    <!World> <%s106> <XComWorldData.GetPositionFromTileCoordinates.ReturnValue> 00 1B <GetPositionFromTileCoordinates>
        A7 <!numX> 16
        A7 <!numY> 16
        A7 <!numZ> 16
    16
    28 28 28
16

//kSpawnPoint = Spawn(class'XComMeldContainerSpawnPoint',,,arrEasyMeldSpawnPoints,,, true)
0F 00 <.kSpawnPoint> 1C <Engine.Actor.Spawn> 20 <Class.XComMeldContainerSpawnPoint> 4A 4A 00 <.arrEasyMeldSpawnPoints> 4A 4A 27 4A 16

//kSpawnPoint.m_iDestroyedOnTurn = int(VSize(kSpawnPoint.Location - PlayerLocation) / 768) // TileSize = 96, mobility = 8 tiles
0F 19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
38 44 // cast float to int
AC // /
E1 // VSize
D8 // -
19 00 <.kSpawnPoint> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
<!PlayerLocation>
16 // - end
16 // VSize end
1E <%f 768.0>
16 // / end

/*
<!DebugLog> 70 1F <%t "m_iDestroyedOnTurn: "> 38 53 
19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
16 16
*/

//kSpawnPoint.m_iDestroyedOnTurn += 2
A1 19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 2C 02 16

/*
<!DebugLog> 70 1F <%t "m_iDestroyedOnTurn + 2: "> 38 53 
19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
16 16
*/

//kMeldActor = Spawn(class'XComMeldContainerActor', self,, kSpawnPoint.Location,, kMeldActor_Arc)
0F 00 <.kMeldActor> 1C <Engine.Actor.Spawn> 20 <Class.XComMeldContainerActor> 17 4A 19 00 <.kSpawnPoint> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location> 4A 00 <.kMeldActor_Arc> 4A 4A 16

//kMeldActor.InitFromSpawnPoint(kSpawnPoint)
19 00 <.kMeldActor> 13 00 <NullRef> 00 1B <InitFromSpawnPoint> 00 <.kSpawnPoint> 16

//++A
A3 00 <.A> 16

//goto loop1
06 [@loop1]

[#loop1end]

//return
04 0B

//EOS
53

 

 

Result:

 

http://i.imgur.com/YEU3acz.jpg

 

Again, meld placement is this code is completely random. m_iDestroyedOnTurn timer is calculated based on distance between the squad and current container.

 

Meld placement could be enhanced by dividing map to, say, 4 sections and placing containers randomly inside those sections (as random pods code does with pods). Additional logic to create 'easy' and 'hard' containers could also be programmed in. And I can also disallow containers placement inside 30 methers radius near LZ (again, as random pods code does).

Link to comment
Share on other sites

  • Replies 60
  • Created
  • Last Reply

Top Posters In This Topic

You mean drop ThinMen on the roofs only?

I think some gradation w.r.t. difficulty by factoring in elevation, cover and proximity to overwatching XCOM troops would be cool. So, for instance, an 'easy' drop would happen smack in the middle of a bunch of overwatching units whereas a 'hard' drop would maybe place a thin man on some roof in cover and outside the immediate visual range of your units so they invariably will run into its overwatch fire.

Maybe coding in some special cases for 'cheap tricks' could be fun, too, like dropping a thin man right next to an injured soldier so that overwatch fire killing the thin man would be poisoning that already injured soldier, a 'suicide drop' if you will. Might be a little frustrating though, most people probably won't appreciate aliens playing dirty like that :happy:

 

It would be cool if the drop-in animations for sectoids, chryssalids and mutons from the Slingshot DLC could be used to have more variation in alien unit types dropping in, but as far as I understand those are tied to the map packages in some fashion, bummer.

Link to comment
Share on other sites

It's theoretically possible to do such thing, but it will be very-very difficult to script.

 

I was thinking more about randomness and unpredictability. In vanilla game Council missions are not fun at all, because they're 100% scripted:

1. VIP/bomb/assets positions are fixed.

2. Pods positions are fixed, patrolling disabled.

3. ThinMen are always dropped to the same positions.

4. Drop-downs are triggered by VIP movement.

 

So, basically, you could leave VIP at the start point, clear the map and then move VIP and overwatch all the drop-downs.

 

The idea is to make player really guard the VIP. I.e. if the player will try to leave VIP at some 'safe' location, VIP will be killed by patrols/drop-downs.

 

With current mechanics I see three ways to achieve this goal:

1. Make pods on map patrol randomly (done).

2. Make ThinMen drop down to random locations (almost done).

3. Make drop-downs trigger not only by VIP, but by any other squad member too.

 

If I could make 3 possible, I'll probably tie drop-down locations to VIP position. About 30-40 meters in random direction.

 

If I couldn't make 3 possible, it might be better to drop ThinMen at completely random locations for less overall predictability. Or I could randomly pick one of the squad members (including VIP) and drop ThinMen nearby.

Edited by wghost81
Link to comment
Share on other sites

Yes. I'll post UE Explorer code later, as I'm under Linux now.

 

Random drop-downs mod is playable, but I won't recommend including random Meld mod right now. With placement being completely random Meld canisters often end up close together and too close to LZ. I'm working on improved canister placement algorithm right now and will release the code when it's done.

Link to comment
Share on other sites

Thanks!

 

 

Random drop-downs mod is playable, but I won't recommend including random Meld mod right now. With placement being completely random Meld canisters often end up close together and too close to LZ. I'm working on improved canister placement algorithm right now and will release the code when it's done.

 

Roger that. We like the vanilla easy / hard placement functionality (and have varied up the number of canisters from 1 to 3), so we'd probably stick with that unless it's more or less duplicated by your mod, but I'll take a look at your code when it's ready. Thanks again!

Link to comment
Share on other sites

Decompiled code

 

 

event Activated()
{
    local Vector SpawnObj;
    local XComWorldData ThisObj;
    local XComSpawnPoint_Alien SpawnPt;

    // End:0x11
    if(!bEnabled)
    {
        return;
    }
    ThisObj = class'XComWorldData'.static.GetWorldData();
    SpawnedUnit = none;
    OutputLinks[1].bHasImpulse = false;
    m_kDropIn = new class'XGAISpawnMethod_DropIn';
    m_kDropIn.InitDropIn(iDropHeight, bUseOverwatch, bTriggerOverwatch, bPlaySound, bRevealSpawn, bSpawnImmediately, ForceAlienType, kAdditionalSound, self);
    SpawnObj = ThisObj.FindClosestValidLocation(ThisObj.GetPositionFromTileCoordinates(Rand(ThisObj.NumX), Rand(ThisObj.NumY), ThisObj.NumZ - 1), false, true, false);
    SpawnPt = Spawn(class'XComSpawnPoint_Alien',,, SpawnObj,,, true);
    m_kDropIn.AddSpawnPoint(SpawnPt);
    // End:0x219
    if(bSpawnImmediately)
    {
        m_kDropIn.CheckContentLoaded();
    }
    OutputLinks[0].bHasImpulse = true;
    ActivateOutputLink(0);
    //return;    
}

 

 

Tokens:

 

 

(000/000) [07 11 00 81 2D 01 D5 0E 00 00 16]
	JIN(15/11) -> NF(12/8) -> BV(10/6) -> IV(9/5) -> EFP(1/1)
	if(!bEnabled)

(00F/00B) [04 0B]
	R(2/2) -> N(1/1)
	return

(011/00D) [0F 00 D8 0E 00 00 12 20 2A 0C 00 00 0A 00 C8 D2 00 00 00 1B 47 3C 00 00 00 00 00 00 16]
	L(41/29) -> LV(9/5) -> CC(31/23) -> OC(9/5) -> VF(10/10) -> EFP(1/1)
	ThisObj = class'XComWorldData'.static.GetWorldData()

(03A/02A) [0F 01 CD 0E 00 00 2A]
	L(11/7) -> IV(9/5) -> NO(1/1)
	SpawnedUnit = none

(045/031) [14 2D 35 DD FE FF FF F0 F8 FF FF 00 01 10 26 01 D9 FF FF FF 28]
	LB(33/21) -> BV(31/19) -> SM(30/18) -> DAE(11/7) -> IO(1/1) -> IV(9/5) -> F(1/1)
	OutputLinks[1].bHasImpulse = false

(066/046) [0F 01 CC 0E 00 00 11 0B 0B 0B 20 53 B0 00 00 0B]
	L(24/16) -> IV(9/5) -> N(14/10) -> N(1/1) -> N(1/1) -> N(1/1) -> OC(9/5) -> N(1/1)
	m_kDropIn = new class'XGAISpawnMethod_DropIn'

(07E/056) [19 01 CC 0E 00 00 59 00 00 00 00 00 00 1B B8 41 00 00 00 00 00 00 01 CF 0E 00 00 2D 01 D4 0E 00 00 2D 01 D3 0E 00 00 2D 01 D2 0E 00 00 2D 01 D1 0E 00 00 2D 01 D0 0E 00 00 01 D6 0E 00 00 01 CE 0E 00 00 17 4A 16]
	C(110/70) -> IV(9/5) -> VF(89/57) -> IV(9/5) -> BV(10/6) -> IV(9/5) -> BV(10/6) -> IV(9/5) -> BV(10/6) -> IV(9/5) -> BV(10/6) -> IV(9/5) -> BV(10/6) -> IV(9/5) -> IV(9/5) -> IV(9/5) -> S(1/1) -> NP(1/1) -> EFP(1/1)
	m_kDropIn.InitDropIn(iDropHeight, bUseOverwatch, bTriggerOverwatch, bPlaySound, bRevealSpawn, bSpawnImmediately, ForceAlienType, kAdditionalSound, self)

(0EC/09C) [0F 00 D9 0E 00 00 19 00 D8 0E 00 00 8D 00 AC D2 00 00 00 1B 29 33 00 00 00 00 00 00 19 00 D8 0E 00 00 6B 00 BC 0C 00 00 00 1B DD 3A 00 00 00 00 00 00 A7 19 00 D8 0E 00 00 09 00 30 0D 00 00 00 01 30 0D 00 00 16 A7 19 00 D8 0E 00 00 09 00 2F 0D 00 00 00 01 2F 0D 00 00 16 93 19 00 D8 0E 00 00 09 00 2E 0D 00 00 00 01 2E 0D 00 00 26 16 16 28 27 28 16]
	L(172/116) -> LV(9/5) -> C(162/110) -> LV(9/5) -> VF(141/97) -> C(128/84) -> LV(9/5) -> VF(107/71) -> NF(32/20) -> C(30/18) -> LV(9/5) -> IV(9/5) -> EFP(1/1) -> NF(32/20) -> C(30/18) -> LV(9/5) -> IV(9/5) -> EFP(1/1) -> NF(33/21) -> C(30/18) -> LV(9/5) -> IV(9/5) -> IO(1/1) -> EFP(1/1) -> EFP(1/1) -> F(1/1) -> T(1/1) -> F(1/1) -> EFP(1/1)
	SpawnObj = ThisObj.FindClosestValidLocation(ThisObj.GetPositionFromTileCoordinates(Rand(ThisObj.NumX), Rand(ThisObj.NumY), ThisObj.NumZ - 1), false, true, false)

(198/110) [0F 00 D7 0E 00 00 1C 12 FC FF FF 20 17 79 00 00 4A 4A 00 D9 0E 00 00 4A 4A 27 4A 16]
	L(44/28) -> LV(9/5) -> FF(34/22) -> OC(9/5) -> NP(1/1) -> NP(1/1) -> LV(9/5) -> NP(1/1) -> NP(1/1) -> T(1/1) -> NP(1/1) -> EFP(1/1)
	SpawnPt = Spawn(class'XComSpawnPoint_Alien',,, SpawnObj,,, true)

(1C4/12C) [19 01 CC 0E 00 00 14 00 00 00 00 00 00 1B AE 02 00 00 00 00 00 00 00 D7 0E 00 00 4A 16]
	C(41/29) -> IV(9/5) -> VF(20/16) -> LV(9/5) -> NP(1/1) -> EFP(1/1)
	m_kDropIn.AddSpawnPoint(SpawnPt)

(1ED/149) [07 19 02 2D 01 D0 0E 00 00]
	JIN(13/9) -> BV(10/6) -> IV(9/5)
	if(bSpawnImmediately)

(1FA/152) [19 01 CC 0E 00 00 0A 00 00 00 00 00 00 1B 59 12 00 00 00 00 00 00 16]
	C(31/23) -> IV(9/5) -> VF(10/10) -> EFP(1/1)
	m_kDropIn.CheckContentLoaded()

(219/169) [14 2D 35 DD FE FF FF F0 F8 FF FF 00 01 10 25 01 D9 FF FF FF 27]
	LB(33/21) -> BV(31/19) -> SM(30/18) -> DAE(11/7) -> IZ(1/1) -> IV(9/5) -> T(1/1)
	OutputLinks[0].bHasImpulse = true

(23A/17E) [1C 7A FA FF FF 25 16]
	FF(11/7) -> IZ(1/1) -> EFP(1/1)
	ActivateOutputLink(0)

(245/185) [04 0B]
	R(2/2) -> N(1/1)
	return

(247/187) [53]
	EOS(1/1)

 

Link to comment
Share on other sites

I've changed Meld placement algorithm a bit and also tested it with reused maps: all seems to work fine.

 

http://i.imgur.com/ZAlFuGr.jpg

 

New algorithm (PatchUPK/PatcherGUI modfile):

 

UPK_FILE=XComGame.upk

// convert XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints.arrEasyMeldSpawnPoints to integer
EXPORT_ENTRY=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints.arrEasyMeldSpawnPoints
[MODDED_CODE]
<Core.IntProperty>					// new TypeRef
<NullRef>							// ParentClassRef
<XGBattle_SP.InitMeldContainers>	// new OwnerRef
<A>									// new NameIdx
<NullRef>							// ArchetypeRef
<%u 0x00000000>						// ObjectFlagsH
<%u 0x00070004>						// ObjectFlagsL
<%u 40>								// new SerialSize

// reset property flags for new var A and make it a static array
OBJECT=XGBattle_SP.InitMeldContainers.A
REL_OFFSET=16
[MODDED_CODE]
<%s 3>								// increase ArrayDim
<%s 0>								// ElementSize
<%u 0>								// set PropertyFlagsL to zero

// convert XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints dynamic array to structure
EXPORT_ENTRY=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints
MODDED_CODE=<Core.StructProperty>	// new type

// make arrEasyMeldSpawnPoints a static array of vector type
OBJECT=XGBattle_SP.InitMeldContainers.arrEasyMeldSpawnPoints
REL_OFFSET=16
[MODDED_CODE]
<%s 5>								// increase ArrayDim
<%s 0>								// ElementSize
<%u 0>								// set PropertyFlagsL to zero
<%u 0>								// PropertyFlagsH
<None>								// CategoryIndex
<NullRef>							// ArrayEnumRef
<Core.Object.Vector>				// new InnerObjRef

// change inner type of arrHardMeldSpawnPoints array
OBJECT=XGBattle_SP.InitMeldContainers.arrHardMeldSpawnPoints.arrHardMeldSpawnPoints
REL_OFFSET=40
MODDED_CODE=<Class.XComSpawnPoint>	// new type

// link new var A to XGBattle_SP.InitMeldContainers
OBJECT=XGBattle_SP.InitMeldContainers.kMeldActor_Arc
REL_OFFSET=12
MODDED_CODE=<XGBattle_SP.InitMeldContainers.A>	// NextRef

OBJECT=XGBattle_SP.InitMeldContainers:AUTO

ALIAS=World:12 20 <Class.XComWorldData> 0A 00 <XComWorldData.GetWorldData.ReturnValue> 00 1B <GetWorldData> 16
ALIAS=numX:19 <!World> 09 00 <XComWorldData.NumX> 00 01 <XComWorldData.NumX>
ALIAS=numY:19 <!World> 09 00 <XComWorldData.NumY> 00 01 <XComWorldData.NumY>
ALIAS=numZ:19 <!World> 09 00 <XComWorldData.NumZ> 00 01 <XComWorldData.NumZ>
ALIAS=PlayerLocation:19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>

//ALIAS=DebugLog:19 2E <Class.XComOnlineEventMgr> 19 2E <Engine.GameEngine> 12 20 <Engine.Engine> 0A 00 <Engine.Engine.GetEngine.ReturnValue> 00 1C <Engine.Engine.GetEngine> 16 09 00 <Engine.GameEngine.OnlineEventManager> 00 01 <Engine.GameEngine.OnlineEventManager> FF 00 <NullRef> 00 1B <DevOnlineMsg>

ALIAS=MIN_DIST_LZ:1E <%f 1600>			// 25 meters
ALIAS=MIN_DIST_OTHER:1E <%f 1920>		// 30 meters
ALIAS=NUM_CONTAINERS:2C 02
ALIAS=ADD_TURNS:2C 02					// num additional turns until destroyed

[REPLACEMENT_CODE]
//kMeldActor_Arc = XComMeldContainerActor(DynamicLoadObject("MeldCanister_MOD.MeldCanister_ARC", class'XComMeldContainerActor', false))
0F 00 <.kMeldActor_Arc> 2E <Class.XComMeldContainerActor> 1C <Core.Object.DynamicLoadObject> 1F <%t "MeldCanister_MOD.MeldCanister_ARC"> 20 <Class.XComMeldContainerActor> 28 16

//arrHardMeldSpawnPoints = GetSpawnPoints(8) // get player start
0F 00 <.arrHardMeldSpawnPoints> 1B <GetSpawnPoints> 24 08 4A 16

/*
<!DebugLog> 70 1F <%t "player_location_x: "> 38 55 
35 <Core.Object.Vector.X> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
<!DebugLog> 70 1F <%t "player_location_y: "> 38 55 
35 <Core.Object.Vector.Y> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
<!DebugLog> 70 1F <%t "player_location_z: "> 38 55 
35 <Core.Object.Vector.Z> <Core.Object.Vector> 00 00
19 10 26 00 <.arrHardMeldSpawnPoints> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
16 16
*/

//find good spawn points

//A[0] = 0
0F 1A 25 00 <.A> 25

[#loop1]
//if (A[0] < NUM_CONTAINERS)
07 [@loop1end] 96 1A 25 00 <.A> <!NUM_CONTAINERS> 16

//A[1] = 0
0F 1A 26 00 <.A> 25

[#loop3]
//if (A[1] < 100)
07 [@loop3end] 96 1A 26 00 <.A> 2C <%b 100> 16

//arrEasyMeldSpawnPoints[0] = World.FindClosestValidLocation(World.GetPositionFromTileCoordinates(Rand(numX), Rand(numY), Rand(numZ)), false, false, false);
0F 1A 25 00 <.arrEasyMeldSpawnPoints>
19
<!World> <%s140> <XComWorldData.FindClosestValidLocation.ReturnValue> 00 1B <FindClosestValidLocation>
    19
    <!World> <%s106> <XComWorldData.GetPositionFromTileCoordinates.ReturnValue> 00 1B <GetPositionFromTileCoordinates>
        A7 <!numX> 16
        A7 <!numY> 16
        A7 <!numZ> 16
    16
    28 28 28
16

//if (VSize(arrEasyMeldSpawnPoints[0] - PlayerLocation) < MIN_DIST_LZ)
07 [@check1] B0 E1 D8 1A 25 00 <.arrEasyMeldSpawnPoints> <!PlayerLocation> 16 16 <!MIN_DIST_LZ> 16

//goto loop3
06 [@loop3]

[#check1]

//A[2] = 1
0F 1A 2C 02 00 <.A> 26

[#loop4]
//if (A[2] <= A[0])
07 [@loop3end] 98 1A 2C 02 00 <.A> 1A 25 00 <.A> 16

//if (VSize(arrEasyMeldSpawnPoints[0] - arrEasyMeldSpawnPoints[A[2]]) < MIN_DIST_OTHER)
07 [@check2] B0 E1 D8 1A 25 00 <.arrEasyMeldSpawnPoints> 1A 1A 2C 02 00 <.A> 00 <.arrEasyMeldSpawnPoints> 16 16 <!MIN_DIST_OTHER> 16

//goto loop3
06 [@loop3]

[#check2]

//++A[2]
A3 1A 2C 02 00 <.A> 16

//goto loop4
06 [@loop4]

[#loop3end]

//arrEasyMeldSpawnPoints[A[0] + 1] = arrEasyMeldSpawnPoints[0]
0F 1A 92 1A 25 00 <.A> 26 16 00 <.arrEasyMeldSpawnPoints> 1A 25 00 <.arrEasyMeldSpawnPoints>

//++A[0]
A3 1A 25 00 <.A> 16

//goto loop1
06 [@loop1]

[#loop1end]

//A[0] = 0
0F 1A 25 00 <.A> 25

[#loop2]
//if (A[0] < NUM_CONTAINERS)
07 [@loop2end] 96 1A 25 00 <.A> <!NUM_CONTAINERS> 16

//kSpawnPoint = Spawn(class'XComMeldContainerSpawnPoint',,,arrEasyMeldSpawnPoints[A[0] + 1],,, true)
0F 00 <.kSpawnPoint> 1C <Engine.Actor.Spawn> 20 <Class.XComMeldContainerSpawnPoint> 4A 4A
1A 92 1A 25 00 <.A> 26 16 00 <.arrEasyMeldSpawnPoints>
4A 4A 27 4A 16

//kSpawnPoint.m_iDestroyedOnTurn = int(VSize(kSpawnPoint.Location - PlayerLocation) / 768) // TileSize = 96, mobility = 8 tiles
0F 19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
38 44 // cast float to int
AC // /
E1 // VSize
D8 // -
19 00 <.kSpawnPoint> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location>
<!PlayerLocation>
16 // - end
16 // VSize end
1E <%f 768.0>
16 // / end

/*
<!DebugLog> 70 1F <%t "m_iDestroyedOnTurn: "> 38 53 
19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
16 16
*/

//kSpawnPoint.m_iDestroyedOnTurn += ADD_TURNS
A1 19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> <!ADD_TURNS> 16

/*
<!DebugLog> 70 1F <%t "m_iDestroyedOnTurn + 2: "> 38 53 
19 00 <.kSpawnPoint> 09 00 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn> 00 01 <XComMeldContainerSpawnPoint.m_iDestroyedOnTurn>
16 16
*/

//kMeldActor = Spawn(class'XComMeldContainerActor', self,, kSpawnPoint.Location,, kMeldActor_Arc)
0F 00 <.kMeldActor> 1C <Engine.Actor.Spawn> 20 <Class.XComMeldContainerActor> 17 4A 19 00 <.kSpawnPoint> 09 00 <Engine.Actor.Location> 00 01 <Engine.Actor.Location> 4A 00 <.kMeldActor_Arc> 4A 4A 16

//kMeldActor.InitFromSpawnPoint(kSpawnPoint)
19 00 <.kMeldActor> 13 00 <NullRef> 00 1B <InitFromSpawnPoint> 00 <.kSpawnPoint> 16

//++A[0]
A3 1A 25 00 <.A> 16

//goto loop2
06 [@loop2]

[#loop2end]

//return
04 0B

//EOS
53

 

 

This part of the code

ALIAS=MIN_DIST_LZ:1E <%f 1600>			// 25 meters
ALIAS=MIN_DIST_OTHER:1E <%f 1920>		// 30 meters
ALIAS=NUM_CONTAINERS:2C 02
ALIAS=ADD_TURNS:2C 02				// num additional turns until destroyed
is adjustable. MIN_DIST_LZ sets minimum distance from landing zone and MIN_DIST_OTHER sets minimum distance from the other canisters. NUM_CONTAINERS sets number of Meld canisters and can vary between 1 and 4 (if you want more, you'll need to increase ArrayDim for arrEasyMeldSpawnPoints, but that much Meld is crazy :smile: ). ADD_TURNS sets number of additional turns for timer.

 

Random placement sure adds something new to Meld tactics, as in lot of the cases you'll get two canisters on opposite map corners. In vanilla game you can always collect both canisters along the way, it's the matter of moving and killing everything quickly. But with randomness it might not be possible.

 

I'm still working on improving placement algorithm. One thing I noticed: linear dependency between timer and distance to LZ is not good. The further Meld canister is, the more additional turns it needs, because squad will engage enemies and won't be able to move quickly. It needs additional in-game testing, but I'm thinking about changing linear dependency to, say, quadratic.

 

And yes, THAT map (Highway1) can have Meld spawned on THAT roof. :smile: I really-really hate that map. I really do.

 

johnnylump, if you like the idea of easy-hard-very hard three-canisters setup for LW, I can try to script it. I have some ideas of my own, like using three probabilities to determine if particular (easy, hard, very hard) canister will be spawned and spawning those canisters with random distance and angle. Distance restrictions will be set by canister difficultly, distance variation, LZ and other canister proximity. Angle can be completely random, or we can set some restriction, to create vanilla-like placement (I'm not sure I can do this, but it's worth a try anyway).

Link to comment
Share on other sites

WGhost,

 

Thanks much for the tokens.

 

On meld cans, I'll actually defer to Amineri for what's best for Long War ... she designed what variety in canisters we have, so she should probably ring in on how we'd want to mix things up. She's taking a break right now but I'm sure she'll post here when she's back.

Link to comment
Share on other sites

Regarding Highway1 map.

 

Here are two examples of "Show VOLUMES" console command.

 

Simple square map UPB_Bar:

 

http://i.imgur.com/kX8L9TR.jpg

 

Highway1 map:

 

http://i.imgur.com/jdpdHfk.jpg

 

Magenta color lines show the volumes we're looking for. It should be map object and it should be a volume. But I can't find it. :sad:

 

If we could find this object (or group of objects), we could simply add another check: if spawn point is inside this volume.

 

But, since I can't find it, I will add another check: if map is Highway1 and x coord < 40 and y coord > 900, do not spawn anything there.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...