Merge pull request #6030 from UberWaffe/CustomWarheads
Custom Warheads refactor
This commit is contained in:
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
86
OpenRA.Mods.RA/Warheads/CreateEffectWarhead.cs
Normal file
86
OpenRA.Mods.RA/Warheads/CreateEffectWarhead.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
66
OpenRA.Mods.RA/Warheads/CreateResourceWarhead.cs
Normal file
66
OpenRA.Mods.RA/Warheads/CreateResourceWarhead.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
47
OpenRA.Mods.RA/Warheads/DestroyResourceWarhead.cs
Normal file
47
OpenRA.Mods.RA/Warheads/DestroyResourceWarhead.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
58
OpenRA.Mods.RA/Warheads/LeaveSmudgeWarhead.cs
Normal file
58
OpenRA.Mods.RA/Warheads/LeaveSmudgeWarhead.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user