Custom Warheads refactor
Changes included: Warhead code split out of weapon code and refactored. Warhead functionality now split into several classes, each handling one effect/impact. Additional custom warheads can now be defined and called via yaml. Custom warheads inherit the abstract class Warhead, which provides target check functions. Custom warheads have to define their own impact functions, and can also define their own replacement for check functions.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class AbsoluteSpreadDamageWarhead : DamageWarhead
|
||||
{
|
||||
[Desc("Maximum spread of the associated SpreadFactor.")]
|
||||
public readonly WRange[] Spread = { new WRange(43) };
|
||||
|
||||
[Desc("What factor to multiply the Damage by for this spread range.", "Each factor specified must have an associated Spread defined.")]
|
||||
public readonly float[] SpreadFactor = { 1f };
|
||||
|
||||
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
// Used by traits that damage a single actor, rather than a position
|
||||
if (target.Type == TargetType.Actor)
|
||||
DoImpact(target.Actor, firedBy, firepowerModifier);
|
||||
else
|
||||
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
var world = firedBy.World;
|
||||
|
||||
for (var i = 0; i < Spread.Length; i++)
|
||||
{
|
||||
var currentSpread = Spread[i];
|
||||
var currentFactor = SpreadFactor[i];
|
||||
var previousSpread = WRange.Zero;
|
||||
if (i > 0)
|
||||
previousSpread = Spread[i - 1];
|
||||
if (currentFactor <= 0f)
|
||||
continue;
|
||||
|
||||
var hitActors = world.FindActorsInCircle(pos, currentSpread);
|
||||
if (previousSpread.Range > 0)
|
||||
hitActors.Except(world.FindActorsInCircle(pos, previousSpread));
|
||||
|
||||
foreach (var victim in hitActors)
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var damage = GetDamageToInflict(victim, firedBy, firepowerModifier * currentFactor);
|
||||
victim.InflictDamage(firedBy, damage, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DoImpact(Actor victim, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var currentFactor = SpreadFactor[0];
|
||||
var damage = (int)GetDamageToInflict(victim, firedBy, firepowerModifier * currentFactor);
|
||||
victim.InflictDamage(firedBy, damage, this);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetDamageToInflict(Actor target, Actor firedBy, float modifier)
|
||||
{
|
||||
var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if (healthInfo == null)
|
||||
return 0;
|
||||
|
||||
var rawDamage = (float)Damage;
|
||||
|
||||
return (int)(rawDamage * modifier * EffectivenessAgainst(target.Info));
|
||||
}
|
||||
}
|
||||
}
|
||||
59
OpenRA.Game/GameRules/Warheads/DamageWarhead.cs
Normal file
59
OpenRA.Game/GameRules/Warheads/DamageWarhead.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public abstract class DamageWarhead : Warhead
|
||||
{
|
||||
[Desc("How much (raw) damage to deal")]
|
||||
public readonly int Damage = 0;
|
||||
|
||||
[Desc("Infantry death animation to use")]
|
||||
public readonly string InfDeath = "1";
|
||||
|
||||
[Desc("Whether we should prevent prone response for infantry.")]
|
||||
public readonly bool PreventProne = false;
|
||||
|
||||
[Desc("By what percentage should damage be modified against prone infantry.")]
|
||||
public readonly int ProneModifier = 50;
|
||||
|
||||
[FieldLoader.LoadUsing("LoadVersus")]
|
||||
[Desc("Damage vs each armortype. 0% = can't target.")]
|
||||
public readonly Dictionary<string, float> Versus;
|
||||
|
||||
public static object LoadVersus(MiniYaml yaml)
|
||||
{
|
||||
var nd = yaml.ToDictionary();
|
||||
return nd.ContainsKey("Versus")
|
||||
? nd["Versus"].ToDictionary(my => FieldLoader.GetValue<float>("(value)", my.Value))
|
||||
: new Dictionary<string, float>();
|
||||
}
|
||||
|
||||
public override float EffectivenessAgainst(ActorInfo ai)
|
||||
{
|
||||
var health = ai.Traits.GetOrDefault<HealthInfo>();
|
||||
if (health == null)
|
||||
return 0f;
|
||||
|
||||
var armor = ai.Traits.GetOrDefault<ArmorInfo>();
|
||||
if (armor == null || armor.Type == null)
|
||||
return 1f;
|
||||
|
||||
float versus;
|
||||
return Versus.TryGetValue(armor.Type, out versus) ? versus : 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class HealthPercentageDamageWarhead : DamageWarhead
|
||||
{
|
||||
[Desc("Size of the area. Damage will be applied to this area.", "If two spreads are defined, the area of effect is a ring, where the second value is the inner radius.")]
|
||||
public readonly WRange[] Spread = { new WRange(43), WRange.Zero };
|
||||
|
||||
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
// Used by traits that damage a single actor, rather than a position
|
||||
if (target.Type == TargetType.Actor)
|
||||
DoImpact(target.Actor, firedBy, firepowerModifier);
|
||||
else
|
||||
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
var world = firedBy.World;
|
||||
var range = Spread[0];
|
||||
var hitActors = world.FindActorsInCircle(pos, range);
|
||||
if (Spread.Length > 1 && Spread[1].Range > 0)
|
||||
hitActors.Except(world.FindActorsInCircle(pos, Spread[1]));
|
||||
|
||||
foreach (var victim in hitActors)
|
||||
DoImpact(victim, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(Actor victim, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var damage = GetDamageToInflict(victim, firedBy, firepowerModifier);
|
||||
if (damage != 0) // will be 0 if the target doesn't have HealthInfo
|
||||
{
|
||||
var healthInfo = victim.Info.Traits.Get<HealthInfo>();
|
||||
damage = (float)(damage / 100 * healthInfo.HP);
|
||||
}
|
||||
|
||||
victim.InflictDamage(firedBy, (int)damage, this);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetDamageToInflict(Actor target, Actor firedBy, float modifier)
|
||||
{
|
||||
var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if (healthInfo == null)
|
||||
return 0;
|
||||
|
||||
var rawDamage = (float)Damage;
|
||||
|
||||
return rawDamage * modifier * EffectivenessAgainst(target.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
OpenRA.Game/GameRules/Warheads/PerCellDamageWarhead.cs
Normal file
65
OpenRA.Game/GameRules/Warheads/PerCellDamageWarhead.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class PerCellDamageWarhead : DamageWarhead
|
||||
{
|
||||
[Desc("Size of the area. Damage will be applied to this area.")]
|
||||
public readonly int[] Size = { 0, 0 };
|
||||
|
||||
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
// Used by traits that damage a single actor, rather than a position
|
||||
if (target.Type == TargetType.Actor)
|
||||
DoImpact(target.Actor, firedBy, firepowerModifier);
|
||||
else
|
||||
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
var world = firedBy.World;
|
||||
var targetTile = world.Map.CellContaining(pos);
|
||||
var minRange = (Size.Length > 1 && Size[1] > 0) ? Size[1] : 0;
|
||||
var affectedTiles = world.Map.FindTilesInAnnulus(targetTile, minRange, Size[0]);
|
||||
|
||||
foreach (var t in affectedTiles)
|
||||
foreach (var victim in world.ActorMap.GetUnitsAt(t))
|
||||
DoImpact(victim, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(Actor victim, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var damage = GetDamageToInflict(victim, firedBy, firepowerModifier);
|
||||
victim.InflictDamage(firedBy, damage, this);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetDamageToInflict(Actor target, Actor firedBy, float modifier)
|
||||
{
|
||||
var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if (healthInfo == null)
|
||||
return 0;
|
||||
|
||||
var rawDamage = (float)Damage;
|
||||
|
||||
return (int)(rawDamage * modifier * EffectivenessAgainst(target.Info));
|
||||
}
|
||||
}
|
||||
}
|
||||
83
OpenRA.Game/GameRules/Warheads/SpreadDamageWarhead.cs
Normal file
83
OpenRA.Game/GameRules/Warheads/SpreadDamageWarhead.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class SpreadDamageWarhead : DamageWarhead
|
||||
{
|
||||
[Desc("For Normal DamageModel: Distance from the explosion center at which damage is 1/2.")]
|
||||
public readonly WRange Spread = new WRange(43);
|
||||
|
||||
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
// Used by traits that damage a single actor, rather than a position
|
||||
if (target.Type == TargetType.Actor)
|
||||
DoImpact(target.Actor, firedBy, firepowerModifier);
|
||||
else
|
||||
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
|
||||
}
|
||||
|
||||
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
var world = firedBy.World;
|
||||
var maxSpread = new WRange((int)(Spread.Range * (float)Math.Log(Math.Abs(Damage), 2)));
|
||||
var hitActors = world.FindActorsInCircle(pos, maxSpread);
|
||||
|
||||
foreach (var victim in hitActors)
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var damage = (int)GetDamageToInflict(pos, victim, firedBy, firepowerModifier);
|
||||
victim.InflictDamage(firedBy, damage, this);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoImpact(Actor victim, Actor firedBy, float firepowerModifier)
|
||||
{
|
||||
if (IsValidAgainst(victim, firedBy))
|
||||
{
|
||||
var damage = GetDamageToInflict(victim.CenterPosition, victim, firedBy, firepowerModifier);
|
||||
victim.InflictDamage(firedBy, damage, this);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetDamageToInflict(WPos pos, Actor target, Actor firedBy, float modifier)
|
||||
{
|
||||
var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if (healthInfo == null)
|
||||
return 0;
|
||||
|
||||
var distance = Math.Max(0, (target.CenterPosition - pos).Length - healthInfo.Radius.Range);
|
||||
var falloff = (float)GetDamageFalloff(distance * 1f / Spread.Range);
|
||||
var rawDamage = (float)(falloff * Damage);
|
||||
|
||||
return (int)(rawDamage * modifier * EffectivenessAgainst(target.Info));
|
||||
}
|
||||
|
||||
static readonly float[] falloff =
|
||||
{
|
||||
1f, 0.3678795f, 0.1353353f, 0.04978707f,
|
||||
0.01831564f, 0.006737947f, 0.002478752f, 0.000911882f
|
||||
};
|
||||
|
||||
static float GetDamageFalloff(float x)
|
||||
{
|
||||
var u = (int)x;
|
||||
if (u >= falloff.Length - 1) return 0;
|
||||
var t = x - u;
|
||||
return (falloff[u] * (1 - t)) + (falloff[u + 1] * t);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user