@@ -43,14 +43,65 @@ namespace OpenRA.Mods.Common.Warheads
|
||||
[Desc("Chance of impact sound to play.")]
|
||||
public readonly int ImpactSoundChance = 100;
|
||||
|
||||
[Desc("Explosions above this altitude that don't impact an actor will check target validity against the 'TargetTypeAir' target types.")]
|
||||
[Desc("Consider explosion above this altitude an air explosion.",
|
||||
"If that's the case, this warhead will consider the explosion position to have the 'Air' TargetType (in addition to any nearby actor's TargetTypes).")]
|
||||
public readonly WDist AirThreshold = new WDist(128);
|
||||
|
||||
[Desc("Target types to use when the warhead detonated at an altitude greater than 'AirThreshold'.")]
|
||||
[Desc("Whether to consider actors in determining whether the explosion should happen. If false, only terrain will be considered.")]
|
||||
public readonly bool ImpactActors = true;
|
||||
|
||||
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
|
||||
|
||||
[Desc("Check for direct hits against nearby actors for use in the target validity checks.")]
|
||||
public readonly bool ImpactActors = true;
|
||||
public ImpactType GetImpactType(World world, CPos cell, WPos pos, Actor firedBy)
|
||||
{
|
||||
// Matching target actor
|
||||
if (ImpactActors)
|
||||
{
|
||||
var targetType = GetDirectHitTargetType(world, cell, pos, firedBy, true);
|
||||
if (targetType == ImpactTargetType.ValidActor)
|
||||
return ImpactType.TargetHit;
|
||||
if (targetType == ImpactTargetType.InvalidActor)
|
||||
return ImpactType.None;
|
||||
}
|
||||
|
||||
var dat = world.Map.DistanceAboveTerrain(pos);
|
||||
if (dat > AirThreshold)
|
||||
return ImpactType.Air;
|
||||
|
||||
return ImpactType.Ground;
|
||||
}
|
||||
|
||||
public ImpactTargetType GetDirectHitTargetType(World world, CPos cell, WPos pos, Actor firedBy, bool checkTargetValidity = false)
|
||||
{
|
||||
var victims = world.FindActorsOnCircle(pos, WDist.Zero);
|
||||
var invalidHit = false;
|
||||
|
||||
foreach (var victim in victims)
|
||||
{
|
||||
if (!AffectsParent && victim == firedBy)
|
||||
continue;
|
||||
|
||||
if (!victim.Info.HasTraitInfo<IHealthInfo>())
|
||||
continue;
|
||||
|
||||
// If the impact position is within any HitShape, we have a direct hit
|
||||
var activeShapes = victim.TraitsImplementing<HitShape>().Where(Exts.IsTraitEnabled);
|
||||
var directHit = activeShapes.Any(i => i.Info.Type.DistanceFromEdge(pos, victim).Length <= 0);
|
||||
|
||||
// If the warhead landed outside the actor's hit-shape(s), we need to skip the rest so it won't be considered an invalidHit
|
||||
if (!directHit)
|
||||
continue;
|
||||
|
||||
if (!checkTargetValidity || IsValidAgainst(victim, firedBy))
|
||||
return ImpactTargetType.ValidActor;
|
||||
|
||||
// If we got here, it must be an invalid target
|
||||
invalidHit = true;
|
||||
}
|
||||
|
||||
// If there was at least a single direct hit, but none on valid target(s), we return InvalidActor
|
||||
return invalidHit ? ImpactTargetType.InvalidActor : ImpactTargetType.NoActor;
|
||||
}
|
||||
|
||||
public override void DoImpact(Target target, Actor firedBy, IEnumerable<int> damageModifiers)
|
||||
{
|
||||
@@ -72,9 +123,11 @@ namespace OpenRA.Mods.Common.Warheads
|
||||
var explosion = Explosions.RandomOrDefault(world.LocalRandom);
|
||||
if (Image != null && explosion != null)
|
||||
{
|
||||
var dat = world.Map.DistanceAboveTerrain(pos);
|
||||
if (ForceDisplayAtGroundLevel)
|
||||
pos -= new WVec(0, 0, dat.Length);
|
||||
{
|
||||
var dat = world.Map.DistanceAboveTerrain(pos);
|
||||
pos = new WPos(pos.X, pos.Y, pos.Z - dat.Length);
|
||||
}
|
||||
|
||||
world.AddFrameEndTask(w => w.Add(new SpriteEffect(pos, w, Image, explosion, palette)));
|
||||
}
|
||||
@@ -87,32 +140,23 @@ namespace OpenRA.Mods.Common.Warheads
|
||||
public bool IsValidImpact(WPos pos, Actor firedBy)
|
||||
{
|
||||
var world = firedBy.World;
|
||||
|
||||
if (ImpactActors)
|
||||
{
|
||||
// Check whether the explosion overlaps with an actor's hitshape
|
||||
var potentialVictims = world.FindActorsOnCircle(pos, WDist.Zero);
|
||||
foreach (var victim in potentialVictims)
|
||||
{
|
||||
if (!AffectsParent && victim == firedBy)
|
||||
continue;
|
||||
|
||||
var activeShapes = victim.TraitsImplementing<HitShape>().Where(Exts.IsTraitEnabled);
|
||||
if (!activeShapes.Any(i => i.Info.Type.DistanceFromEdge(pos, victim).Length <= 0))
|
||||
continue;
|
||||
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var targetTile = world.Map.CellContaining(pos);
|
||||
if (!world.Map.Contains(targetTile))
|
||||
return false;
|
||||
|
||||
var dat = world.Map.DistanceAboveTerrain(pos);
|
||||
var impactType = GetImpactType(world, targetTile, pos, firedBy);
|
||||
switch (impactType)
|
||||
{
|
||||
case ImpactType.TargetHit:
|
||||
return true;
|
||||
case ImpactType.Air:
|
||||
return IsValidTarget(TargetTypeAir);
|
||||
case ImpactType.Ground:
|
||||
var tileInfo = world.Map.GetTerrainInfo(targetTile);
|
||||
return IsValidTarget(dat > AirThreshold ? TargetTypeAir : tileInfo.TargetTypes);
|
||||
return IsValidTarget(tileInfo.TargetTypes);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Warheads
|
||||
{
|
||||
public enum ImpactType
|
||||
{
|
||||
None,
|
||||
Ground,
|
||||
Air,
|
||||
TargetHit
|
||||
}
|
||||
|
||||
public enum ImpactTargetType
|
||||
{
|
||||
NoActor,
|
||||
|
||||
Reference in New Issue
Block a user