Merge pull request #6030 from UberWaffe/CustomWarheads

Custom Warheads refactor
This commit is contained in:
Taryn Hill
2014-08-03 10:35:36 -05:00
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

@@ -189,7 +189,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" />
@@ -289,6 +288,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; }
}
}