Merge pull request #6030 from UberWaffe/CustomWarheads
Custom Warheads refactor
This commit is contained in:
@@ -509,10 +509,16 @@ namespace OpenRA
|
||||
|
||||
internal Func<MiniYaml, object> GetLoader(Type type)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
||||
|
||||
if (!string.IsNullOrEmpty(Loader))
|
||||
return (Func<MiniYaml, object>)Delegate.CreateDelegate(typeof(Func<MiniYaml, object>), type.GetMethod(Loader, flags));
|
||||
{
|
||||
var method = type.GetMethod(Loader, flags);
|
||||
if (method == null)
|
||||
throw new InvalidOperationException("{0} does not specify a loader function '{1}'".F(type.Name, Loader));
|
||||
|
||||
return (Func<MiniYaml, object>)Delegate.CreateDelegate(typeof(Func<MiniYaml, object>), method);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
102
OpenRA.Game/GameRules/Warhead.cs
Normal file
102
OpenRA.Game/GameRules/Warhead.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
#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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
[Desc("Base warhead class. This can be used to derive other warheads from.")]
|
||||
public abstract class Warhead
|
||||
{
|
||||
[Desc("What types of targets are affected.")]
|
||||
public readonly string[] ValidTargets = { "Air", "Ground", "Water" };
|
||||
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly string[] InvalidTargets = { };
|
||||
|
||||
[Desc("Delay in ticks before applying the warhead effect.","0 = instant (old model).")]
|
||||
public readonly int Delay = 0;
|
||||
|
||||
public abstract void DoImpact(Target target, Actor firedBy, float firepowerModifier);
|
||||
|
||||
public abstract float EffectivenessAgainst(ActorInfo ai);
|
||||
|
||||
public bool IsValidAgainst(Target target, World world, Actor firedBy)
|
||||
{
|
||||
if (target.Type == TargetType.Actor)
|
||||
return IsValidAgainst(target.Actor, firedBy);
|
||||
|
||||
if (target.Type == TargetType.FrozenActor)
|
||||
return IsValidAgainst(target.FrozenActor, firedBy);
|
||||
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
var cell = world.Map.CellContaining(target.CenterPosition);
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
var cellInfo = world.Map.GetTerrainInfo(cell);
|
||||
if (!ValidTargets.Intersect(cellInfo.TargetTypes).Any()
|
||||
|| InvalidTargets.Intersect(cellInfo.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Actor victim, Actor firedBy)
|
||||
{
|
||||
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.
|
||||
return InTargetList(victim, firedBy, ValidTargets) &&
|
||||
!InTargetList(victim, firedBy, InvalidTargets);
|
||||
}
|
||||
|
||||
public static bool InTargetList(Actor victim, Actor firedBy, string[] targetList)
|
||||
{
|
||||
if (!targetList.Any())
|
||||
return false;
|
||||
|
||||
var targetable = victim.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null)
|
||||
return false;
|
||||
if (!targetList.Intersect(targetable.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(FrozenActor victim, Actor firedBy)
|
||||
{
|
||||
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.
|
||||
return InTargetList(victim, firedBy, ValidTargets) &&
|
||||
!InTargetList(victim, firedBy, InvalidTargets);
|
||||
}
|
||||
|
||||
public static bool InTargetList(FrozenActor victim, Actor firedBy, string[] targetList)
|
||||
{
|
||||
// Frozen Actors need to be handled slightly differently. Since FrozenActor.Actor can be null if the Actor is dead.
|
||||
if (!targetList.Any())
|
||||
return false;
|
||||
|
||||
var targetable = victim.Info.Traits.GetOrDefault<ITargetableInfo>();
|
||||
if (targetable == null)
|
||||
return false;
|
||||
if (!targetList.Intersect(targetable.GetTargetTypes()).Any())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
@@ -15,83 +16,6 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class WarheadInfo
|
||||
{
|
||||
[Desc("Distance from the explosion center at which damage is 1/2.")]
|
||||
public readonly WRange Spread = new WRange(43);
|
||||
[Desc("Maximum Spread. If a value >= Spread is set, this sets a fixed maximum area of damage.")]
|
||||
public readonly WRange MaxSpread = new WRange(0);
|
||||
[FieldLoader.LoadUsing("LoadVersus")]
|
||||
[Desc("Damage vs each armortype. 0% = can't target.")]
|
||||
public readonly Dictionary<string, float> Versus;
|
||||
[Desc("Can this damage resource patches?")]
|
||||
public readonly bool DestroyResources = false;
|
||||
[Desc("Will this splatter resources and which?")]
|
||||
public readonly string AddsResourceType = null;
|
||||
[Desc("Explosion effect to use.")]
|
||||
public readonly string Explosion = null;
|
||||
[Desc("Palette to use for explosion effect.")]
|
||||
public readonly string ExplosionPalette = "effect";
|
||||
[Desc("Explosion effect on hitting water (usually a splash).")]
|
||||
public readonly string WaterExplosion = null;
|
||||
[Desc("Palette to use for effect on hitting water (usually a splash).")]
|
||||
public readonly string WaterExplosionPalette = "effect";
|
||||
[Desc("Type of smudge to apply to terrain.")]
|
||||
public readonly string[] SmudgeType = { };
|
||||
[Desc("Size of the explosion. provide 2 values for a ring effect (outer/inner).")]
|
||||
public readonly int[] Size = { 0, 0 };
|
||||
[Desc("Infantry death animation to use")]
|
||||
public readonly string InfDeath = "1";
|
||||
[Desc("Sound to play on impact.")]
|
||||
public readonly string ImpactSound = null;
|
||||
[Desc("Sound to play on impact with water")]
|
||||
public readonly string WaterImpactSound = null;
|
||||
[Desc("How much (raw) damage to deal")]
|
||||
public readonly int Damage = 0;
|
||||
[Desc("Delay in ticks before dealing the damage, 0 = instant (old model).")]
|
||||
public readonly int Delay = 0;
|
||||
[Desc("Which damage model to use.")]
|
||||
public readonly DamageModel DamageModel = DamageModel.Normal;
|
||||
[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;
|
||||
|
||||
public 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 1;
|
||||
|
||||
float versus;
|
||||
return Versus.TryGetValue(armor.Type, out versus) ? versus : 1;
|
||||
}
|
||||
|
||||
public WarheadInfo(MiniYaml yaml)
|
||||
{
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
static object LoadVersus(MiniYaml y)
|
||||
{
|
||||
var nd = y.ToDictionary();
|
||||
return nd.ContainsKey("Versus")
|
||||
? nd["Versus"].ToDictionary(my => FieldLoader.GetValue<float>("(value)", my.Value))
|
||||
: new Dictionary<string, float>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DamageModel
|
||||
{
|
||||
Normal, // classic RA damage model: point actors, distance-based falloff
|
||||
PerCell, // like RA's "nuke damage"
|
||||
HealthPercentage // for MAD Tank
|
||||
}
|
||||
|
||||
public class ProjectileArgs
|
||||
{
|
||||
public WeaponInfo Weapon;
|
||||
@@ -107,20 +31,38 @@ namespace OpenRA.GameRules
|
||||
|
||||
public class WeaponInfo
|
||||
{
|
||||
[Desc("The maximum range the weapon can fire.")]
|
||||
public readonly WRange Range = WRange.Zero;
|
||||
|
||||
[Desc("The sound played when the weapon is fired.")]
|
||||
public readonly string[] Report = null;
|
||||
[Desc("Rate of Fire")]
|
||||
|
||||
[Desc("Rate of Fire = Delay in ticks between reloading ammo magazines.")]
|
||||
public readonly int ROF = 1;
|
||||
|
||||
[Desc("Number of shots in a single ammo magazine.")]
|
||||
public readonly int Burst = 1;
|
||||
|
||||
public readonly bool Charges = false;
|
||||
|
||||
public readonly string Palette = "effect";
|
||||
|
||||
[Desc("What types of targets are affected.")]
|
||||
public readonly string[] ValidTargets = { "Ground", "Water" };
|
||||
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly string[] InvalidTargets = { };
|
||||
|
||||
[Desc("Delay in ticks between firing shots from the same ammo magazine.")]
|
||||
public readonly int BurstDelay = 5;
|
||||
|
||||
[Desc("The minimum range the weapon can fire.")]
|
||||
public readonly WRange MinRange = WRange.Zero;
|
||||
|
||||
[FieldLoader.LoadUsing("LoadProjectile")] public IProjectileInfo Projectile;
|
||||
[FieldLoader.LoadUsing("LoadWarheads")] public List<WarheadInfo> Warheads;
|
||||
[FieldLoader.LoadUsing("LoadProjectile")]
|
||||
public readonly IProjectileInfo Projectile;
|
||||
[FieldLoader.LoadUsing("LoadWarheads")]
|
||||
public readonly List<Warhead> Warheads = new List<Warhead>();
|
||||
|
||||
public WeaponInfo(string name, MiniYaml content)
|
||||
{
|
||||
@@ -139,47 +81,24 @@ namespace OpenRA.GameRules
|
||||
|
||||
static object LoadWarheads(MiniYaml yaml)
|
||||
{
|
||||
var ret = new List<WarheadInfo>();
|
||||
foreach (var w in yaml.Nodes)
|
||||
if (w.Key.Split('@')[0] == "Warhead")
|
||||
ret.Add(new WarheadInfo(w.Value));
|
||||
var retList = new List<Warhead>();
|
||||
foreach (var node in yaml.Nodes.Where(n => n.Key.StartsWith("Warhead")))
|
||||
{
|
||||
var ret = Game.CreateObject<Warhead>(node.Value.Value + "Warhead");
|
||||
FieldLoader.Load(ret, node.Value);
|
||||
retList.Add(ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return retList;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Actor a)
|
||||
{
|
||||
var targetable = a.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()
|
||||
|| InvalidTargets.Intersect(targetable.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(FrozenActor a)
|
||||
{
|
||||
var targetable = a.Info.Traits.GetOrDefault<ITargetableInfo>();
|
||||
if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()
|
||||
|| InvalidTargets.Intersect(targetable.GetTargetTypes()).Any())
|
||||
return false;
|
||||
|
||||
if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Target target, World world)
|
||||
public bool IsValidAgainst(Target target, World world, Actor firedBy)
|
||||
{
|
||||
if (target.Type == TargetType.Actor)
|
||||
return IsValidAgainst(target.Actor);
|
||||
return IsValidAgainst(target.Actor, firedBy);
|
||||
|
||||
if (target.Type == TargetType.FrozenActor)
|
||||
return IsValidAgainst(target.FrozenActor);
|
||||
return IsValidAgainst(target.FrozenActor, firedBy);
|
||||
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
@@ -197,5 +116,47 @@ namespace OpenRA.GameRules
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(Actor victim, Actor firedBy)
|
||||
{
|
||||
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
|
||||
return false;
|
||||
|
||||
var targetable = victim.TraitOrDefault<ITargetable>();
|
||||
if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()
|
||||
|| InvalidTargets.Intersect(targetable.TargetTypes).Any())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValidAgainst(FrozenActor victim, Actor firedBy)
|
||||
{
|
||||
// Frozen Actors are treated slightly differently.
|
||||
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
|
||||
return false;
|
||||
|
||||
var targetable = victim.Info.Traits.GetOrDefault<ITargetableInfo>();
|
||||
if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()
|
||||
|| InvalidTargets.Intersect(targetable.GetTargetTypes()).Any())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Impact(WPos pos, Actor firedBy, float damageModifier)
|
||||
{
|
||||
foreach (var wh in Warheads)
|
||||
{
|
||||
Action a;
|
||||
|
||||
a = () => wh.DoImpact(Target.FromPos(pos), firedBy, damageModifier);
|
||||
if (wh.Delay > 0)
|
||||
firedBy.World.AddFrameEndTask(
|
||||
w => w.Add(new DelayedAction(wh.Delay, a)));
|
||||
else
|
||||
a();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Actor.cs" />
|
||||
<Compile Include="GameRules\Warhead.cs" />
|
||||
<Compile Include="GameRules\Warheads\AbsoluteSpreadDamageWarhead.cs" />
|
||||
<Compile Include="GameRules\Warheads\DamageWarhead.cs" />
|
||||
<Compile Include="GameRules\Warheads\HealthPercentageDamageWarhead.cs" />
|
||||
<Compile Include="GameRules\Warheads\PerCellDamageWarhead.cs" />
|
||||
<Compile Include="GameRules\Warheads\SpreadDamageWarhead.cs" />
|
||||
<Compile Include="Graphics\QuadRenderer.cs" />
|
||||
<Compile Include="Download.cs" />
|
||||
<Compile Include="Effects\DelayedAction.cs" />
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace OpenRA.Traits
|
||||
nd.AppliedDamage(repairer, self, ai);
|
||||
}
|
||||
|
||||
public void InflictDamage(Actor self, Actor attacker, int damage, WarheadInfo warhead, bool ignoreModifiers)
|
||||
public void InflictDamage(Actor self, Actor attacker, int damage, DamageWarhead warhead, bool ignoreModifiers)
|
||||
{
|
||||
if (IsDead) return; /* overkill! don't count extra hits as more kills! */
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace OpenRA.Traits
|
||||
return (health == null) ? DamageState.Undamaged : health.DamageState;
|
||||
}
|
||||
|
||||
public static void InflictDamage(this Actor self, Actor attacker, int damage, WarheadInfo warhead)
|
||||
public static void InflictDamage(this Actor self, Actor attacker, int damage, DamageWarhead warhead)
|
||||
{
|
||||
if (self.Destroyed) return;
|
||||
var health = self.TraitOrDefault<Health>();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace OpenRA.Traits
|
||||
public class AttackInfo
|
||||
{
|
||||
public Actor Attacker;
|
||||
public WarheadInfo Warhead;
|
||||
public DamageWarhead Warhead;
|
||||
public int Damage;
|
||||
public DamageState DamageState;
|
||||
public DamageState PreviousDamageState;
|
||||
@@ -149,7 +149,7 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
public interface IRenderModifier { IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r); }
|
||||
public interface IDamageModifier { float GetDamageModifier(Actor attacker, WarheadInfo warhead); }
|
||||
public interface IDamageModifier { float GetDamageModifier(Actor attacker, DamageWarhead warhead); }
|
||||
public interface ISpeedModifier { decimal GetSpeedModifier(); }
|
||||
public interface IFirepowerModifier { float GetFirepowerModifier(); }
|
||||
public interface ILoadsPalettes { void LoadPalettes(WorldRenderer wr); }
|
||||
|
||||
Reference in New Issue
Block a user