Add MAD Tank and InvalidTargets field to WeaponInfo

This commit is contained in:
ScottNZ
2013-11-02 17:17:19 +13:00
parent d74c54d9f7
commit 9eff54c217
14 changed files with 265 additions and 24 deletions

View File

@@ -47,6 +47,8 @@ namespace OpenRA.GameRules
public readonly DamageModel DamageModel = DamageModel.Normal; public readonly DamageModel DamageModel = DamageModel.Normal;
[Desc("Whether we should prevent prone response for infantry.")] [Desc("Whether we should prevent prone response for infantry.")]
public readonly bool PreventProne = false; public readonly bool PreventProne = false;
[Desc("Whether to use the provided Damage value as a maximum health percentage.")]
public readonly bool ScaleDamageByTargetHealth = false;
public float EffectivenessAgainst(ActorInfo ai) public float EffectivenessAgainst(ActorInfo ai)
{ {
@@ -106,6 +108,7 @@ namespace OpenRA.GameRules
public readonly bool Charges = false; public readonly bool Charges = false;
public readonly bool Underwater = false; public readonly bool Underwater = false;
public readonly string[] ValidTargets = { "Ground", "Water" }; public readonly string[] ValidTargets = { "Ground", "Water" };
public readonly string[] InvalidTargets = { };
public readonly int BurstDelay = 5; public readonly int BurstDelay = 5;
public readonly float MinRange = 0; public readonly float MinRange = 0;
@@ -140,7 +143,8 @@ namespace OpenRA.GameRules
public bool IsValidAgainst(Actor a) public bool IsValidAgainst(Actor a)
{ {
var targetable = a.TraitOrDefault<ITargetable>(); var targetable = a.TraitOrDefault<ITargetable>();
if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()) if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()
|| InvalidTargets.Intersect(targetable.TargetTypes).Any())
return false; return false;
if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0))
@@ -152,7 +156,8 @@ namespace OpenRA.GameRules
public bool IsValidAgainst(FrozenActor a) public bool IsValidAgainst(FrozenActor a)
{ {
var targetable = a.Info.Traits.GetOrDefault<ITargetableInfo>(); var targetable = a.Info.Traits.GetOrDefault<ITargetableInfo>();
if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()) if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()
|| InvalidTargets.Intersect(targetable.GetTargetTypes()).Any())
return false; return false;
if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0))

View File

@@ -108,7 +108,7 @@ namespace OpenRA.Mods.RA
foreach (var victim in hitActors) foreach (var victim in hitActors)
{ {
var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier); var damage = (int)GetDamageToInflict(pos, victim, warhead, weapon, firepowerModifier, true);
victim.InflictDamage(firedBy, damage, warhead); victim.InflictDamage(firedBy, damage, warhead);
} }
} break; } break;
@@ -117,8 +117,10 @@ namespace OpenRA.Mods.RA
{ {
foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0]))
foreach (var unit in world.FindActorsInBox(t, t)) foreach (var unit in world.FindActorsInBox(t, t))
unit.InflictDamage(firedBy, {
(int)(warhead.Damage * warhead.EffectivenessAgainst(unit.Info)), warhead); var damage = (int)GetDamageToInflict(pos, unit, warhead, weapon, firepowerModifier, false);
unit.InflictDamage(firedBy, damage, warhead);
}
} break; } break;
} }
} }
@@ -161,21 +163,26 @@ namespace OpenRA.Mods.RA
return (falloff[u] * (1 - t)) + (falloff[u + 1] * t); return (falloff[u] * (1 - t)) + (falloff[u + 1] * t);
} }
static float GetDamageToInflict(WPos pos, Actor target, WarheadInfo warhead, WeaponInfo weapon, float modifier) 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 // don't hit air units with splash from ground explosions, etc
if (!weapon.IsValidAgainst(target)) if (!weapon.IsValidAgainst(target))
return 0f; return 0;
var health = target.Info.Traits.GetOrDefault<HealthInfo>(); var healthInfo = target.Info.Traits.GetOrDefault<HealthInfo>();
if( health == null ) return 0f; if (healthInfo == null)
return 0;
var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius); var rawDamage = (float)warhead.Damage;
var falloff = (float)GetDamageFalloff(distance / warhead.Spread); if (warhead.ScaleDamageByTargetHealth)
var rawDamage = (float)(warhead.Damage * modifier * falloff); rawDamage = (float)(rawDamage / 100 * healthInfo.HP);
var multiplier = (float)warhead.EffectivenessAgainst(target.Info); if (withFalloff)
{
return (float)(rawDamage * multiplier); var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - healthInfo.Radius);
var falloff = (float)GetDamageFalloff(distance / warhead.Spread);
rawDamage = (float)(falloff * rawDamage);
}
return (float)(rawDamage * modifier * (float)warhead.EffectivenessAgainst(target.Info));
} }
} }
} }

View File

@@ -33,14 +33,14 @@ namespace OpenRA.Mods.RA
{ {
get get
{ {
yield return new TargetTypeOrderTargeter("DemoTruck", "DemoAttack", 5, "attack", true, false) { ForceAttack = false }; yield return new TargetTypeOrderTargeter("DetonateAttack", "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new DeployOrderTargeter("DemoDeploy", 5); yield return new DeployOrderTargeter("Detonate", 5);
} }
} }
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{ {
if (order.OrderID != "DemoAttack" && order.OrderID != "DemoDeploy") if (order.OrderID != "DetonateAttack" && order.OrderID != "Detonate")
return null; return null;
if (target.Type == TargetType.FrozenActor) if (target.Type == TargetType.FrozenActor)
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.RA
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)
{ {
if (order.OrderString == "DemoAttack") if (order.OrderString == "DetonateAttack")
{ {
var target = self.ResolveFrozenActorOrder(order, Color.Red); var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor) if (target.Type != TargetType.Actor)
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.RA
self.QueueActivity(new CallFunc(() => Explode(self))); self.QueueActivity(new CallFunc(() => Explode(self)));
} }
if (order.OrderString == "DemoDeploy") else if (order.OrderString == "Detonate")
Explode(self); Explode(self);
} }
} }

160
OpenRA.Mods.RA/MadTank.cs Normal file
View File

@@ -0,0 +1,160 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.Drawing;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
using OpenRA.Mods.RA.Orders;
using OpenRA.Mods.RA.Render;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class MadTankInfo : ITraitInfo, Requires<ExplodesInfo>, Requires<RenderUnitInfo>
{
public readonly string ThumpSequence = "piston";
public readonly int ThumpInterval = 8;
[WeaponReference]
public readonly string ThumpDamageWeapon = "MADTankThump";
public readonly int ThumpShakeIntensity = 3;
public readonly float2 ThumpShakeMultiplier = new float2(1, 0);
public readonly int ThumpShakeTime = 10;
public readonly int ChargeDelay = 96;
public readonly string ChargeSound = "madchrg2.aud";
public readonly int DetonationDelay = 42;
public readonly string DetonationSound = "madexplo.aud";
[WeaponReference]
public readonly string DetonationWeapon = "MADTankDetonate";
[ActorReference]
public readonly string DriverActor = "e1";
public object Create(ActorInitializer init) { return new MadTank(init.self, this); }
}
class MadTank : IIssueOrder, IResolveOrder, IOrderVoice, ITick
{
readonly Actor self;
readonly MadTankInfo info;
readonly RenderUnit renderUnit;
readonly ScreenShaker screenShaker;
bool deployed;
int tick;
public MadTank(Actor self, MadTankInfo info)
{
this.self = self;
this.info = info;
renderUnit = self.Trait<RenderUnit>();
screenShaker = self.World.WorldActor.Trait<ScreenShaker>();
}
public void Tick(Actor self)
{
if (!deployed)
return;
if (++tick >= info.ThumpInterval)
{
if (info.ThumpDamageWeapon != null)
Combat.DoExplosion(self, info.ThumpDamageWeapon, self.CenterPosition);
screenShaker.AddEffect(info.ThumpShakeTime, self.CenterPosition, info.ThumpShakeIntensity, info.ThumpShakeMultiplier);
tick = 0;
}
}
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new TargetTypeOrderTargeter("DetonateAttack", "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new DeployOrderTargeter("Detonate", 5);
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID != "DetonateAttack" && order.OrderID != "Detonate")
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return "Attack";
}
void Detonate()
{
self.World.AddFrameEndTask(w =>
{
if (info.DetonationWeapon != null)
Combat.DoExplosion(self, info.DetonationWeapon, self.CenterPosition);
self.Kill(self);
});
}
void EjectDriver()
{
var driver = self.World.CreateActor(info.DriverActor.ToLowerInvariant(), new TypeDictionary
{
new LocationInit(self.Location),
new OwnerInit(self.Owner)
});
var driverMobile = driver.TraitOrDefault<Mobile>();
if (driverMobile != null)
driverMobile.Nudge(driver, driver, true);
}
void StartDetonationSequence()
{
self.World.AddFrameEndTask(w => EjectDriver());
if (info.ThumpSequence != null)
renderUnit.PlayCustomAnimRepeating(self, info.ThumpSequence);
deployed = true;
self.QueueActivity(new Wait(info.ChargeDelay, false));
self.QueueActivity(new CallFunc(() => Sound.Play(info.ChargeSound, self.CenterPosition)));
self.QueueActivity(new Wait(info.DetonationDelay, false));
self.QueueActivity(new CallFunc(() => Sound.Play(info.DetonationSound, self.CenterPosition)));
self.QueueActivity(new CallFunc(Detonate));
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "DetonateAttack")
{
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new MoveAdjacentTo(target));
self.QueueActivity(new CallFunc(StartDetonationSequence));
}
else if (order.OrderString == "Detonate")
{
self.CancelActivity();
self.QueueActivity(new CallFunc(StartDetonationSequence));
}
}
}
}

View File

@@ -251,6 +251,7 @@
<Compile Include="Lint\CheckSyncAnnotations.cs" /> <Compile Include="Lint\CheckSyncAnnotations.cs" />
<Compile Include="Lint\CheckTraitPrerequisites.cs" /> <Compile Include="Lint\CheckTraitPrerequisites.cs" />
<Compile Include="Lint\LintBuildablePrerequisites.cs" /> <Compile Include="Lint\LintBuildablePrerequisites.cs" />
<Compile Include="MadTank.cs" />
<Compile Include="Missions\DesertShellmapScript.cs" /> <Compile Include="Missions\DesertShellmapScript.cs" />
<Compile Include="Missions\FortLonestarScript.cs" /> <Compile Include="Missions\FortLonestarScript.cs" />
<Compile Include="Missions\Survival02Script.cs" /> <Compile Include="Missions\Survival02Script.cs" />

View File

@@ -129,7 +129,7 @@ namespace OpenRA.Mods.RA.Widgets
PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false)); PerformKeyboardOrderOnSelection(a => new Order("ReturnToBase", a, false));
PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false)); PerformKeyboardOrderOnSelection(a => new Order("DeployTransform", a, false));
PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false)); PerformKeyboardOrderOnSelection(a => new Order("Unload", a, false));
PerformKeyboardOrderOnSelection(a => new Order("DemoDeploy", a, false)); PerformKeyboardOrderOnSelection(a => new Order("Detonate", a, false));
return true; return true;
} }

BIN
mods/ra/bits/madchrg2.aud Normal file

Binary file not shown.

BIN
mods/ra/bits/madexplo.aud Normal file

Binary file not shown.

BIN
mods/ra/bits/qtnk.shp Normal file

Binary file not shown.

BIN
mods/ra/bits/qtnkicon.shp Normal file

Binary file not shown.

View File

@@ -215,7 +215,7 @@
Selectable: Selectable:
Priority: 3 Priority: 3
TargetableBuilding: TargetableBuilding:
TargetTypes: Ground, C4, DemoTruck TargetTypes: Ground, C4, DetonateAttack
Building: Building:
Dimensions: 1,1 Dimensions: 1,1
Footprint: x Footprint: x
@@ -272,7 +272,7 @@
Selectable: Selectable:
Priority: 1 Priority: 1
TargetableBuilding: TargetableBuilding:
TargetTypes: Ground, C4, DemoTruck TargetTypes: Ground, C4, DetonateAttack
RenderBuildingWall: RenderBuildingWall:
HasMakeAnimation: false HasMakeAnimation: false
Palette: terrain Palette: terrain
@@ -323,7 +323,7 @@
Armor: Armor:
Type: Light Type: Light
TargetableBuilding: TargetableBuilding:
TargetTypes: Ground, DemoTruck TargetTypes: Ground, DetonateAttack
^CivInfantry: ^CivInfantry:
Inherits: ^Infantry Inherits: ^Infantry

View File

@@ -766,3 +766,37 @@ CTNK:
LocalYaw: -100 LocalYaw: -100
AttackFrontal: AttackFrontal:
ChronoshiftDeploy: ChronoshiftDeploy:
QTNK:
Inherits: ^Tank
Buildable:
Queue: Vehicle
BuildPaletteOrder: 150
Prerequisites: fix,stek
Owner: soviet
Hotkey: q
Valued:
Cost: 2500
Tooltip:
Name: MAD Tank
Description: Deals seismic damage to nearby vehicles\nand structures.\n Strong vs Vehicles, Buildings\n Weak vs Infantry
Health:
HP: 900
Armor:
Type: Heavy
Mobile:
Speed: 4
Crushes: wall, atmine, crate, infantry
RevealsShroud:
Range: 6
Selectable:
Bounds: 44,38,0,-4
RenderUnit:
AttackMove:
JustMove: True
Explodes:
Weapon: UnitExplodeSmall
MadTank:
-EjectOnDeath:
TargetableUnit:
TargetTypes: Ground, MADTank

View File

@@ -253,4 +253,15 @@ ctnk:
Start: 0 Start: 0
Length: 5 Length: 5
icon: ctnkicon icon: ctnkicon
Start: 0
qtnk:
idle:
Start: 0
Facings: 32
piston:
Start: 32
Facings: 8
Length: 8
icon: qtnkicon
Start: 0 Start: 0

View File

@@ -1327,3 +1327,26 @@ Mandible:
Concrete: 10% Concrete: 10%
InfDeath: 1 InfDeath: 1
Damage: 60 Damage: 60
MADTankThump:
InvalidTargets: MADTank
Warhead:
DamageModel: PerCell
Damage: 1
ScaleDamageByTargetHealth: True
Versus:
None: 0%
Size: 7,6
MADTankDetonate:
InvalidTargets: MADTank
Warhead:
DamageModel: PerCell
Damage: 19
ScaleDamageByTargetHealth: True
Versus:
None: 0%
Size: 7,6
Explosion: med_explosion
ImpactSound: mineblo1.aud
SmudgeType: Crater