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:
UberWaffe
2014-07-09 17:58:06 +02:00
parent f84b1c145e
commit c972b39687
59 changed files with 2275 additions and 1233 deletions

View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq;
using AI.Fuzzy.Library;
using OpenRA.Mods.RA.Move;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI
@@ -195,8 +196,11 @@ namespace OpenRA.Mods.RA.AI
var sumOfDamage = 0;
var arms = a.TraitsImplementing<Armament>();
foreach (var arm in arms)
if (arm.Weapon.Warheads[0] != null)
sumOfDamage += arm.Weapon.Warheads[0].Damage;
{
var warhead = arm.Weapon.Warheads.Select(w => w as DamageWarhead).FirstOrDefault(w => w != null);
if (warhead != null)
sumOfDamage += warhead.Damage;
}
return sumOfDamage;
});
}

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA.Activities
mobile.IsMoving = false;
self.World.ActorMap.GetUnitsAt(mobile.toCell, mobile.toSubCell)
.Except(new []{self}).Where(t => weapon.IsValidAgainst(t))
.Except(new []{self}).Where(t => weapon.IsValidAgainst(t, self))
.Do(t => t.Kill(self));
return NextActivity;

View File

@@ -8,6 +8,7 @@
*/
#endregion
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Air
@@ -51,7 +52,10 @@ namespace OpenRA.Mods.RA.Air
if (self.CenterPosition.Z <= 0)
{
if (info.Explosion != null)
Combat.DoExplosion(self, info.Explosion, self.CenterPosition);
{
var weapon = self.World.Map.Rules.Weapons[info.Explosion.ToLowerInvariant()];
weapon.Impact(self.CenterPosition, self, 1f);
}
self.Destroy();
return null;

View File

@@ -142,7 +142,7 @@ namespace OpenRA.Mods.RA
if (Weapon.MinRange != WRange.Zero && target.IsInRange(self.CenterPosition, Weapon.MinRange))
return null;
if (!Weapon.IsValidAgainst(target, self.World))
if (!Weapon.IsValidAgainst(target, self.World, self))
return null;
var barrel = Barrels[Burst % Barrels.Length];

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Traits;
@@ -87,11 +88,11 @@ namespace OpenRA.Mods.RA
{
get
{
var armament = Armaments.FirstOrDefault();
var armament = Armaments.FirstOrDefault(a => a.Weapon.Warheads.Any(w => (w is DamageWarhead)));
if (armament == null)
yield break;
var negativeDamage = armament.Weapon.Warheads[0].Damage < 0;
var negativeDamage = (armament.Weapon.Warheads.FirstOrDefault(w => (w is DamageWarhead)) as DamageWarhead).Damage < 0;
yield return new AttackOrderTargeter(this, "Attack", 6, negativeDamage);
}
}
@@ -134,13 +135,13 @@ namespace OpenRA.Mods.RA
public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World)); }
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World, self)); }
public WRange GetMaximumRange()
{
return Armaments.Select(a => a.Weapon.Range).Append(WRange.Zero).Max();
}
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World)); }
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World, self)); }
public void AttackTarget(Target target, bool queued, bool allowMove)
{

View File

@@ -22,6 +22,8 @@ namespace OpenRA.Mods.RA
[Desc("How many game ticks should pass before closing the actor's turret.")]
public int CloseDelay = 125;
public int DefaultFacing = 0;
[Desc("The factor damage received is multiplied by while this actor is closed.")]
public float ClosedDamageMultiplier = 0.5f;
public override object Create(ActorInitializer init) { return new AttackPopupTurreted(init, this); }
@@ -103,7 +105,7 @@ namespace OpenRA.Mods.RA
}
}
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return state == PopupState.Closed ? info.ClosedDamageMultiplier : 1f;
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
@@ -294,7 +295,8 @@ namespace OpenRA.Mods.RA
var initialDamage = Health.DamageState;
self.World.AddFrameEndTask(w =>
{
Combat.DoExplosion(saboteur, "Demolish", self.CenterPosition);
var weapon = saboteur.World.Map.Rules.Weapons["demolish"];
weapon.Impact(self.CenterPosition, saboteur, 1f);
self.World.WorldActor.Trait<ScreenShaker>().AddEffect(15, self.CenterPosition, 6);
self.Kill(saboteur);
});

View File

@@ -1,232 +0,0 @@
#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.GameRules;
using OpenRA.Mods.RA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
// some utility bits that are shared between various things
public static class Combat
{
static string GetImpactSound(WarheadInfo warhead, bool isWater)
{
if (isWater && warhead.WaterImpactSound != null)
return warhead.WaterImpactSound;
if (warhead.ImpactSound != null)
return warhead.ImpactSound;
return null;
}
public static void DoImpact(WPos pos, WarheadInfo warhead, WeaponInfo weapon, Actor firedBy, float firepowerModifier)
{
var world = firedBy.World;
var targetTile = world.Map.CellContaining(pos);
if (!world.Map.Contains(targetTile))
return;
var isWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater;
var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion;
var explosionTypePalette = isWater ? warhead.WaterExplosionPalette : warhead.ExplosionPalette;
if (explosionType != null)
world.AddFrameEndTask(w => w.Add(new Explosion(w, pos, explosionType, explosionTypePalette)));
Sound.Play(GetImpactSound(warhead, isWater), pos);
var smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type);
var resLayer = warhead.DestroyResources || !string.IsNullOrEmpty(warhead.AddsResourceType) ? world.WorldActor.Trait<ResourceLayer>() : null;
if (warhead.Size[0] > 0)
{
var allCells = world.Map.FindTilesInCircle(targetTile, warhead.Size[0]).ToList();
// `smudgeCells` might want to just be an outer shell of the cells:
IEnumerable<CPos> smudgeCells = allCells;
if (warhead.Size.Length == 2)
smudgeCells = smudgeCells.Except(world.Map.FindTilesInCircle(targetTile, warhead.Size[1]));
// Draw the smudges:
foreach (var sc in smudgeCells)
{
var smudgeType = world.Map.GetTerrainInfo(sc).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t));
if (smudgeType == null) continue;
SmudgeLayer smudgeLayer;
if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer))
throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType));
smudgeLayer.AddSmudge(sc);
if (warhead.DestroyResources)
resLayer.Destroy(sc);
}
// Destroy all resources in range, not just the outer shell:
if (warhead.DestroyResources)
foreach (var cell in allCells)
resLayer.Destroy(cell);
// Splatter resources:
if (!string.IsNullOrEmpty(warhead.AddsResourceType))
{
var resourceType = world.WorldActor.TraitsImplementing<ResourceType>()
.FirstOrDefault(t => t.Info.Name == warhead.AddsResourceType);
if (resourceType == null)
Log.Write("debug", "Warhead defines an invalid resource type '{0}'".F(warhead.AddsResourceType));
else
{
foreach (var cell in allCells)
{
if (!resLayer.CanSpawnResourceAt(resourceType, cell))
continue;
var splash = world.SharedRandom.Next(1, resourceType.Info.MaxDensity - resLayer.GetResourceDensity(cell));
resLayer.AddResource(resourceType, cell, splash);
}
}
}
}
else
{
var smudgeType = world.Map.GetTerrainInfo(targetTile).AcceptsSmudgeType.FirstOrDefault(t => warhead.SmudgeType.Contains(t));
if (smudgeType != null)
{
SmudgeLayer smudgeLayer;
if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer))
throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType));
smudgeLayer.AddSmudge(targetTile);
}
}
if (warhead.DestroyResources)
world.WorldActor.Trait<ResourceLayer>().Destroy(targetTile);
switch (warhead.DamageModel)
{
case DamageModel.Normal:
{
var spreadMax = warhead.MaxSpread.Range;
var maxSpreadCalculation = spreadMax >= warhead.Spread.Range ? spreadMax : (warhead.Spread.Range * (float)Math.Log(Math.Abs(warhead.Damage), 2));
var maxSpread = new WRange((int)(maxSpreadCalculation));
var hitActors = world.FindActorsInCircle(pos, maxSpread);
foreach (var victim in hitActors)
{
var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, true);
victim.InflictDamage(firedBy, damage, warhead);
}
}
break;
case DamageModel.PerCell:
{
foreach (var t in world.Map.FindTilesInCircle(targetTile, warhead.Size[0]))
{
foreach (var unit in world.ActorMap.GetUnitsAt(t))
{
var damage = (int)GetDamageToInflict(pos, unit, warhead, weapon, firepowerModifier, false);
unit.InflictDamage(firedBy, damage, warhead);
}
}
}
break;
case DamageModel.HealthPercentage:
{
var range = new WRange(warhead.Size[0] * 1024);
var hitActors = world.FindActorsInCircle(pos, range);
foreach (var victim in hitActors)
{
var damage = GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, false);
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, warhead);
}
}
break;
}
}
public static void DoImpacts(WPos pos, Actor firedBy, WeaponInfo weapon, float damageModifier)
{
foreach (var wh in weapon.Warheads)
{
var warhead = wh;
Action a = () => DoImpact(pos, warhead, weapon, firedBy, damageModifier);
if (warhead.Delay > 0)
firedBy.World.AddFrameEndTask(
w => w.Add(new DelayedAction(warhead.Delay, a)));
else
a();
}
}
public static void DoExplosion(Actor attacker, string weapontype, WPos pos)
{
var weapon = attacker.World.Map.Rules.Weapons[weapontype.ToLowerInvariant()];
if (weapon.Report != null && weapon.Report.Any())
Sound.Play(weapon.Report.Random(attacker.World.SharedRandom), pos);
DoImpacts(pos, attacker, weapon, 1f);
}
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);
}
static float GetDamageToInflict(WPos pos, Actor target, WarheadInfo warhead, WeaponInfo weapon, float modifier, bool withFalloff)
{
// don't hit air units with splash from ground explosions, etc
if (!weapon.IsValidAgainst(target))
return 0;
var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
if (healthInfo == null)
return 0;
var rawDamage = (float)warhead.Damage;
if (withFalloff)
{
var distance = Math.Max(0, (target.CenterPosition - pos).Length - healthInfo.Radius.Range);
var falloff = (float)GetDamageFalloff(distance * 1f / warhead.Spread.Range);
rawDamage = (float)(falloff * rawDamage);
}
return (float)(rawDamage * modifier * (float)warhead.EffectivenessAgainst(target.Info));
}
}
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -28,7 +29,8 @@ namespace OpenRA.Mods.RA
public override void Activate(Actor collector)
{
Combat.DoExplosion(self, ((ExplodeCrateActionInfo)info).Weapon, collector.CenterPosition);
var weapon = self.World.Map.Rules.Weapons[((ExplodeCrateActionInfo)info).Weapon.ToLowerInvariant()];
weapon.Impact(collector.CenterPosition, self, 1f);
base.Activate(collector);
}
}

View File

@@ -172,7 +172,7 @@ namespace OpenRA.Mods.RA.Effects
world.AddFrameEndTask(w => w.Remove(this));
Combat.DoImpacts(pos, args.SourceActor, args.Weapon, args.FirepowerModifier);
args.Weapon.Impact(pos, args.SourceActor, args.FirepowerModifier);
}
}
}

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Mods.RA.Effects
{
pos += new WVec(0, 0, args.PassiveTarget.Z - pos.Z);
world.AddFrameEndTask(w => w.Remove(this));
Combat.DoImpacts(pos, args.SourceActor, args.Weapon, args.FirepowerModifier);
args.Weapon.Impact(pos, args.SourceActor, args.FirepowerModifier);
}
anim.Tick();

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Mods.RA.Effects
if (hitanim != null)
hitanim.PlayThen("idle", () => animationComplete = true);
Combat.DoImpacts(target, args.SourceActor, args.Weapon, args.FirepowerModifier);
args.Weapon.Impact(target, args.SourceActor, args.FirepowerModifier);
doneDamage = true;
}

View File

@@ -180,7 +180,7 @@ namespace OpenRA.Mods.RA.Effects
if (ticks <= info.Arm)
return;
Combat.DoImpacts(pos, args.SourceActor, args.Weapon, args.FirepowerModifier);
args.Weapon.Impact(pos, args.SourceActor, args.FirepowerModifier);
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
@@ -79,7 +80,8 @@ namespace OpenRA.Mods.RA.Effects
void Explode(World world)
{
world.AddFrameEndTask(w => w.Remove(this));
Combat.DoExplosion(firedBy.PlayerActor, weapon, pos);
var weapon = world.Map.Rules.Weapons[this.weapon.ToLowerInvariant()];
weapon.Impact(pos, firedBy.PlayerActor, 1f);
world.WorldActor.Trait<ScreenShaker>().AddEffect(20, pos, 5);
foreach (var a in world.ActorsWithTrait<NukePaletteEffect>())

View File

@@ -47,7 +47,7 @@ namespace OpenRA.Mods.RA.Effects
if (!doneDamage)
{
var pos = Args.GuidedTarget.IsValidFor(Args.SourceActor) ? Args.GuidedTarget.CenterPosition : Args.PassiveTarget;
Combat.DoImpacts(pos, Args.SourceActor, Args.Weapon, Args.FirepowerModifier);
Args.Weapon.Impact(pos, Args.SourceActor, Args.FirepowerModifier);
doneDamage = true;
}
}

View File

@@ -9,6 +9,7 @@
#endregion
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -43,9 +44,15 @@ namespace OpenRA.Mods.RA
if (explodesInfo.InfDeath != null && e.Warhead != null && !explodesInfo.InfDeath.Contains(e.Warhead.InfDeath))
return;
var weapon = ChooseWeaponForExplosion(self);
if (weapon != null)
Combat.DoExplosion(e.Attacker, weapon, self.CenterPosition);
var weaponName = ChooseWeaponForExplosion(self);
if (weaponName != null)
{
var weapon = e.Attacker.World.Map.Rules.Weapons[weaponName.ToLowerInvariant()];
if (weapon.Report != null && weapon.Report.Any())
Sound.Play(weapon.Report.Random(e.Attacker.World.SharedRandom), self.CenterPosition);
weapon.Impact(self.CenterPosition, e.Attacker, 1f);
}
}
string ChooseWeaponForExplosion(Actor self)

View File

@@ -82,7 +82,7 @@ namespace OpenRA.Mods.RA
}
}
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return Level > 0 ? 1 / info.ArmorModifier[Level - 1] : 1;
}

View File

@@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA
return FirepowerLevel > 0 ? (1 + FirepowerLevel * info.FirepowerModifier) : 1;
}
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return ArmorLevel > 0 ? (1 / (1 + ArmorLevel * info.ArmorModifier)) : 1;
}

View File

@@ -18,6 +18,6 @@ namespace OpenRA.Mods.RA
class Invulnerable : IDamageModifier
{
public float GetDamageModifier(Actor attacker, WarheadInfo warhead) { return 0.0f; }
public float GetDamageModifier(Actor attacker, DamageWarhead warhead) { return 0.0f; }
}
}

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.RA
RemainingTicks--;
}
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return (RemainingTicks > 0) ? 0.0f : 1.0f;
}

View File

@@ -10,6 +10,7 @@
using System.Collections.Generic;
using System.Drawing;
using OpenRA.GameRules;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
using OpenRA.Mods.RA.Orders;
@@ -68,7 +69,10 @@ namespace OpenRA.Mods.RA
if (++tick >= info.ThumpInterval)
{
if (info.ThumpDamageWeapon != null)
Combat.DoExplosion(self, info.ThumpDamageWeapon, self.CenterPosition);
{
var weapon = self.World.Map.Rules.Weapons[info.ThumpDamageWeapon.ToLowerInvariant()];
weapon.Impact(self.CenterPosition, self, 1f);
}
screenShaker.AddEffect(info.ThumpShakeTime, self.CenterPosition, info.ThumpShakeIntensity, info.ThumpShakeMultiplier);
tick = 0;
}
@@ -104,7 +108,10 @@ namespace OpenRA.Mods.RA
self.World.AddFrameEndTask(w =>
{
if (info.DetonationWeapon != null)
Combat.DoExplosion(self, info.DetonationWeapon, self.CenterPosition);
{
var weapon = self.World.Map.Rules.Weapons[info.DetonationWeapon.ToLowerInvariant()];
weapon.Impact(self.CenterPosition, self, 1f);
}
self.Kill(self);
});
}

View File

@@ -188,7 +188,6 @@
<Compile Include="ChronoshiftPaletteEffect.cs" />
<Compile Include="Chronoshiftable.cs" />
<Compile Include="Cloak.cs" />
<Compile Include="Combat.cs" />
<Compile Include="ConquestVictoryConditions.cs" />
<Compile Include="ContainsCrate.cs" />
<Compile Include="Crate.cs" />
@@ -288,6 +287,10 @@
<Compile Include="Player\ProductionQueue.cs" />
<Compile Include="Player\ProvidesTechPrerequisite.cs" />
<Compile Include="PortableChrono.cs" />
<Compile Include="Warheads\DestroyResourceWarhead.cs" />
<Compile Include="Warheads\CreateEffectWarhead.cs" />
<Compile Include="Warheads\CreateResourceWarhead.cs" />
<Compile Include="Warheads\LeaveSmudgeWarhead.cs" />
<Compile Include="World\RadarPings.cs" />
<Compile Include="Player\TechTree.cs" />
<Compile Include="PrimaryBuilding.cs" />

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA
{
public bool Invulnerable = false;
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return Invulnerable ? 0.0f : 1.0f;
}

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.RA
LocalOffset = WVec.Zero;
}
public float GetDamageModifier(Actor attacker, WarheadInfo warhead)
public float GetDamageModifier(Actor attacker, DamageWarhead warhead)
{
return IsProne && warhead != null ? warhead.ProneModifier / 100f : 1f;
}

View File

@@ -0,0 +1,86 @@
#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.GameRules;
using OpenRA.Traits;
using OpenRA.Mods.RA.Effects;
namespace OpenRA.Mods.RA
{
public class CreateEffectWarhead : Warhead
{
[Desc("Size of the area. An explosion animation will be created in each tile.", "Provide 2 values for a ring effect (outer/inner).")]
public readonly int[] Size = { 0, 0 };
[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("Sound to play on impact.")]
public readonly string ImpactSound = null;
[Desc("Sound to play on impact with water")]
public readonly string WaterImpactSound = null;
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
{
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
}
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
{
var world = firedBy.World;
var targetTile = world.Map.CellContaining(pos);
if (!world.Map.Contains(targetTile))
return;
var minRange = (Size.Length > 1 && Size[1] > 0) ? Size[1] : 0;
var allCells = world.Map.FindTilesInAnnulus(targetTile, minRange, Size[0]);
// Draw the effects
foreach (var currentCell in allCells)
{
var currentPos = world.Map.CenterOfCell(currentCell);
// TODO: #5937 should go in here after rebase.
var isWater = currentPos.Z <= 0 && world.Map.GetTerrainInfo(currentCell).IsWater;
var explosionType = isWater ? WaterExplosion : Explosion;
var explosionTypePalette = isWater ? WaterExplosionPalette : ExplosionPalette;
if (explosionType != null)
world.AddFrameEndTask(w => w.Add(new Explosion(w, currentPos, explosionType, explosionTypePalette)));
}
string sound = null;
var isTargetWater = pos.Z <= 0 && world.Map.GetTerrainInfo(targetTile).IsWater;
if (isTargetWater && WaterImpactSound != null)
sound = WaterImpactSound;
if (ImpactSound != null)
sound = ImpactSound;
Sound.Play(sound, pos);
}
public override float EffectivenessAgainst(ActorInfo ai) { return 1f; }
}
}

View File

@@ -0,0 +1,66 @@
#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.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class CreateResourceWarhead : Warhead
{
[Desc("Size of the area. The resources are seeded within this area.", "Provide 2 values for a ring effect (outer/inner).")]
public readonly int[] Size = { 0, 0 };
[Desc("Will this splatter resources and which?")]
public readonly string AddsResourceType = null;
// TODO: Allow maximum resource splatter to be defined. (Per tile, and in total).
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
{
DoImpact(target.CenterPosition, firedBy, firepowerModifier);
}
public void DoImpact(WPos pos, Actor firedBy, float firepowerModifier)
{
if (string.IsNullOrEmpty(AddsResourceType))
return;
var world = firedBy.World;
var targetTile = world.Map.CellContaining(pos);
var resLayer = world.WorldActor.Trait<ResourceLayer>();
var minRange = (Size.Length > 1 && Size[1] > 0) ? Size[1] : 0;
var allCells = world.Map.FindTilesInAnnulus(targetTile, minRange, Size[0]);
var resourceType = world.WorldActor.TraitsImplementing<ResourceType>()
.FirstOrDefault(t => t.Info.Name == AddsResourceType);
if (resourceType == null)
Log.Write("debug", "Warhead defines an invalid resource type '{0}'".F(AddsResourceType));
else
{
foreach (var cell in allCells)
{
if (!resLayer.CanSpawnResourceAt(resourceType, cell))
continue;
var splash = world.SharedRandom.Next(1, resourceType.Info.MaxDensity - resLayer.GetResourceDensity(cell));
resLayer.AddResource(resourceType, cell, splash);
}
}
}
public override float EffectivenessAgainst(ActorInfo ai) { return 1f; }
}
}

View File

@@ -0,0 +1,47 @@
#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.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class DestroyResourceWarhead : Warhead
{
[Desc("Size of the area. The resources are seeded within this area.", "Provide 2 values for a ring effect (outer/inner).")]
public readonly int[] Size = { 0, 0 };
// TODO: Allow maximum resource removal to be defined. (Per tile, and in total).
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
{
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 resLayer = world.WorldActor.Trait<ResourceLayer>();
var minRange = (Size.Length > 1 && Size[1] > 0) ? Size[1] : 0;
var allCells = world.Map.FindTilesInAnnulus(targetTile, minRange, Size[0]);
// Destroy all resources in the selected tiles
foreach (var cell in allCells)
resLayer.Destroy(cell);
}
public override float EffectivenessAgainst(ActorInfo ai) { return 1f; }
}
}

View File

@@ -0,0 +1,58 @@
#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.GameRules;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class LeaveSmudgeWarhead : Warhead
{
[Desc("Size of the area. A smudge will be created in each tile.", "Provide 2 values for a ring effect (outer/inner).")]
public readonly int[] Size = { 0, 0 };
[Desc("Type of smudge to apply to terrain.")]
public readonly string[] SmudgeType = { };
public override void DoImpact(Target target, Actor firedBy, float firepowerModifier)
{
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 smudgeLayers = world.WorldActor.TraitsImplementing<SmudgeLayer>().ToDictionary(x => x.Info.Type);
var minRange = (Size.Length > 1 && Size[1] > 0) ? Size[1] : 0;
var allCells = world.Map.FindTilesInAnnulus(targetTile, minRange, Size[0]);
// Draw the smudges:
foreach (var sc in allCells)
{
var smudgeType = world.Map.GetTerrainInfo(sc).AcceptsSmudgeType.FirstOrDefault(t => SmudgeType.Contains(t));
if (smudgeType == null) continue;
SmudgeLayer smudgeLayer;
if (!smudgeLayers.TryGetValue(smudgeType, out smudgeLayer))
throw new NotImplementedException("Unknown smudge type `{0}`".F(smudgeType));
smudgeLayer.AddSmudge(sc);
}
}
public override float EffectivenessAgainst(ActorInfo ai) { return 1f; }
}
}