diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackWander.cs b/OpenRA.Mods.Common/Traits/Attack/AttackWander.cs index 6843308d32..073f415d63 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackWander.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackWander.cs @@ -29,9 +29,9 @@ namespace OpenRA.Mods.Common.Traits attackMove = self.TraitOrDefault(); } - public override void DoAction(Actor self, CPos targetPos) + public override void DoAction(Actor self, CPos targetCell) { - attackMove.ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = targetPos }); + attackMove.ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = targetCell }); } } } diff --git a/OpenRA.Mods.Common/Traits/Wanders.cs b/OpenRA.Mods.Common/Traits/Wanders.cs index b90e946fa4..625b3a2055 100644 --- a/OpenRA.Mods.Common/Traits/Wanders.cs +++ b/OpenRA.Mods.Common/Traits/Wanders.cs @@ -14,9 +14,9 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Wanders around aimlessly while idle.")] - abstract class WandersInfo : ITraitInfo + public abstract class WandersInfo : ITraitInfo { - public readonly int WanderMoveRadius = 10; + public readonly int WanderMoveRadius = 1; [Desc("Number of ticks to wait before decreasing the effective move radius.")] public readonly int TicksToWaitBeforeReducingMoveRadius = 5; @@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.Traits public abstract object Create(ActorInitializer init); } - class Wanders : INotifyIdle, INotifyBecomingIdle + public class Wanders : INotifyIdle, INotifyBecomingIdle { readonly Actor self; readonly WandersInfo info; @@ -46,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits effectiveMoveRadius = info.WanderMoveRadius; } - public void OnBecomingIdle(Actor self) + public virtual void OnBecomingIdle(Actor self) { countdown = self.World.SharedRandom.Next(info.MinMoveDelayInTicks, info.MaxMoveDelayInTicks); } @@ -56,9 +56,9 @@ namespace OpenRA.Mods.Common.Traits if (--countdown > 0) return; - var targetPos = PickTargetLocation(); - if (targetPos != CPos.Zero) - DoAction(self, targetPos); + var targetCell = PickTargetLocation(); + if (targetCell != CPos.Zero) + DoAction(self, targetCell); } CPos PickTargetLocation() @@ -81,7 +81,7 @@ namespace OpenRA.Mods.Common.Traits return targetCell; } - public virtual void DoAction(Actor self, CPos targetPos) + public virtual void DoAction(Actor self, CPos targetCell) { throw new NotImplementedException("Base class Wanders does not implement method DoAction!"); } diff --git a/OpenRA.Mods.D2k/Activities/SwallowActor.cs b/OpenRA.Mods.D2k/Activities/SwallowActor.cs index 5aa63d3781..1a123dcd98 100644 --- a/OpenRA.Mods.D2k/Activities/SwallowActor.cs +++ b/OpenRA.Mods.D2k/Activities/SwallowActor.cs @@ -14,9 +14,6 @@ using OpenRA.Activities; using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.D2k.Traits; -using OpenRA.Mods.RA; -using OpenRA.Mods.RA.Activities; -using OpenRA.Mods.RA.Traits; using OpenRA.Traits; namespace OpenRA.Mods.D2k.Activities @@ -29,11 +26,11 @@ namespace OpenRA.Mods.D2k.Activities readonly CPos location; readonly Target target; + readonly Sandworm sandworm; readonly WeaponInfo weapon; readonly RenderUnit renderUnit; readonly RadarPings radarPings; readonly AttackSwallow swallow; - readonly AttackSwallowInfo swallowInfo; readonly IPositionable positionable; int countdown; @@ -43,12 +40,12 @@ namespace OpenRA.Mods.D2k.Activities { this.target = target; this.weapon = weapon; + sandworm = self.Trait(); positionable = self.Trait(); swallow = self.Trait(); - swallowInfo = (AttackSwallowInfo)swallow.Info; renderUnit = self.Trait(); radarPings = self.World.WorldActor.TraitOrDefault(); - countdown = swallowInfo.AttackTime; + countdown = swallow.Info.AttackTime; renderUnit.DefaultAnimation.ReplaceAnim("burrowed"); stance = AttackState.Burrowed; @@ -70,6 +67,7 @@ namespace OpenRA.Mods.D2k.Activities return false; stance = AttackState.EmergingAboveGround; + sandworm.IsAttacking = true; foreach (var actor in lunch) actor.World.AddFrameEndTask(_ => actor.Destroy()); @@ -94,7 +92,7 @@ namespace OpenRA.Mods.D2k.Activities void NotifyPlayer(Player player, WPos location) { - Sound.PlayNotification(player.World.Map.Rules, player, "Speech", swallowInfo.WormAttackNotification, player.Country.Race); + Sound.PlayNotification(player.World.Map.Rules, player, "Speech", swallow.Info.WormAttackNotification, player.Country.Race); radarPings.Add(() => true, location, Color.Red, 50); } @@ -109,15 +107,17 @@ namespace OpenRA.Mods.D2k.Activities // Wait for the worm to get back underground if (stance == AttackState.ReturningUnderground) { - // There is a 50-50 chance that the worm would just go away - if (self.World.SharedRandom.Next() % 2 == 0) + sandworm.IsAttacking = false; + + // There is a chance that the worm would just go away after attacking + if (self.World.SharedRandom.Next() % 100 <= sandworm.Info.ChanceToDisappear) { self.CancelActivity(); self.World.AddFrameEndTask(w => w.Remove(self)); var wormManager = self.World.WorldActor.TraitOrDefault(); if (wormManager != null) - wormManager.DecreaseWorms(); + wormManager.DecreaseWormCount(); } else renderUnit.DefaultAnimation.ReplaceAnim("idle"); @@ -138,7 +138,7 @@ namespace OpenRA.Mods.D2k.Activities return NextActivity; } - countdown = swallowInfo.ReturnTime; + countdown = swallow.Info.ReturnTime; stance = AttackState.ReturningUnderground; } diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj index cb51a2f0a2..74756d80ee 100644 --- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj +++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj @@ -81,6 +81,7 @@ + @@ -90,6 +91,8 @@ + + diff --git a/OpenRA.Mods.D2k/Traits/AttackSwallow.cs b/OpenRA.Mods.D2k/Traits/AttackSwallow.cs index 98baae5391..3eea5404f2 100644 --- a/OpenRA.Mods.D2k/Traits/AttackSwallow.cs +++ b/OpenRA.Mods.D2k/Traits/AttackSwallow.cs @@ -30,8 +30,13 @@ namespace OpenRA.Mods.D2k.Traits class AttackSwallow : AttackFrontal { + public readonly new AttackSwallowInfo Info; + public AttackSwallow(Actor self, AttackSwallowInfo info) - : base(self, info) { } + : base(self, info) + { + Info = info; + } public override void DoAttack(Actor self, Target target) { diff --git a/OpenRA.Mods.D2k/Traits/AttractsWorms.cs b/OpenRA.Mods.D2k/Traits/AttractsWorms.cs new file mode 100644 index 0000000000..34a41abda9 --- /dev/null +++ b/OpenRA.Mods.D2k/Traits/AttractsWorms.cs @@ -0,0 +1,60 @@ +#region Copyright & License Information +/* +* Copyright 2007-2015 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 OpenRA.Traits; + +namespace OpenRA.Mods.D2k.Traits +{ + [Desc("This actor makes noise, which causes them to be targeted by actors with the Sandworm trait.")] + public class AttractsWormsInfo : ITraitInfo + { + [Desc("How much noise this actor produces.")] + public readonly int Intensity = 0; + + [Desc("Noise percentage at Range step away from the actor.")] + public readonly int[] Falloff = { 100, 100, 25, 11, 6, 4, 3, 2, 1, 0 }; + + [Desc("Range between falloff steps.")] + public readonly WRange Spread = new WRange(3072); + + [Desc("Ranges at which each Falloff step is defined. Overrides Spread.")] + public WRange[] Range = null; + + public object Create(ActorInitializer init) { return new AttractsWorms(this); } + } + + public class AttractsWorms + { + public readonly AttractsWormsInfo Info; + + public AttractsWorms(AttractsWormsInfo info) + { + Info = info; + + if (info.Range == null) + info.Range = Exts.MakeArray(info.Falloff.Length, i => i * info.Spread); + } + + public int GetNoisePercentageAtDistance(int distance) + { + var inner = Info.Range[0].Range; + for (var i = 1; i < Info.Range.Length; i++) + { + var outer = Info.Range[i].Range; + if (outer > distance) + return int2.Lerp(Info.Falloff[i - 1], Info.Falloff[i], distance - inner, outer - inner); + + inner = outer; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.D2k/Traits/Sandworm.cs b/OpenRA.Mods.D2k/Traits/Sandworm.cs new file mode 100644 index 0000000000..d40d1fcfbb --- /dev/null +++ b/OpenRA.Mods.D2k/Traits/Sandworm.cs @@ -0,0 +1,159 @@ +#region Copyright & License Information +/* +* Copyright 2007-2015 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 OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.D2k.Traits +{ + class SandwormInfo : WandersInfo, Requires, Requires, Requires + { + [Desc("Time between rescanning for targets (in ticks).")] + public readonly int TargetRescanInterval = 32; + + [Desc("The radius in which the worm \"searches\" for targets.")] + public readonly WRange MaxSearchRadius = WRange.FromCells(27); + + [Desc("The range at which the worm launches an attack regardless of noise levels.")] + public readonly WRange IgnoreNoiseAttackRange = WRange.FromCells(3); + + [Desc("The chance this actor has of disappearing after it attacks (in %).")] + public readonly int ChanceToDisappear = 80; + + [Desc("Name of the sequence that is used when the actor is idle or moving (not attacking).")] + public readonly string IdleSequence = "idle"; + + public override object Create(ActorInitializer init) { return new Sandworm(init.Self, this); } + } + + class Sandworm : Wanders, ITick, INotifyKilled + { + public readonly SandwormInfo Info; + + readonly WormManager manager; + readonly Lazy mobile; + readonly Lazy renderUnit; + readonly Lazy attackTrait; + + public bool IsMovingTowardTarget { get; private set; } + + public bool IsAttacking; + + int targetCountdown; + + public Sandworm(Actor self, SandwormInfo info) + : base(self, info) + { + Info = info; + mobile = Exts.Lazy(self.Trait); + renderUnit = Exts.Lazy(self.Trait); + attackTrait = Exts.Lazy(self.Trait); + manager = self.World.WorldActor.Trait(); + } + + public override void OnBecomingIdle(Actor self) + { + if (renderUnit.Value.DefaultAnimation.CurrentSequence.Name != Info.IdleSequence) + renderUnit.Value.DefaultAnimation.PlayRepeating("idle"); + + base.OnBecomingIdle(self); + } + + public override void DoAction(Actor self, CPos targetCell) + { + IsMovingTowardTarget = false; + + RescanForTargets(self); + + if (IsMovingTowardTarget) + return; + + self.QueueActivity(mobile.Value.MoveWithinRange(Target.FromCell(self.World, targetCell, SubCell.Any), WRange.FromCells(1))); + } + + public void Tick(Actor self) + { + if (--targetCountdown > 0 || IsAttacking || !self.IsInWorld) + return; + + RescanForTargets(self); + } + + void RescanForTargets(Actor self) + { + targetCountdown = Info.TargetRescanInterval; + + var actorsInRange = self.World.FindActorsInCircle(self.CenterPosition, Info.MaxSearchRadius); + var noiseDirection = WVec.Zero; + + foreach (var actor in actorsInRange) + { + if (!actor.IsInWorld) + continue; + + // TODO: Test if we really want to ignore actors that are on rock + if (!mobile.Value.CanEnterCell(actor.Location, null, false)) + continue; + + var noise = actor.TraitOrDefault(); + if (noise == null) + continue; + + var distance = actor.CenterPosition - self.CenterPosition; + var length = distance.Length; + + // Actor is too far to be heard + if (noise.Info.Range[noise.Info.Range.Length - 1].Range < length) + continue; + + // If close enough, we don't care about other actors + if (length <= Info.IgnoreNoiseAttackRange.Range) + { + self.CancelActivity(); + attackTrait.Value.ResolveOrder(self, new Order("Attack", actor, true) { TargetActor = actor }); + return; + } + + var direction = 1024 * distance / length; + var percentage = noise.GetNoisePercentageAtDistance(length); + + noiseDirection += direction * noise.Info.Intensity * percentage / 100; + } + + // No target was found + if (noiseDirection == WVec.Zero) + return; + + var moveTo = self.World.Map.CellContaining(self.CenterPosition + noiseDirection); + + while (!self.World.Map.Contains(moveTo) || !mobile.Value.CanEnterCell(moveTo, null, false)) + { + noiseDirection /= 2; + moveTo = self.World.Map.CellContaining(self.CenterPosition + noiseDirection); + } + + // Don't get stuck when the noise is distributed evenly! This will make the worm wander instead of trying to move to where it already is + if (moveTo == self.Location) + { + self.CancelActivity(); + return; + } + + self.QueueActivity(false, mobile.Value.MoveTo(moveTo, 3)); + IsMovingTowardTarget = true; + } + + public void Killed(Actor self, AttackInfo e) + { + manager.DecreaseWormCount(); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.D2k/Traits/World/WormManager.cs b/OpenRA.Mods.D2k/Traits/World/WormManager.cs index 661ef7dfbd..f3a5175656 100644 --- a/OpenRA.Mods.D2k/Traits/World/WormManager.cs +++ b/OpenRA.Mods.D2k/Traits/World/WormManager.cs @@ -27,31 +27,32 @@ namespace OpenRA.Mods.D2k.Traits [Desc("Maximum number of worms")] public readonly int Maximum = 4; - [Desc("Average time (seconds) between worm spawn")] - public readonly int SpawnInterval = 120; + [Desc("Time (in ticks) between worm spawn.")] + public readonly int SpawnInterval = 3000; + + [Desc("Name of the actor that will be spawned.")] + public readonly string WormSignature = "sandworm"; public readonly string WormSignNotification = "WormSign"; - - public readonly string WormSignature = "sandworm"; public readonly string WormOwnerPlayer = "Creeps"; - public object Create(ActorInitializer init) { return new WormManager(this, init.Self); } + public object Create(ActorInitializer init) { return new WormManager(init.Self, this); } } class WormManager : ITick { readonly WormManagerInfo info; - readonly Lazy spawnPoints; + readonly Lazy spawnPointActors; readonly Lazy radarPings; - int countdown; + int spawnCountdown; int wormsPresent; - public WormManager(WormManagerInfo info, Actor self) + public WormManager(Actor self, WormManagerInfo info) { this.info = info; radarPings = Exts.Lazy(() => self.World.WorldActor.Trait()); - spawnPoints = Exts.Lazy(() => self.World.ActorsWithTrait().Select(x => x.Actor).ToArray()); + spawnPointActors = Exts.Lazy(() => self.World.ActorsWithTrait().Select(x => x.Actor).ToArray()); } public void Tick(Actor self) @@ -59,18 +60,17 @@ namespace OpenRA.Mods.D2k.Traits if (!self.World.LobbyInfo.GlobalSettings.Creeps) return; - // TODO: It would be even better to stop - if (!spawnPoints.Value.Any()) + if (!spawnPointActors.Value.Any()) return; // Apparantly someone doesn't want worms or the maximum number of worms has been reached if (info.Maximum < 1 || wormsPresent >= info.Maximum) return; - if (--countdown > 0 && wormsPresent >= info.Minimum) + if (--spawnCountdown > 0 && wormsPresent >= info.Minimum) return; - countdown = info.SpawnInterval * 25; + spawnCountdown = info.SpawnInterval; var wormLocations = new List(); @@ -100,10 +100,10 @@ namespace OpenRA.Mods.D2k.Traits Actor GetRandomSpawnPoint(Actor self) { - return spawnPoints.Value.Random(self.World.SharedRandom); + return spawnPointActors.Value.Random(self.World.SharedRandom); } - public void DecreaseWorms() + public void DecreaseWormCount() { wormsPresent--; } @@ -120,8 +120,4 @@ namespace OpenRA.Mods.D2k.Traits radarPings.Value.Add(() => true, wormLocation, Color.Red, 50); } } - - [Desc("An actor with this trait indicates a valid spawn point for sandworms.")] - class WormSpawnerInfo : TraitInfo { } - class WormSpawner { } } diff --git a/OpenRA.Mods.D2k/Traits/WormSpawner.cs b/OpenRA.Mods.D2k/Traits/WormSpawner.cs new file mode 100644 index 0000000000..78f0290670 --- /dev/null +++ b/OpenRA.Mods.D2k/Traits/WormSpawner.cs @@ -0,0 +1,18 @@ +#region Copyright & License Information +/* +* Copyright 2007-2015 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 OpenRA.Traits; + +namespace OpenRA.Mods.D2k.Traits +{ + [Desc("An actor with this trait indicates a valid spawn point for sandworms.")] + class WormSpawnerInfo : TraitInfo { } + class WormSpawner { } +} \ No newline at end of file diff --git a/mods/d2k/rules/arrakis.yaml b/mods/d2k/rules/arrakis.yaml index 78d088fcb1..cc93955b22 100644 --- a/mods/d2k/rules/arrakis.yaml +++ b/mods/d2k/rules/arrakis.yaml @@ -24,7 +24,7 @@ SANDWORM: Name: Sandworm Description: Attracted by vibrations in the sand.\nWill eat units whole and has a large appetite. Health: - HP: 10000 + HP: 1200 Radius: 3 Armor: Type: None @@ -35,9 +35,7 @@ SANDWORM: Dune: 100 Spice: 100 TargetableUnit: - TargetTypes: Underground - RevealsShroud: - Range: 32c0 + TargetTypes: Ground RenderUnit: WithAttackOverlay: Sequence: sand @@ -47,9 +45,9 @@ SANDWORM: UseLocation: yes AttackSwallow: AttackRequiresEnteringCell: true - AttackMove: - AttackWander: - AutoTarget: - ScanRadius: 32 + IgnoresVisibility: true Armament: - Weapon: WormJaw \ No newline at end of file + Weapon: WormJaw + Sandworm: + WanderMoveRadius: 5 + IgnoresCloak: \ No newline at end of file diff --git a/mods/d2k/rules/atreides.yaml b/mods/d2k/rules/atreides.yaml index 655e1c295a..459d53922f 100644 --- a/mods/d2k/rules/atreides.yaml +++ b/mods/d2k/rules/atreides.yaml @@ -200,6 +200,8 @@ SONICTANK: EmptyWeapon: UnitExplodeSmall LeavesHusk: HuskActor: Sonictank.Husk + AttractsWorms: + Intensity: 600 SONICTANK.Husk: Inherits: ^Husk @@ -272,4 +274,6 @@ GRENADIER: Explodes: Weapon: UnitExplodeSmall Chance: 100 + AttractsWorms: + Intensity: 180 diff --git a/mods/d2k/rules/harkonnen.yaml b/mods/d2k/rules/harkonnen.yaml index 2a66d72cd8..06afb4f3f2 100644 --- a/mods/d2k/rules/harkonnen.yaml +++ b/mods/d2k/rules/harkonnen.yaml @@ -203,6 +203,8 @@ DEVAST: Bounds: 44,38,0,0 LeavesHusk: HuskActor: Devast.Husk + AttractsWorms: + Intensity: 700 DEVAST.Husk: Inherits: ^Husk @@ -239,4 +241,6 @@ SARDAUKAR: Armament@SECONDARY: Weapon: Slung AttackFrontal: + AttractsWorms: + Intensity: 180 diff --git a/mods/d2k/rules/infantry.yaml b/mods/d2k/rules/infantry.yaml index 4f655389a0..e5c5aae23d 100644 --- a/mods/d2k/rules/infantry.yaml +++ b/mods/d2k/rules/infantry.yaml @@ -19,6 +19,8 @@ RIFLE: Weapon: LMG AttackFrontal: TakeCover: + AttractsWorms: + Intensity: 120 ENGINEER: Inherits: ^Infantry @@ -46,6 +48,8 @@ ENGINEER: Captures: CaptureTypes: husk -AutoTarget: + AttractsWorms: + Intensity: 180 BAZOOKA: Inherits: ^Infantry @@ -69,6 +73,8 @@ BAZOOKA: LocalOffset: 0,0,555 AttackFrontal: TakeCover: + AttractsWorms: + Intensity: 180 MEDIC: Inherits: ^Infantry @@ -99,4 +105,6 @@ MEDIC: Passenger: PipType: Blue -AutoTarget: + AttractsWorms: + Intensity: 180 diff --git a/mods/d2k/rules/ordos.yaml b/mods/d2k/rules/ordos.yaml index 19fc9b2bb8..bf66215af9 100644 --- a/mods/d2k/rules/ordos.yaml +++ b/mods/d2k/rules/ordos.yaml @@ -179,6 +179,8 @@ RAIDER: Explodes: Weapon: UnitExplodeTiny EmptyWeapon: UnitExplodeTiny + AttractsWorms: + Intensity: 420 STEALTHRAIDER: Inherits: RAIDER @@ -247,6 +249,8 @@ DEVIATORTANK: Bounds: 30,30 LeavesHusk: HuskActor: Deviatortank.Husk + AttractsWorms: + Intensity: 600 DEVIATORTANK.Husk: Inherits: ^Husk @@ -278,4 +282,6 @@ SABOTEUR: C4Demolition: C4Delay: 45 -AutoTarget: + AttractsWorms: + Intensity: 120 diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index 298b37c885..58b1c67755 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -31,6 +31,8 @@ TransformSounds: BUILD1.WAV LeavesHusk: HuskActor: MCV.Husk + AttractsWorms: + Intensity: 700 MCV.Husk: Inherits: ^Husk @@ -82,6 +84,8 @@ HARVESTER: LeavesHusk: HuskActor: Harvester.Husk WithHarvestAnimation: + AttractsWorms: + Intensity: 700 HARVESTER.Husk: Inherits: ^Husk @@ -135,6 +139,8 @@ TRIKE: Explodes: Weapon: UnitExplodeTiny EmptyWeapon: UnitExplodeTiny + AttractsWorms: + Intensity: 420 TRIKE.starport: Inherits: TRIKE @@ -176,6 +182,8 @@ QUAD: EmptyWeapon: UnitExplodeTiny Selectable: Bounds: 24,24 + AttractsWorms: + Intensity: 470 QUAD.starport: Inherits: QUAD @@ -226,6 +234,8 @@ QUAD.starport: EmptyWeapon: UnitExplodeSmall Selectable: Bounds: 30,30 + AttractsWorms: + Intensity: 520 ^COMBAT.Husk: Inherits: ^Husk @@ -278,6 +288,8 @@ SIEGETANK: Bounds: 30,30 LeavesHusk: HuskActor: Siegetank.Husk + AttractsWorms: + Intensity: 600 SIEGETANK.Husk: Inherits: ^Husk @@ -333,6 +345,8 @@ MISSILETANK: Bounds: 30,30 LeavesHusk: HuskActor: Missiletank.Husk + AttractsWorms: + Intensity: 600 MISSILETANK.Husk: Inherits: ^Husk diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index 78827c2ef4..3b89b63125 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -201,4 +201,3 @@ World: RadarPings: ObjectivesPanel: PanelName: SKIRMISH_STATS - diff --git a/mods/ts/rules/infantry.yaml b/mods/ts/rules/infantry.yaml index d01208595f..1f50f9a01b 100644 --- a/mods/ts/rules/infantry.yaml +++ b/mods/ts/rules/infantry.yaml @@ -646,6 +646,9 @@ VISLRG: Weapon: SlimeAttack AttackFrontal: AttackWander: + WanderMoveRadius: 2 + MinMoveDelayInTicks: 25 + MaxMoveDelayInTicks: 45 -RenderInfantry: RenderUnit: -WithDeathAnimation: