diff --git a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
index 92dd5fc7d9..fd3357dda1 100644
--- a/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
+++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj
@@ -78,7 +78,6 @@
-
diff --git a/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs b/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs
deleted file mode 100644
index e0e00986cb..0000000000
--- a/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#region Copyright & License Information
-/*
- * Copyright 2007-2016 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, either version 3 of
- * the License, or (at your option) any later version. For more
- * information, see COPYING.
- */
-#endregion
-
-using System.Collections.Generic;
-using System.Linq;
-using OpenRA.GameRules;
-using OpenRA.Mods.Common.Traits;
-using OpenRA.Traits;
-
-namespace OpenRA.Mods.Cnc.Traits
-{
- class PoisonedByTiberiumInfo : UpgradableTraitInfo, IRulesetLoaded
- {
- [WeaponReference] public readonly string Weapon = "Tiberium";
- public readonly HashSet Resources = new HashSet { "Tiberium", "BlueTiberium" };
-
- public WeaponInfo WeaponInfo { get; private set; }
-
- public override object Create(ActorInitializer init) { return new PoisonedByTiberium(init, this); }
- public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; }
- }
-
- class PoisonedByTiberium : UpgradableTrait, ITick, ISync
- {
- readonly ResourceLayer rl;
- [Sync] int poisonTicks;
-
- public PoisonedByTiberium(ActorInitializer init, PoisonedByTiberiumInfo info)
- : base(info)
- {
- rl = init.Self.World.WorldActor.Trait();
- }
-
- public void Tick(Actor self)
- {
- if (IsTraitDisabled || --poisonTicks > 0)
- return;
-
- // Prevents harming infantry in cargo.
- if (!self.IsInWorld)
- return;
-
- var r = rl.GetResource(self.Location);
- if (r == null || !Info.Resources.Contains(r.Info.Name))
- return;
-
- Info.WeaponInfo.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty());
- poisonTicks = Info.WeaponInfo.ReloadDelay;
- }
- }
-}
diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index fa44f8d02b..44835881df 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -326,6 +326,7 @@
+
diff --git a/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs b/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs
new file mode 100644
index 0000000000..9106d4ef9f
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/DamagedByTerrain.cs
@@ -0,0 +1,90 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using System.Linq;
+using OpenRA.GameRules;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("This actor receives damage from the given weapon when on the specified terrain type.")]
+ class DamagedByTerrainInfo : UpgradableTraitInfo, IRulesetLoaded, Requires
+ {
+ [Desc("The weapon which is used to damage the actor.")]
+ [WeaponReference, FieldLoader.Require] public readonly string Weapon;
+
+ [Desc("Terrain types where the actor will take damage.")]
+ [FieldLoader.Require] public readonly string[] Terrain = { };
+
+ [Desc("Percentage health below which the actor will not receive further damage.")]
+ public readonly int DamageThreshold = 0;
+
+ [Desc("Inflict damage down to the DamageThreshold when the actor gets created on damaging terrain.")]
+ public readonly bool StartOnThreshold = false;
+
+ public WeaponInfo WeaponInfo { get; private set; }
+
+ public override object Create(ActorInitializer init) { return new DamagedByTerrain(init.Self, this); }
+ public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; }
+ }
+
+ class DamagedByTerrain : UpgradableTrait, ITick, ISync, INotifyAddedToWorld
+ {
+ readonly Health health;
+
+ [Sync] int damageTicks;
+ [Sync] int damageThreshold;
+
+ public DamagedByTerrain(Actor self, DamagedByTerrainInfo info) : base(info)
+ {
+ health = self.Trait();
+ }
+
+ public void AddedToWorld(Actor self)
+ {
+ if (!Info.StartOnThreshold)
+ return;
+
+ var safeTiles = 0;
+ var totalTiles = 0;
+ foreach (var kv in self.OccupiesSpace.OccupiedCells())
+ {
+ totalTiles++;
+ if (!Info.Terrain.Contains(self.World.Map.GetTerrainInfo(kv.First).Type))
+ safeTiles++;
+ }
+
+ damageThreshold = (Info.DamageThreshold * health.MaxHP + (100 - Info.DamageThreshold) * safeTiles * health.MaxHP / totalTiles) / 100;
+
+ // Actors start with maximum damage applied
+ var delta = health.HP - damageThreshold;
+ if (delta > 0)
+ health.InflictDamage(self, self.World.WorldActor, delta, null, false);
+ }
+
+ public void Tick(Actor self)
+ {
+ if (IsTraitDisabled || health.HP <= damageThreshold || --damageTicks > 0)
+ return;
+
+ // Prevents harming cargo.
+ if (!self.IsInWorld)
+ return;
+
+ var t = self.World.Map.GetTerrainInfo(self.Location);
+ if (!Info.Terrain.Contains(t.Type))
+ return;
+
+ Info.WeaponInfo.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty());
+ damageTicks = Info.WeaponInfo.ReloadDelay;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
index 48aebb832f..517fd62a40 100644
--- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs
@@ -222,6 +222,40 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
+ if (engineVersion < 20160704)
+ {
+ if (node.Key.Contains("PoisonedByTiberium"))
+ {
+ node.Key = node.Key.Replace("PoisonedByTiberium", "DamagedByTerrain");
+ if (!node.Key.StartsWith("-"))
+ {
+ if (node.Value.Nodes.Any(a => a.Key == "Resources"))
+ node.Value.Nodes.Where(n => n.Key == "Resources").Do(n => n.Key = "Terrain");
+ else
+ node.Value.Nodes.Add(new MiniYamlNode("Terrain", new MiniYaml("Tiberium, BlueTiberium")));
+
+ if (!node.Value.Nodes.Any(a => a.Key == "Weapon"))
+ node.Value.Nodes.Add(new MiniYamlNode("Weapon", new MiniYaml("Tiberium")));
+ }
+ }
+
+ if (node.Key.Contains("DamagedWithoutFoundation"))
+ {
+ node.Key = node.Key.Replace("DamagedWithoutFoundation", "DamagedByTerrain");
+ if (!node.Key.StartsWith("-"))
+ {
+ if (!node.Value.Nodes.Any(a => a.Key == "Weapon"))
+ node.Value.Nodes.Add(new MiniYamlNode("Weapon", new MiniYaml("weathering")));
+
+ Console.WriteLine("SafeTerrain isn't converted. Setup an inverted check using Terrain.");
+
+ node.Value.Nodes.Add(new MiniYamlNode("StartOnThreshold", new MiniYaml("true")));
+ if (!node.Value.Nodes.Any(a => a.Key == "DamageThreshold"))
+ node.Value.Nodes.Add(new MiniYamlNode("DamageThreshold", new MiniYaml("50")));
+ }
+ }
+ }
+
UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
}
diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
index 1f1107c965..7baf4c5cc0 100644
--- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
+++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
@@ -79,7 +79,6 @@
-
diff --git a/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs b/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs
deleted file mode 100644
index 162bf05048..0000000000
--- a/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-#region Copyright & License Information
-/*
- * Copyright 2007-2016 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, either version 3 of
- * the License, or (at your option) any later version. For more
- * information, see COPYING.
- */
-#endregion
-
-using System.Collections.Generic;
-using System.Linq;
-using OpenRA.GameRules;
-using OpenRA.Mods.Common.Traits;
-using OpenRA.Traits;
-
-namespace OpenRA.Mods.D2k.Traits
-{
- [Desc("Reduces health points over time when the actor is placed on unsafe terrain.")]
- class DamagedWithoutFoundationInfo : ITraitInfo, IRulesetLoaded, Requires
- {
- [WeaponReference, Desc("The weapon to use for causing damage.")]
- public readonly string Weapon = "weathering";
-
- [Desc("Terrain types on which no damage is caused.")]
- public readonly HashSet SafeTerrain = new HashSet { "Concrete" };
-
- [Desc("The percentage of health the actor should keep.")]
- public readonly int DamageThreshold = 50;
-
- public WeaponInfo WeaponInfo { get; private set; }
-
- public object Create(ActorInitializer init) { return new DamagedWithoutFoundation(init.Self, this); }
- public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; }
- }
-
- class DamagedWithoutFoundation : ITick, ISync, INotifyAddedToWorld
- {
- readonly DamagedWithoutFoundationInfo info;
- readonly Health health;
-
- [Sync] int damageThreshold = 100;
- [Sync] int damageTicks;
-
- public DamagedWithoutFoundation(Actor self, DamagedWithoutFoundationInfo info)
- {
- this.info = info;
- health = self.Trait();
- }
-
- public void AddedToWorld(Actor self)
- {
- var safeTiles = 0;
- var totalTiles = 0;
- foreach (var kv in self.OccupiesSpace.OccupiedCells())
- {
- totalTiles++;
- if (info.SafeTerrain.Contains(self.World.Map.GetTerrainInfo(kv.First).Type))
- safeTiles++;
- }
-
- if (totalTiles > 0)
- damageThreshold = (info.DamageThreshold * health.MaxHP + (100 - info.DamageThreshold) * safeTiles * health.MaxHP / totalTiles) / 100;
- else
- damageThreshold = health.HP;
-
- // Actors start with maximum damage applied
- var delta = health.HP - damageThreshold;
- if (delta > 0)
- health.InflictDamage(self, self.World.WorldActor, delta, null, false);
-
- damageTicks = info.WeaponInfo.ReloadDelay;
- }
-
- public void Tick(Actor self)
- {
- if (health.HP <= damageThreshold || --damageTicks > 0)
- return;
-
- info.WeaponInfo.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty());
- damageTicks = info.WeaponInfo.ReloadDelay;
- }
- }
-}
diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml
index ef6472da97..7169d0ca4d 100644
--- a/mods/cnc/rules/defaults.yaml
+++ b/mods/cnc/rules/defaults.yaml
@@ -210,9 +210,11 @@
Passenger:
CargoType: Infantry
HiddenUnderFog:
- PoisonedByTiberium:
+ DamagedByTerrain:
UpgradeTypes: hazmatsuits
UpgradeMaxEnabledLevel: 0
+ Terrain: Tiberium, BlueTiberium
+ Weapon: Tiberium
GlobalUpgradable@BIO:
Upgrades: hazmatsuits
Prerequisites: bio
@@ -416,8 +418,9 @@
Guard:
Voice: Move
Guardable:
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: Heal
+ Terrain: Tiberium, BlueTiberium
Voiced:
VoiceSet: DinoVoice
diff --git a/mods/cnc/rules/infantry.yaml b/mods/cnc/rules/infantry.yaml
index defa48affd..7e035b835e 100644
--- a/mods/cnc/rules/infantry.yaml
+++ b/mods/cnc/rules/infantry.yaml
@@ -122,7 +122,7 @@ E5:
MuzzleSequence: muzzle
AttackFrontal:
WithMuzzleOverlay:
- -PoisonedByTiberium:
+ -DamagedByTerrain:
WithInfantryBody:
AttackSequence: shoot
diff --git a/mods/d2k/rules/arrakis.yaml b/mods/d2k/rules/arrakis.yaml
index 95419fec35..bbd288b957 100644
--- a/mods/d2k/rules/arrakis.yaml
+++ b/mods/d2k/rules/arrakis.yaml
@@ -125,7 +125,7 @@ sietch:
Type: wood
RevealsShroud:
Range: 10c0
- -DamagedWithoutFoundation:
+ -DamagedByTerrain:
-GivesBuildableArea:
-Sellable:
-Capturable:
diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml
index 63c8da3750..c11436f09e 100644
--- a/mods/d2k/rules/defaults.yaml
+++ b/mods/d2k/rules/defaults.yaml
@@ -277,7 +277,11 @@
Range: 3c0
WithCrumbleOverlay:
Demolishable:
- DamagedWithoutFoundation:
+ DamagedByTerrain:
+ Weapon: weathering
+ Terrain: Rock
+ DamageThreshold: 50
+ StartOnThreshold: true
ThrowsShrapnel:
Weapons: Debris, Debris2, Debris3, Debris4
Pieces: 2, 5
diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml
index 9de2511fdb..5c3b8f7842 100644
--- a/mods/d2k/rules/structures.yaml
+++ b/mods/d2k/rules/structures.yaml
@@ -43,7 +43,7 @@ concreteb:
construction_yard:
Inherits: ^Building
- -DamagedWithoutFoundation:
+ -DamagedByTerrain:
Building:
Footprint: xxx xxx
Dimensions: 3,2
diff --git a/mods/ts/rules/civilian-infantry.yaml b/mods/ts/rules/civilian-infantry.yaml
index d374b5a2e6..7cb1ba4291 100644
--- a/mods/ts/rules/civilian-infantry.yaml
+++ b/mods/ts/rules/civilian-infantry.yaml
@@ -35,8 +35,9 @@ UMAGON:
Speed: 71
Health:
HP: 150
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Passenger:
RevealsShroud:
Range: 7c0
@@ -90,8 +91,9 @@ MUTANT:
VoiceSet: Mutant
Health:
HP: 50
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Mobile:
Speed: 56
RevealsShroud:
@@ -115,8 +117,9 @@ MWMN:
VoiceSet: CivilianFemale
Health:
HP: 50
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Mobile:
Speed: 56
RevealsShroud:
@@ -140,8 +143,9 @@ MUTANT3:
VoiceSet: Mutant
Health:
HP: 50
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Mobile:
Speed: 56
RevealsShroud:
@@ -165,8 +169,9 @@ TRATOS:
VoiceSet: Tratos
Health:
HP: 200
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Mobile:
Speed: 71
RevealsShroud:
@@ -220,8 +225,9 @@ DOGGIE:
HP: 250
Shape: Circle
Radius: 213
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Valued:
Cost: 100
Armor:
diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml
index a69d65fd21..e410bfcbd0 100644
--- a/mods/ts/rules/defaults.yaml
+++ b/mods/ts/rules/defaults.yaml
@@ -284,7 +284,9 @@
Voice: Move
HiddenUnderFog:
ActorLostNotification:
- PoisonedByTiberium:
+ DamagedByTerrain:
+ Terrain: Tiberium, BlueTiberium
+ Weapon: Tiberium
Guard:
Voice: Move
Guardable:
@@ -383,8 +385,9 @@
RevealsShroud:
Range: 4c0
MustBeDestroyed:
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
WithPermanentInjury:
WithInfantryBody:
AttackSequence: attack
@@ -465,9 +468,9 @@
Weapons: SmallDebris
Pieces: 3, 7
Range: 2c0, 5c0
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: Veins
- Resources: Veins
+ Terrain: Veins
UpgradeOnDamage@DAMAGED:
Upgrades: damagedspeed
ValidDamageStates: Heavy
@@ -633,8 +636,9 @@
TargetTypes: Ground, Creep
AttackMove:
HiddenUnderFog:
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Guardable:
WithSpriteBody:
diff --git a/mods/ts/rules/gdi-infantry.yaml b/mods/ts/rules/gdi-infantry.yaml
index 46b3d86c31..e5db95918b 100644
--- a/mods/ts/rules/gdi-infantry.yaml
+++ b/mods/ts/rules/gdi-infantry.yaml
@@ -113,8 +113,9 @@ GHOST:
Speed: 56
Health:
HP: 200
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Passenger:
RevealsShroud:
Range: 6c0
diff --git a/mods/ts/rules/gdi-vehicles.yaml b/mods/ts/rules/gdi-vehicles.yaml
index d8b40033ab..dfbb422685 100644
--- a/mods/ts/rules/gdi-vehicles.yaml
+++ b/mods/ts/rules/gdi-vehicles.yaml
@@ -93,7 +93,7 @@ HVR:
TrailWhileStationary: True
StationaryInterval: 18
MovingInterval: 6
- -PoisonedByTiberium:
+ -DamagedByTerrain:
SMECH:
Inherits: ^Vehicle
@@ -132,7 +132,7 @@ SMECH:
MoveSequence: run
Selectable:
Bounds: 20, 32, 0, -8
- -PoisonedByTiberium:
+ -DamagedByTerrain:
MMCH:
Inherits: ^Tank
diff --git a/mods/ts/rules/nod-infantry.yaml b/mods/ts/rules/nod-infantry.yaml
index 59fc9cd028..c9409b8422 100644
--- a/mods/ts/rules/nod-infantry.yaml
+++ b/mods/ts/rules/nod-infantry.yaml
@@ -109,8 +109,9 @@ MHIJACK:
VoiceSet: Hijacker
Health:
HP: 300
- PoisonedByTiberium:
+ DamagedByTerrain:
Weapon: TiberiumHeal
+ Terrain: Tiberium, BlueTiberium
Mobile:
Speed: 99
-Crushable:
diff --git a/mods/ts/rules/nod-vehicles.yaml b/mods/ts/rules/nod-vehicles.yaml
index d9c04b2ceb..2a73f78185 100644
--- a/mods/ts/rules/nod-vehicles.yaml
+++ b/mods/ts/rules/nod-vehicles.yaml
@@ -27,7 +27,7 @@ BGGY:
Voice: Attack
AutoTarget:
WithMuzzleOverlay:
- -PoisonedByTiberium:
+ -DamagedByTerrain:
BIKE:
Inherits: ^VoxelVehicle
@@ -257,7 +257,7 @@ WEED:
-WithVoxelBody:
WithVoxelUnloadBody:
-GainsExperience:
- -PoisonedByTiberium:
+ -DamagedByTerrain:
SAPC:
Inherits: ^VoxelTank
diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml
index 7ff3527ad0..30e6fe1337 100644
--- a/mods/ts/rules/shared-vehicles.yaml
+++ b/mods/ts/rules/shared-vehicles.yaml
@@ -96,7 +96,7 @@ HARV:
FactionImages:
gdi: harv.gdi
nod: harv.nod
- -PoisonedByTiberium:
+ -DamagedByTerrain:
LPST:
Inherits: ^VoxelTank