diff --git a/OpenRA.Mods.Cnc/Effects/IonCannon.cs b/OpenRA.Mods.Cnc/Effects/IonCannon.cs index 850f7d56d6..df4f1b14ed 100644 --- a/OpenRA.Mods.Cnc/Effects/IonCannon.cs +++ b/OpenRA.Mods.Cnc/Effects/IonCannon.cs @@ -23,12 +23,12 @@ namespace OpenRA.Mods.Cnc.Effects readonly Animation anim; readonly Player firedBy; readonly string palette; - readonly string weapon; + readonly WeaponInfo weapon; int weaponDelay; bool impacted = false; - public IonCannon(Player firedBy, string weapon, World world, CPos location, string effect, string palette, int delay) + public IonCannon(Player firedBy, WeaponInfo weapon, World world, CPos location, string effect, string palette, int delay) { this.firedBy = firedBy; this.weapon = weapon; @@ -44,7 +44,6 @@ namespace OpenRA.Mods.Cnc.Effects anim.Tick(); if (!impacted && weaponDelay-- <= 0) { - var weapon = world.Map.Rules.Weapons[this.weapon.ToLowerInvariant()]; weapon.Impact(target, firedBy.PlayerActor, Enumerable.Empty()); impacted = true; } diff --git a/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs b/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs index 9abac098e8..92e0341331 100644 --- a/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs +++ b/OpenRA.Mods.Cnc/Traits/PoisonedByTiberium.cs @@ -16,12 +16,15 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { - class PoisonedByTiberiumInfo : UpgradableTraitInfo + 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(this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; } } class PoisonedByTiberium : UpgradableTrait, ITick, ISync @@ -45,10 +48,8 @@ namespace OpenRA.Mods.Cnc.Traits if (r == null || !Info.Resources.Contains(r.Info.Name)) return; - var weapon = self.World.Map.Rules.Weapons[Info.Weapon.ToLowerInvariant()]; - - weapon.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty()); - poisonTicks = weapon.ReloadDelay; + Info.WeaponInfo.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty()); + poisonTicks = Info.WeaponInfo.ReloadDelay; } } } diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs index 3ef847b48f..f14832bd29 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/IonCannonPower.cs @@ -8,6 +8,7 @@ */ #endregion +using OpenRA.GameRules; using OpenRA.Mods.Cnc.Effects; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; @@ -16,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { - class IonCannonPowerInfo : SupportPowerInfo + class IonCannonPowerInfo : SupportPowerInfo, IRulesetLoaded { [ActorReference] [Desc("Actor to spawn when the attack starts")] @@ -32,10 +33,13 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Which weapon to fire"), WeaponReference] public readonly string Weapon = "IonCannon"; + public WeaponInfo WeaponInfo { get; private set; } + [Desc("Apply the weapon impact this many ticks into the effect")] public readonly int WeaponDelay = 7; public override object Create(ActorInitializer init) { return new IonCannonPower(init.Self, this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[Weapon.ToLowerInvariant()]; } } class IonCannonPower : SupportPower @@ -55,7 +59,7 @@ namespace OpenRA.Mods.Cnc.Traits self.World.AddFrameEndTask(w => { Game.Sound.Play(Info.LaunchSound, self.World.Map.CenterOfCell(order.TargetLocation)); - w.Add(new IonCannon(self.Owner, info.Weapon, w, order.TargetLocation, info.Effect, info.EffectPalette, info.WeaponDelay)); + w.Add(new IonCannon(self.Owner, info.WeaponInfo, w, order.TargetLocation, info.Effect, info.EffectPalette, info.WeaponDelay)); if (info.CameraActor == null) return; diff --git a/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs b/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs index 9aa00efdf4..343b2b8d19 100644 --- a/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs +++ b/OpenRA.Mods.Common/Activities/Air/FallToEarth.cs @@ -35,12 +35,10 @@ namespace OpenRA.Mods.Common.Activities { if (self.CenterPosition.Z <= 0) { - if (info.Explosion != null) + if (info.ExplosionWeapon != null) { - var weapon = self.World.Map.Rules.Weapons[info.Explosion.ToLowerInvariant()]; - // Use .FromPos since this actor is killed. Cannot use Target.FromActor - weapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); + info.ExplosionWeapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); } self.Dispose(); diff --git a/OpenRA.Mods.Common/Effects/NukeLaunch.cs b/OpenRA.Mods.Common/Effects/NukeLaunch.cs index 7808952c9b..8097289e4a 100644 --- a/OpenRA.Mods.Common/Effects/NukeLaunch.cs +++ b/OpenRA.Mods.Common/Effects/NukeLaunch.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Effects { readonly Player firedBy; readonly Animation anim; - readonly string weapon; + readonly WeaponInfo weapon; readonly string flashType; readonly WPos ascendSource; @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Effects WPos pos; int ticks; - public NukeLaunch(Player firedBy, string weapon, WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType) + public NukeLaunch(Player firedBy, string name, WeaponInfo weapon, WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType) { this.firedBy = firedBy; this.weapon = weapon; @@ -49,13 +49,12 @@ namespace OpenRA.Mods.Common.Effects descendSource = targetPos + offset; descendTarget = targetPos; - anim = new Animation(firedBy.World, weapon); + anim = new Animation(firedBy.World, name); anim.PlayRepeating("up"); pos = launchPos; - var weaponRules = firedBy.World.Map.Rules.Weapons[weapon.ToLowerInvariant()]; - if (weaponRules.Report != null && weaponRules.Report.Any()) - Game.Sound.Play(weaponRules.Report.Random(firedBy.World.SharedRandom), pos); + if (weapon.Report != null && weapon.Report.Any()) + Game.Sound.Play(weapon.Report.Random(firedBy.World.SharedRandom), pos); if (skipAscent) ticks = turn; @@ -82,7 +81,6 @@ namespace OpenRA.Mods.Common.Effects void Explode(World world) { world.AddFrameEndTask(w => w.Remove(this)); - var weapon = world.Map.Rules.Weapons[this.weapon.ToLowerInvariant()]; weapon.Impact(Target.FromPos(pos), firedBy.PlayerActor, Enumerable.Empty()); world.WorldActor.Trait().AddEffect(20, pos, 5); diff --git a/OpenRA.Mods.Common/Traits/Air/FallsToEarth.cs b/OpenRA.Mods.Common/Traits/Air/FallsToEarth.cs index ab1d1b8352..f233ba7cb5 100644 --- a/OpenRA.Mods.Common/Traits/Air/FallsToEarth.cs +++ b/OpenRA.Mods.Common/Traits/Air/FallsToEarth.cs @@ -16,7 +16,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Causes aircraft husks that are spawned in the air to crash to the ground.")] - public class FallsToEarthInfo : ITraitInfo + public class FallsToEarthInfo : ITraitInfo, IRulesetLoaded { [WeaponReference] public readonly string Explosion = "UnitExplode"; @@ -25,7 +25,13 @@ namespace OpenRA.Mods.Common.Traits public readonly bool Moves = false; public readonly WDist Velocity = new WDist(43); + public WeaponInfo ExplosionWeapon { get; private set; } + public object Create(ActorInitializer init) { return new FallsToEarth(init.Self, this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + ExplosionWeapon = string.IsNullOrEmpty(Explosion) ? null : rules.Weapons[Explosion.ToLowerInvariant()]; + } } public class FallsToEarth diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 1566ed636b..846e09ef8f 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -12,13 +12,14 @@ using System; using System.Collections.Generic; using System.Linq; using OpenRA.Effects; +using OpenRA.GameRules; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - class BridgeInfo : ITraitInfo, Requires, Requires + class BridgeInfo : ITraitInfo, IRulesetLoaded, Requires, Requires { public readonly bool Long = false; @@ -41,8 +42,12 @@ namespace OpenRA.Mods.Common.Traits [Desc("The name of the weapon to use when demolishing the bridge")] [WeaponReference] public readonly string DemolishWeapon = "Demolish"; + public WeaponInfo DemolishWeaponInfo { get; private set; } + public object Create(ActorInitializer init) { return new Bridge(init.Self, this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { DemolishWeaponInfo = rules.Weapons[DemolishWeapon.ToLowerInvariant()]; } + public IEnumerable> Templates { get @@ -333,10 +338,8 @@ namespace OpenRA.Mods.Common.Traits var initialDamage = health.DamageState; self.World.AddFrameEndTask(w => { - var weapon = saboteur.World.Map.Rules.Weapons[info.DemolishWeapon.ToLowerInvariant()]; - // Use .FromPos since this actor is killed. Cannot use Target.FromActor - weapon.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty()); + info.DemolishWeaponInfo.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty()); self.World.WorldActor.Trait().AddEffect(15, self.CenterPosition, 6); self.Kill(saboteur); diff --git a/OpenRA.Mods.Common/Traits/Explodes.cs b/OpenRA.Mods.Common/Traits/Explodes.cs index 495bd4ae43..4eaa18c33a 100644 --- a/OpenRA.Mods.Common/Traits/Explodes.cs +++ b/OpenRA.Mods.Common/Traits/Explodes.cs @@ -11,13 +11,14 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.GameRules; using OpenRA.Mods.Common.Warheads; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("This actor explodes when killed.")] - public class ExplodesInfo : ITraitInfo + public class ExplodesInfo : ITraitInfo, IRulesetLoaded { [WeaponReference, FieldLoader.Require, Desc("Weapon to use for explosion if ammo/payload is loaded.")] public readonly string Weapon = "UnitExplode"; @@ -34,7 +35,15 @@ namespace OpenRA.Mods.Common.Traits [Desc("DeathType(s) to apply upon explosion.")] public readonly HashSet DeathType = new HashSet(); + public WeaponInfo WeaponInfo { get; private set; } + public WeaponInfo EmptyWeaponInfo { get; private set; } + public object Create(ActorInitializer init) { return new Explodes(this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + WeaponInfo = string.IsNullOrEmpty(Weapon) ? null : rules.Weapons[Weapon.ToLowerInvariant()]; + EmptyWeaponInfo = string.IsNullOrEmpty(EmptyWeapon) ? null : rules.Weapons[EmptyWeapon.ToLowerInvariant()]; + } } public class Explodes : INotifyKilled @@ -55,15 +64,10 @@ namespace OpenRA.Mods.Common.Traits if (info.DeathType.Count > 0 && warhead != null && !warhead.DamageTypes.Overlaps(info.DeathType)) return; - var weaponName = ChooseWeaponForExplosion(self); - if (string.IsNullOrEmpty(weaponName)) + var weapon = ChooseWeaponForExplosion(self); + if (weapon == null) return; - if (!e.Attacker.World.Map.Rules.Weapons.ContainsKey(weaponName.ToLowerInvariant())) - throw new InvalidOperationException("Actor " + self.Info.Name - + ": Could not find weapon '" + weaponName.ToLowerInvariant() + "', check for typos."); - - var weapon = e.Attacker.World.Map.Rules.Weapons[weaponName.ToLowerInvariant()]; if (weapon.Report != null && weapon.Report.Any()) Game.Sound.Play(weapon.Report.Random(e.Attacker.World.SharedRandom), self.CenterPosition); @@ -71,11 +75,11 @@ namespace OpenRA.Mods.Common.Traits weapon.Impact(Target.FromPos(self.CenterPosition), e.Attacker, Enumerable.Empty()); } - string ChooseWeaponForExplosion(Actor self) + WeaponInfo ChooseWeaponForExplosion(Actor self) { var shouldExplode = self.TraitsImplementing().All(a => a.ShouldExplode(self)); var useFullExplosion = self.World.SharedRandom.Next(100) <= info.LoadedChance; - return (shouldExplode && useFullExplosion) ? info.Weapon : info.EmptyWeapon; + return (shouldExplode && useFullExplosion) ? info.WeaponInfo : info.EmptyWeaponInfo; } } } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs index 197b17ddab..5559a7d6fb 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs @@ -10,6 +10,7 @@ using System; using OpenRA.Effects; +using OpenRA.GameRules; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Effects; using OpenRA.Primitives; @@ -17,7 +18,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - class NukePowerInfo : SupportPowerInfo, Requires + class NukePowerInfo : SupportPowerInfo, IRulesetLoaded, Requires { [WeaponReference] public readonly string MissileWeapon = ""; @@ -51,7 +52,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Sequence the launching actor should play when activating this power.")] public readonly string ActivationSequence = "active"; + public WeaponInfo WeaponInfo { get; private set; } + public override object Create(ActorInitializer init) { return new NukePower(init.Self, this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) { WeaponInfo = rules.Weapons[MissileWeapon.ToLowerInvariant()]; } } class NukePower : SupportPower @@ -82,7 +86,7 @@ namespace OpenRA.Mods.Common.Traits } var targetPosition = self.World.Map.CenterOfCell(order.TargetLocation); - var missile = new NukeLaunch(self.Owner, info.MissileWeapon, + var missile = new NukeLaunch(self.Owner, info.MissileWeapon, info.WeaponInfo, self.CenterPosition + body.LocalToWorld(info.SpawnOffset), targetPosition, info.FlightVelocity, info.FlightDelay, info.SkipAscent, diff --git a/OpenRA.Mods.Common/Traits/ThrowsShrapnel.cs b/OpenRA.Mods.Common/Traits/ThrowsShrapnel.cs index 9fb377d3b7..bd01530ed0 100644 --- a/OpenRA.Mods.Common/Traits/ThrowsShrapnel.cs +++ b/OpenRA.Mods.Common/Traits/ThrowsShrapnel.cs @@ -15,13 +15,20 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Throws particles when the actor is destroyed that do damage on impact.")] - public class ThrowsShrapnelInfo : ITraitInfo + public class ThrowsShrapnelInfo : ITraitInfo, IRulesetLoaded { [WeaponReference, FieldLoader.Require] public string[] Weapons = { }; public int[] Pieces = { 3, 10 }; public WDist[] Range = { WDist.FromCells(2), WDist.FromCells(5) }; + + public WeaponInfo[] WeaponInfos { get; private set; } + public object Create(ActorInitializer actor) { return new ThrowsShrapnel(this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + WeaponInfos = Weapons.Select(w => rules.Weapons[w.ToLowerInvariant()]).ToArray(); + } } class ThrowsShrapnel : INotifyKilled @@ -35,9 +42,8 @@ namespace OpenRA.Mods.Common.Traits public void Killed(Actor self, AttackInfo attack) { - foreach (var name in info.Weapons) + foreach (var wep in info.WeaponInfos) { - var wep = self.World.Map.Rules.Weapons[name.ToLowerInvariant()]; var pieces = self.World.SharedRandom.Next(info.Pieces[0], info.Pieces[1]); var range = self.World.SharedRandom.Next(info.Range[0].Length, info.Range[1].Length); diff --git a/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs b/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs index e18482f698..7a6f3b8035 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/DamagedWithoutFoundation.cs @@ -16,21 +16,23 @@ 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, Requires + class DamagedWithoutFoundationInfo : ITraitInfo, IRulesetLoaded, Requires { [WeaponReference] public readonly string Weapon = "weathering"; public readonly HashSet SafeTerrain = new HashSet { "Concrete" }; 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; - readonly WeaponInfo weapon; [Sync] int damageThreshold = 100; [Sync] int damageTicks; @@ -39,7 +41,6 @@ namespace OpenRA.Mods.D2k.Traits { this.info = info; health = self.Trait(); - weapon = self.World.Map.Rules.Weapons[info.Weapon.ToLowerInvariant()]; } public void AddedToWorld(Actor self) @@ -69,8 +70,8 @@ namespace OpenRA.Mods.D2k.Traits if (health.HP <= damageThreshold || --damageTicks > 0) return; - weapon.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty()); - damageTicks = weapon.ReloadDelay; + info.WeaponInfo.Impact(Target.FromActor(self), self.World.WorldActor, Enumerable.Empty()); + damageTicks = info.WeaponInfo.ReloadDelay; } } } diff --git a/OpenRA.Mods.RA/Traits/MadTank.cs b/OpenRA.Mods.RA/Traits/MadTank.cs index 82877d797f..8c3345a692 100644 --- a/OpenRA.Mods.RA/Traits/MadTank.cs +++ b/OpenRA.Mods.RA/Traits/MadTank.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using OpenRA.Activities; +using OpenRA.GameRules; using OpenRA.Mods.Common; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; @@ -22,7 +23,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Traits { - class MadTankInfo : ITraitInfo, Requires, Requires + class MadTankInfo : ITraitInfo, IRulesetLoaded, Requires, Requires { [SequenceReference] public readonly string ThumpSequence = "piston"; public readonly int ThumpInterval = 8; @@ -47,7 +48,15 @@ namespace OpenRA.Mods.RA.Traits [VoiceReference] public readonly string Voice = "Action"; + public WeaponInfo ThumpDamageWeaponInfo { get; private set; } + public WeaponInfo DetonationWeaponInfo { get; private set; } + public object Create(ActorInitializer init) { return new MadTank(init.Self, this); } + public void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + ThumpDamageWeaponInfo = rules.Weapons[ThumpDamageWeapon.ToLowerInvariant()]; + DetonationWeaponInfo = rules.Weapons[DetonationWeapon.ToLowerInvariant()]; + } } class MadTank : IIssueOrder, IResolveOrder, IOrderVoice, ITick, IPreventsTeleport @@ -76,10 +85,8 @@ namespace OpenRA.Mods.RA.Traits { if (info.ThumpDamageWeapon != null) { - var weapon = self.World.Map.Rules.Weapons[info.ThumpDamageWeapon.ToLowerInvariant()]; - // Use .FromPos since this weapon needs to affect more than just the MadTank actor - weapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); + info.ThumpDamageWeaponInfo.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); } screenShaker.AddEffect(info.ThumpShakeTime, self.CenterPosition, info.ThumpShakeIntensity, info.ThumpShakeMultiplier); @@ -118,10 +125,8 @@ namespace OpenRA.Mods.RA.Traits { if (info.DetonationWeapon != null) { - var weapon = self.World.Map.Rules.Weapons[info.DetonationWeapon.ToLowerInvariant()]; - // Use .FromPos since this actor is killed. Cannot use Target.FromActor - weapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); + info.DetonationWeaponInfo.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty()); } self.Kill(self);