forked from 0ad/0ad
add more flexibility to eject units when garrisonHolder is destroyed, fixes #2242
This was SVN commit r14550.
This commit is contained in:
parent
2d806f81f0
commit
ec36222b20
@ -13,8 +13,11 @@ GarrisonHolder.prototype.Schema =
|
||||
"<element name='EjectHealth' a:help='Percentage of maximum health below which this holder no longer allows garrisoning'>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='EjectEntitiesOnDestroy' a:help='Whether the entity should eject or kill all garrisoned entities on destroy'>" +
|
||||
"<data type='boolean'/>" +
|
||||
"<element name='EjectClassesOnDestroy' a:help='Classes of entities to be ejected on destroy. Others are killed'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"<element name='BuffHeal' a:help='Number of hit points that will be restored to this holder's garrisoned units each second'>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
@ -105,11 +108,6 @@ GarrisonHolder.prototype.GetHealRate = function()
|
||||
return ApplyValueModificationsToEntity("GarrisonHolder/BuffHeal", +this.template.BuffHeal, this.entity);
|
||||
};
|
||||
|
||||
GarrisonHolder.prototype.EjectEntitiesOnDestroy = function()
|
||||
{
|
||||
return this.template.EjectEntitiesOnDestroy == "true";
|
||||
};
|
||||
|
||||
/**
|
||||
* Set this entity to allow or disallow garrisoning in
|
||||
* Every component calling this function should do it with its own ID, and as long as one
|
||||
@ -270,7 +268,15 @@ GarrisonHolder.prototype.Eject = function(entity, forced)
|
||||
|
||||
// Find spawning location
|
||||
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
|
||||
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
|
||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
||||
// If the garrisonHolder is a sinking ship, restrict the location to the intersection of both passabilities
|
||||
// TODO: should use passability classes to be more generic
|
||||
if ((!cmpHealth || cmpHealth.GetHitpoints() == 0) && cmpIdentity && cmpIdentity.HasClass("Ship"))
|
||||
var pos = cmpFootprint.PickSpawnPointBothPass(entity);
|
||||
else
|
||||
var pos = cmpFootprint.PickSpawnPoint(entity);
|
||||
|
||||
if (pos.y < 0)
|
||||
{
|
||||
// Error: couldn't find space satisfying the unit's passability criteria
|
||||
@ -602,8 +608,13 @@ GarrisonHolder.prototype.EjectOrKill = function(entities)
|
||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
// Eject the units which can be ejected (if not in world, it generally means this holder
|
||||
// is inside a holder which kills its entities, so do not eject)
|
||||
if (cmpPosition.IsInWorld() && this.EjectEntitiesOnDestroy())
|
||||
this.PerformEject(entities, true);
|
||||
if (cmpPosition.IsInWorld())
|
||||
{
|
||||
var cmpGarrisonHolder = this;
|
||||
var ejectables = entities.filter(function(ent) { return cmpGarrisonHolder.IsEjectable(ent) });
|
||||
if (ejectables.length)
|
||||
this.PerformEject(ejectables, false);
|
||||
}
|
||||
|
||||
// And destroy all remaining entities
|
||||
for each (var entity in entities)
|
||||
@ -621,5 +632,20 @@ GarrisonHolder.prototype.EjectOrKill = function(entities)
|
||||
this.UpdateGarrisonFlag();
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if an entity is ejectable on destroy if possible
|
||||
*/
|
||||
GarrisonHolder.prototype.IsEjectable = function(entity)
|
||||
{
|
||||
var ejectableClasses = this.template.EjectClassesOnDestroy._string;
|
||||
ejectableClasses = ejectableClasses ? ejectableClasses.split(/\s+/) : [];
|
||||
var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
|
||||
for each (var ejectableClass in ejectableClasses)
|
||||
if (entityClasses.indexOf(ejectableClass) != -1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_GarrisonHolder, "GarrisonHolder", GarrisonHolder);
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>5</LoadingRange>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens" />
|
||||
</GarrisonHolder>
|
||||
<Decay>
|
||||
<Inactive/>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -49,7 +49,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>1</LoadingRange>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<Max>3</Max>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support</List>
|
||||
<LoadingRange>1</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>3</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -46,7 +46,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -44,7 +44,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>1</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>10</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Infantry Cavalry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>1</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -46,7 +46,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.075</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry Siege</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>6</LoadingRange>
|
||||
|
@ -13,7 +13,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>10</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Animal</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Female Infantry</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>10</LoadingRange>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>1</Max>
|
||||
<EjectHealth>0</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Female Infantry</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>10</LoadingRange>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>15</Max>
|
||||
<EjectHealth>0</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Female Infantry</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>10</LoadingRange>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>50</Max>
|
||||
<EjectHealth>0</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Female Infantry</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry Siege</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>10</LoadingRange>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>30</Max>
|
||||
<EjectHealth>0</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>false</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Female Infantry</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry Siege</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>10</LoadingRange>
|
||||
|
@ -48,7 +48,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -46,7 +46,7 @@
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectEntitiesOnDestroy>true</EjectEntitiesOnDestroy>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
|
@ -250,6 +250,144 @@ public:
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
virtual CFixedVector3D PickSpawnPointBothPass(entity_id_t spawned)
|
||||
{
|
||||
// Try to find a free space inside and around this footprint
|
||||
// at the intersection between the footprint passability and the unit passability.
|
||||
// (useful for example for destroyed ships where the spawning point should be in the intersection
|
||||
// of the unit and ship passabilities).
|
||||
// As the overlap between these passabilities regions may be narrow, we need a small step (1 meter)
|
||||
|
||||
const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1));
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (!cmpPosition || !cmpPosition->IsInWorld())
|
||||
return error;
|
||||
|
||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
|
||||
if (!cmpObstructionManager)
|
||||
return error;
|
||||
|
||||
entity_pos_t spawnedRadius;
|
||||
ICmpObstructionManager::tag_t spawnedTag;
|
||||
|
||||
CmpPtr<ICmpObstruction> cmpSpawnedObstruction(GetSimContext(), spawned);
|
||||
if (cmpSpawnedObstruction)
|
||||
{
|
||||
spawnedRadius = cmpSpawnedObstruction->GetUnitRadius();
|
||||
spawnedTag = cmpSpawnedObstruction->GetObstruction();
|
||||
}
|
||||
// else use zero radius
|
||||
|
||||
// Get passability class from UnitMotion
|
||||
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), spawned);
|
||||
if (!cmpUnitMotion)
|
||||
return error;
|
||||
|
||||
ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass();
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
|
||||
if (!cmpPathfinder)
|
||||
return error;
|
||||
|
||||
// Get the Footprint entity passability
|
||||
CmpPtr<ICmpUnitMotion> cmpEntityMotion(GetEntityHandle());
|
||||
if (!cmpEntityMotion)
|
||||
return error;
|
||||
ICmpPathfinder::pass_class_t entityPass = cmpEntityMotion->GetPassabilityClass();
|
||||
|
||||
CFixedVector2D initialPos = cmpPosition->GetPosition2D();
|
||||
entity_angle_t initialAngle = cmpPosition->GetRotation().Y;
|
||||
|
||||
// Max spawning distance + 1 (in meters)
|
||||
const i32 maxSpawningDistance = 13;
|
||||
|
||||
if (m_Shape == CIRCLE)
|
||||
{
|
||||
// Expand outwards from foundation with a fixed step of 1 meter
|
||||
for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
|
||||
{
|
||||
// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
|
||||
entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(1+dist);
|
||||
entity_pos_t radius = m_Size0 + clearance;
|
||||
|
||||
// Try equally-spaced points around the circle in alternating directions, starting from the front
|
||||
const i32 numPoints = 31 + 2*dist;
|
||||
for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
|
||||
{
|
||||
entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);
|
||||
|
||||
fixed s, c;
|
||||
sincos_approx(angle, s, c);
|
||||
|
||||
CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));
|
||||
|
||||
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS &&
|
||||
cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, entityPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
|
||||
return pos; // this position is okay, so return it
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed s, c;
|
||||
sincos_approx(initialAngle, s, c);
|
||||
|
||||
// Expand outwards from foundation with a fixed step of 1 meter
|
||||
for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
|
||||
{
|
||||
// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
|
||||
entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(1+dist);
|
||||
|
||||
for (i32 edge = 0; edge < 4; ++edge)
|
||||
{
|
||||
// Compute the direction and length of the current edge
|
||||
CFixedVector2D dir;
|
||||
fixed sx, sy;
|
||||
switch (edge)
|
||||
{
|
||||
case 0:
|
||||
dir = CFixedVector2D(c, -s);
|
||||
sx = m_Size0;
|
||||
sy = m_Size1;
|
||||
break;
|
||||
case 1:
|
||||
dir = CFixedVector2D(-s, -c);
|
||||
sx = m_Size1;
|
||||
sy = m_Size0;
|
||||
break;
|
||||
case 2:
|
||||
dir = CFixedVector2D(s, c);
|
||||
sx = m_Size1;
|
||||
sy = m_Size0;
|
||||
break;
|
||||
case 3:
|
||||
dir = CFixedVector2D(-c, s);
|
||||
sx = m_Size0;
|
||||
sy = m_Size1;
|
||||
break;
|
||||
}
|
||||
sx = sx/2 + clearance;
|
||||
sy = sy/2 + clearance;
|
||||
// Try equally-spaced (1 meter) points along the edge in alternating directions, starting from the middle
|
||||
i32 numPoints = 1 + 2*sx.ToInt_RoundToNearest();
|
||||
CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy);
|
||||
for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
|
||||
{
|
||||
CFixedVector2D pos (center + dir*i);
|
||||
|
||||
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
|
||||
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS &&
|
||||
cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, entityPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
|
||||
return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(Footprint)
|
||||
|
@ -62,5 +62,6 @@ CScriptVal ICmpFootprint::GetShape_wrapper()
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(Footprint)
|
||||
DEFINE_INTERFACE_METHOD_1("PickSpawnPoint", CFixedVector3D, ICmpFootprint, PickSpawnPoint, entity_id_t)
|
||||
DEFINE_INTERFACE_METHOD_1("PickSpawnPointBothPass", CFixedVector3D, ICmpFootprint, PickSpawnPointBothPass, entity_id_t)
|
||||
DEFINE_INTERFACE_METHOD_0("GetShape", CScriptVal, ICmpFootprint, GetShape_wrapper)
|
||||
END_INTERFACE_WRAPPER(Footprint)
|
||||
|
@ -64,6 +64,13 @@ public:
|
||||
*/
|
||||
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned) = 0;
|
||||
|
||||
/**
|
||||
* Pick a sensible position to place a newly-spawned entity near this footprint,
|
||||
* at the intersection between the footprint passability and the entity one.
|
||||
* @return the X and Z coordinates of the spawn point, with Y = 0; or the special value (-1, -1, -1) if there's no space
|
||||
*/
|
||||
virtual CFixedVector3D PickSpawnPointBothPass(entity_id_t spawned) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Footprint)
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user