diff --git a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs index 4ff82366cb..f862814e4d 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithInfantryBody.cs @@ -24,10 +24,24 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly int MaxIdleDelay = 110; [SequenceReference] public readonly string MoveSequence = "run"; - [SequenceReference] public readonly string AttackSequence = null; + [SequenceReference] public readonly string DefaultAttackSequence = null; + + // TODO: [SequenceReference] isn't smart enough to use Dictionaries. + [Desc("Attack sequence to use for each armament.")] + [FieldLoader.LoadUsing("LoadWeaponSequences")] + public readonly Dictionary AttackSequences = new Dictionary(); [SequenceReference] public readonly string[] IdleSequences = { }; [SequenceReference] public readonly string[] StandSequences = { "stand" }; + public static object LoadWeaponSequences(MiniYaml yaml) + { + var md = yaml.ToDictionary(); + + return md.ContainsKey("AttackSequences") + ? md["AttackSequences"].ToDictionary(my => FieldLoader.GetValue("(value)", my.Value)) + : new Dictionary(); + } + public override object Create(ActorInitializer init) { return new WithInfantryBody(init, this); } public IEnumerable RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) @@ -96,18 +110,22 @@ namespace OpenRA.Mods.Common.Traits.Render return !IsModifyingSequence; } - public void Attacking(Actor self, Target target) + public void Attacking(Actor self, Target target, Armament a) { - if (!string.IsNullOrEmpty(Info.AttackSequence) && DefaultAnimation.HasSequence(NormalizeInfantrySequence(self, Info.AttackSequence))) + string sequence; + if (!Info.AttackSequences.TryGetValue(a.Info.Name, out sequence)) + sequence = Info.DefaultAttackSequence; + + if (!string.IsNullOrEmpty(sequence) && DefaultAnimation.HasSequence(NormalizeInfantrySequence(self, sequence))) { state = AnimationState.Attacking; - DefaultAnimation.PlayThen(NormalizeInfantrySequence(self, Info.AttackSequence), () => state = AnimationState.Idle); + DefaultAnimation.PlayThen(NormalizeInfantrySequence(self, sequence), () => state = AnimationState.Idle); } } void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { - Attacking(self, target); + Attacking(self, target, a); } void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 224b64bcbc..5ea65e392d 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -386,6 +386,17 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // Renamed AttackSequence to DefaultAttackSequence in WithInfantryBody. + if (engineVersion < 20161014) + { + if (node.Key == "WithInfantryBody") + { + var attackSequence = node.Value.Nodes.FirstOrDefault(n => n.Key == "AttackSequence"); + if (attackSequence != null) + attackSequence.Key = "DefaultAttackSequence"; + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.RA/Activities/Leap.cs b/OpenRA.Mods.RA/Activities/Leap.cs index a5f10f5d5f..39b548296b 100644 --- a/OpenRA.Mods.RA/Activities/Leap.cs +++ b/OpenRA.Mods.RA/Activities/Leap.cs @@ -30,13 +30,13 @@ namespace OpenRA.Mods.RA.Activities int ticks; WAngle angle; - public Leap(Actor self, Actor target, WeaponInfo weapon, WDist speed, WAngle angle) + public Leap(Actor self, Actor target, Armament a, WDist speed, WAngle angle) { var targetMobile = target.TraitOrDefault(); if (targetMobile == null) throw new InvalidOperationException("Leap requires a target actor with the Mobile trait"); - this.weapon = weapon; + this.weapon = a.Weapon; this.angle = angle; mobile = self.Trait(); mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, targetMobile.FromCell, targetMobile.FromSubCell); @@ -47,7 +47,7 @@ namespace OpenRA.Mods.RA.Activities length = Math.Max((to - from).Length / speed.Length, 1); // HACK: why isn't this using the interface? - self.Trait().Attacking(self, Target.FromActor(target)); + self.Trait().Attacking(self, Target.FromActor(target), a); if (weapon.Report != null && weapon.Report.Any()) Game.Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); diff --git a/OpenRA.Mods.RA/Traits/Attack/AttackLeap.cs b/OpenRA.Mods.RA/Traits/Attack/AttackLeap.cs index 938dc0dfcd..edf70fc868 100644 --- a/OpenRA.Mods.RA/Traits/Attack/AttackLeap.cs +++ b/OpenRA.Mods.RA/Traits/Attack/AttackLeap.cs @@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA.Traits return; self.CancelActivity(); - self.QueueActivity(new Leap(self, target.Actor, a.Weapon, info.Speed, info.Angle)); + self.QueueActivity(new Leap(self, target.Actor, a, info.Speed, info.Angle)); } } } diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 24a47c7992..7d51e55f28 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -320,7 +320,7 @@ Weapon: Pistol AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot ^DINO: Inherits@1: ^ExistsInWorld @@ -365,7 +365,7 @@ QuantizeFacingsFromSequence: Sequence: stand WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack WithDeathAnimation: UseDeathTypeSuffix: false AutoTarget: diff --git a/mods/cnc/rules/infantry.yaml b/mods/cnc/rules/infantry.yaml index c893fdc410..8d60707608 100644 --- a/mods/cnc/rules/infantry.yaml +++ b/mods/cnc/rules/infantry.yaml @@ -17,7 +17,7 @@ E1: AttackFrontal: WithInfantryBody: IdleSequences: idle1,idle2,idle3,idle4 - AttackSequence: shoot + DefaultAttackSequence: shoot E2: Inherits: ^Soldier @@ -40,7 +40,7 @@ E2: FireDelay: 15 AttackFrontal: WithInfantryBody: - AttackSequence: throw + DefaultAttackSequence: throw Explodes: Weapon: GrenadierExplode EmptyWeapon: GrenadierExplode @@ -68,7 +68,7 @@ E3: FireDelay: 5 AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot E4: Inherits: ^Soldier @@ -93,7 +93,7 @@ E4: AttackFrontal: WithMuzzleOverlay: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot E5: Inherits: ^Soldier @@ -124,7 +124,7 @@ E5: WithMuzzleOverlay: -DamagedByTerrain: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot E6: Inherits: ^Soldier @@ -185,7 +185,7 @@ RMBO: AttackMove: Voice: Attack WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot IdleSequences: idle1,idle2,idle3 AnnounceOnBuild: AnnounceOnKill: diff --git a/mods/d2k/rules/infantry.yaml b/mods/d2k/rules/infantry.yaml index 5070438747..cea226ca0d 100644 --- a/mods/d2k/rules/infantry.yaml +++ b/mods/d2k/rules/infantry.yaml @@ -18,7 +18,7 @@ light_inf: Weapon: LMG AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot engineer: Inherits: ^Infantry @@ -73,7 +73,7 @@ trooper: LocalOffset: 128,0,256 AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot thumper: Inherits: ^Infantry @@ -154,7 +154,7 @@ fremen: Weapon: Fremen_L AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot Cloak: InitialDelay: 85 CloakDelay: 85 @@ -188,7 +188,7 @@ grenadier: FireDelay: 3 AttackFrontal: WithInfantryBody: - AttackSequence: throw + DefaultAttackSequence: throw Explodes: Weapon: GrenDeath EmptyWeapon: GrenDeath @@ -213,7 +213,7 @@ sardaukar: RevealsShroud: Range: 4c768 WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot Armament@PRIMARY: Weapon: M_LMG Armament@SECONDARY: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index a59ea58c84..f87c3d7e93 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -303,7 +303,7 @@ Weapon: Pistol AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot ^Ship: Inherits@1: ^ExistsInWorld diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 1ab6f4f716..9e4566bc8d 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -37,7 +37,7 @@ DOG: Targetable: TargetTypes: Ground, Infantry WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot StandSequences: stand IgnoresDisguise: DetectCloaked: @@ -68,7 +68,7 @@ E1: MuzzleSequence: garrison-muzzle AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -98,7 +98,7 @@ E2: FireDelay: 15 AttackFrontal: WithInfantryBody: - AttackSequence: throw + DefaultAttackSequence: throw Explodes: Weapon: UnitExplodeSmall Chance: 50 @@ -123,6 +123,7 @@ E3: Weapon: RedEye LocalOffset: 0,0,555 Armament@SECONDARY: + Name: secondary Weapon: Dragon LocalOffset: 0,0,555 Armament@GARRISONED: @@ -130,7 +131,7 @@ E3: Weapon: Dragon AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -157,7 +158,7 @@ E4: Weapon: Flamer AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -287,7 +288,7 @@ E7: MuzzleSequence: garrison-muzzle AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot StandSequences: stand AnnounceOnBuild: AnnounceOnKill: @@ -323,7 +324,7 @@ MEDI: AttackFrontal: WithInfantryBody: StandSequences: stand - AttackSequence: heal + DefaultAttackSequence: heal Voiced: VoiceSet: MedicVoice @@ -360,7 +361,7 @@ MECH: CaptureTypes: husk PlayerExperience: 25 WithInfantryBody: - AttackSequence: repair + DefaultAttackSequence: repair StandSequences: stand Voiced: VoiceSet: MechanicVoice @@ -509,7 +510,7 @@ SHOK: Guard: Voice: Move WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot Voiced: VoiceSet: ShokVoice ProducibleWithLevel: @@ -544,7 +545,7 @@ SNIPER: MuzzleSequence: garrison-muzzle AttackFrontal: WithInfantryBody: - AttackSequence: shoot + DefaultAttackSequence: shoot Cloak: InitialDelay: 250 CloakDelay: 120 @@ -579,7 +580,7 @@ Zombie: ScanRadius: 5 AttackFrontal: WithInfantryBody: - AttackSequence: bite + DefaultAttackSequence: bite IdleSequences: idle1 Armament: Weapon: claw @@ -617,7 +618,7 @@ Ant: ScanRadius: 5 AttackFrontal: WithInfantryBody: - AttackSequence: bite + DefaultAttackSequence: bite Armament: Weapon: mandible Targetable: diff --git a/mods/ts/rules/civilian-infantry.yaml b/mods/ts/rules/civilian-infantry.yaml index 1a0f72761d..a1963a5d7d 100644 --- a/mods/ts/rules/civilian-infantry.yaml +++ b/mods/ts/rules/civilian-infantry.yaml @@ -44,7 +44,7 @@ UMAGON: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -99,7 +99,7 @@ MUTANT: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -123,7 +123,7 @@ MWMN: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -147,7 +147,7 @@ MUTANT3: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -168,7 +168,7 @@ TRATOS: Range: 4c0 -AutoTarget: WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack OXANNA: Inherits: ^Soldier @@ -186,7 +186,7 @@ OXANNA: Range: 4c0 -AutoTarget: WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack SLAV: Inherits: ^Soldier @@ -204,7 +204,7 @@ SLAV: Range: 4c0 -AutoTarget: WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack DOGGIE: Inherits@1: ^Infantry @@ -279,7 +279,7 @@ VISC_LRG: CIV1: Inherits: ^CivilianInfantry WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack Armament: Weapon: Pistola AttackFrontal: @@ -292,7 +292,7 @@ CIV2: CIV3: Inherits: ^CivilianInfantry WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack Armament: Weapon: Pistola AttackFrontal: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index d686cce255..0a244b812c 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -396,7 +396,7 @@ MustBeDestroyed: WithPermanentInjury: WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack IdleSequences: idle1,idle2 UpgradeOnDamageState@CRITICAL: Upgrades: criticalspeed diff --git a/mods/ts/rules/gdi-infantry.yaml b/mods/ts/rules/gdi-infantry.yaml index 9babe4779b..704fce9cfe 100644 --- a/mods/ts/rules/gdi-infantry.yaml +++ b/mods/ts/rules/gdi-infantry.yaml @@ -20,7 +20,7 @@ E2: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -53,7 +53,7 @@ MEDIC: OutsideRangeCursor: heal AttackFrontal: WithInfantryBody: - AttackSequence: heal + DefaultAttackSequence: heal SelfHealing: Delay: 60 Passenger: @@ -88,7 +88,7 @@ JUMPJET: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack -TakeCover: ProducibleWithLevel: Prerequisites: barracks.upgraded @@ -128,6 +128,6 @@ GHOST: DetonationDelay: 45 Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded diff --git a/mods/ts/rules/nod-infantry.yaml b/mods/ts/rules/nod-infantry.yaml index 66a92798cc..6a15c175e0 100644 --- a/mods/ts/rules/nod-infantry.yaml +++ b/mods/ts/rules/nod-infantry.yaml @@ -21,7 +21,7 @@ E3: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded diff --git a/mods/ts/rules/shared-infantry.yaml b/mods/ts/rules/shared-infantry.yaml index 34219cea40..f457c1e27f 100644 --- a/mods/ts/rules/shared-infantry.yaml +++ b/mods/ts/rules/shared-infantry.yaml @@ -25,7 +25,7 @@ E1: AttackFrontal: Voice: Attack WithInfantryBody: - AttackSequence: attack + DefaultAttackSequence: attack ProducibleWithLevel: Prerequisites: barracks.upgraded RenderSprites: