diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 030de1e925..20a5e3446b 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -100,7 +100,6 @@ namespace OpenRA.Traits public interface INotifyOwnerChanged { void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner); } public interface INotifyEffectiveOwnerChanged { void OnEffectiveOwnerChanged(Actor self, Player oldEffectiveOwner, Player newEffectiveOwner); } public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); } - public interface INotifyHarvest { void Harvested(Actor self, ResourceType resource); } public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); } public interface IDisableMove { bool MoveDisabled(Actor self); } diff --git a/OpenRA.Mods.Common/CommonTraitsInterfaces.cs b/OpenRA.Mods.Common/CommonTraitsInterfaces.cs index d1fb1b6395..c2ed893c41 100644 --- a/OpenRA.Mods.Common/CommonTraitsInterfaces.cs +++ b/OpenRA.Mods.Common/CommonTraitsInterfaces.cs @@ -28,4 +28,12 @@ namespace OpenRA.Mods.Common bool AcceptsUpgradeLevel(Actor self, string type, int level); void UpgradeLevelChanged(Actor self, string type, int oldLevel, int newLevel); } + + public interface INotifyHarvesterAction + { + void MovingToResources(Actor self, CPos targetCell, Activity next); + void MovingToRefinery(Actor self, CPos targetCell, Activity next); + void MovementCancelled(Actor self); + void Harvested(Actor self, ResourceType resource); + } } diff --git a/OpenRA.Mods.D2k/AutoCarryall/AutoCarryall.cs b/OpenRA.Mods.D2k/AutoCarryall/AutoCarryall.cs new file mode 100644 index 0000000000..e01c3769f5 --- /dev/null +++ b/OpenRA.Mods.D2k/AutoCarryall/AutoCarryall.cs @@ -0,0 +1,175 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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 System.Collections.Generic; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.D2k +{ + [Desc("Automatically transports harvesters with the Carryable trait between resource fields and refineries")] + public class AutoCarryallInfo : ITraitInfo, Requires + { + public object Create(ActorInitializer init) { return new AutoCarryall(init.self, this); } + } + + public class AutoCarryall : INotifyBecomingIdle, INotifyKilled, ISync, IRender + { + readonly Actor self; + readonly WRange carryHeight; + + // The actor we are currently carrying. + [Sync] Actor carrying; + + // TODO: Use ActorPreviews so that this can support actors with multiple sprites + Animation anim; + + public bool Busy { get; internal set; } + + public AutoCarryall(Actor self, AutoCarryallInfo info) + { + this.self = self; + carryHeight = self.Trait().Info.LandAltitude; + } + + public void OnBecomingIdle(Actor self) + { + FindCarryableForTransport(); + + if (!Busy) + self.QueueActivity(new HeliFlyCircle(self)); + } + + // A carryable notifying us that he'd like to be carried + public bool RequestTransportNotify(Actor carryable) + { + if (Busy) + return false; + + if (ReserveCarryable(carryable)) + { + self.QueueActivity(false, new CarryUnit(self, carryable)); + return true; + } + + return false; + } + + void FindCarryableForTransport() + { + // get all carryables who want transport + var carryables = self.World.ActorsWithTrait() + .Where(p => + { + var actor = p.Actor; + if (actor == null) + return false; + + if (actor.Owner != self.Owner) + return false; + + if (actor.IsDead) + return false; + + var trait = p.Trait; + if (trait.Reserved) + return false; + + if (!trait.WantsTransport) + return false; + + return true; + }) + .OrderBy(p => (self.Location - p.Actor.Location).LengthSquared); + + + foreach (var p in carryables) + { + // Check if its actually me who's the best candidate + if (p.Trait.GetClosestIdleCarrier() == self && ReserveCarryable(p.Actor)) + { + self.QueueActivity(false, new CarryUnit(self, p.Actor)); + break; + } + } + } + + // Reserve the carryable so its ours exclusively + public bool ReserveCarryable(Actor carryable) + { + if (carryable.Trait().Reserve(self)) + { + carrying = carryable; + Busy = true; + return true; + } + + return false; + } + + // Unreserve the carryable + public void UnreserveCarryable() + { + if (carrying != null) + { + if (carrying.IsInWorld && !carrying.IsDead) + carrying.Trait().UnReserve(self); + + carrying = null; + } + + Busy = false; + } + + // INotifyKilled + public void Killed(Actor self, AttackInfo e) + { + if (carrying != null) + { + carrying.Kill(e.Attacker); + carrying = null; + } + + UnreserveCarryable(); + } + + // Called when carryable is inside. + public void AttachCarryable(Actor carryable) + { + // Create a new animation for our carryable unit + anim = new Animation(self.World, RenderSprites.GetImage(carryable.Info), RenderSprites.MakeFacingFunc(self)); + anim.PlayRepeating("idle"); + } + + // Called when released + public void CarryableReleased() + { + anim = null; + } + + public IEnumerable Render(Actor self, WorldRenderer wr) + { + // Render the carryable below us TODO: Implement RenderSprites trait + if (anim != null && !self.World.FogObscures(self)) + { + anim.Tick(); + var renderables = anim.Render(self.CenterPosition + new WVec(0, 0, -carryHeight.Range), wr.Palette("player" + carrying.Owner.InternalName)); + + foreach (var rr in renderables) + yield return rr; + } + } + } +} diff --git a/OpenRA.Mods.D2k/AutoCarryall/CarryUnit.cs b/OpenRA.Mods.D2k/AutoCarryall/CarryUnit.cs new file mode 100644 index 0000000000..e6e6bb272a --- /dev/null +++ b/OpenRA.Mods.D2k/AutoCarryall/CarryUnit.cs @@ -0,0 +1,196 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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 System.Drawing; +using System.Linq; +using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.D2k +{ + public class CarryUnit : Activity + { + readonly Actor self; + readonly Actor carryable; + readonly IMove movement; + readonly Carryable c; + readonly AutoCarryall aca; + readonly Helicopter helicopter; + readonly IPositionable positionable; + readonly IFacing cFacing; // Carryable facing + readonly IFacing sFacing; // Self facing + + enum State { Intercept, LockCarryable, MoveToCarryable, Turn, Pickup, Transport, Land, Release, Takeoff, Done } + + State state; + + public CarryUnit(Actor self, Actor carryable) + { + this.self = self; + this.carryable = carryable; + movement = self.Trait(); + c = carryable.Trait(); + aca = self.Trait(); + helicopter = self.Trait(); + positionable = carryable.Trait(); + cFacing = carryable.Trait(); + sFacing = self.Trait(); + + state = State.Intercept; + } + + // Find a suitable location to drop our carryable + CPos GetLocationToDrop(CPos requestedPosition) + { + if (positionable.CanEnterCell(requestedPosition)) + return requestedPosition; + + var candidateCells = Util.AdjacentCells(self.World, Target.FromCell(self.World, requestedPosition)); + + // TODO: This will behave badly if there is no suitable drop point nearby + do + { + foreach (var c in candidateCells) + if (positionable.CanEnterCell(c)) + return c; + + // Expanding dropable cells search area + // TODO: This also includes all of the cells we have just checked + candidateCells = Util.ExpandFootprint(candidateCells, true); + } while (true); + } + + // Check if we can drop the unit at our current location. + bool CanDropHere() + { + return positionable.CanEnterCell(self.Location); + } + + public override Activity Tick(Actor self) + { + if (carryable.IsDead) + { + aca.UnreserveCarryable(); + return NextActivity; + } + + switch (state) + { + case State.Intercept: // Move towards our carryable + + state = State.LockCarryable; + return Util.SequenceActivities(movement.MoveWithinRange(Target.FromActor(carryable), WRange.FromCells(4)), this); + + case State.LockCarryable: + // Last check + if (c.StandbyForPickup(self)) + { + state = State.MoveToCarryable; + return this; + } + else + { + // We got cancelled + aca.UnreserveCarryable(); + return NextActivity; + } + + case State.MoveToCarryable: // We arrived, move on top + + if (self.Location == carryable.Location) + { + state = State.Turn; + return this; + } + else + return Util.SequenceActivities(movement.MoveTo(carryable.Location, 0), this); + + case State.Turn: // Align facing and Land + + if (sFacing.Facing != cFacing.Facing) + return Util.SequenceActivities(new Turn(self, cFacing.Facing), this); + else + { + state = State.Pickup; + return Util.SequenceActivities(new HeliLand(false), new Wait(10), this); + } + + case State.Pickup: + + // Remove our carryable from world + self.World.AddFrameEndTask(w => carryable.World.Remove(carryable)); + + aca.AttachCarryable(carryable); + state = State.Transport; + return this; + + case State.Transport: + + // Move self to destination + var targetl = GetLocationToDrop(c.Destination); + + state = State.Land; + return Util.SequenceActivities(movement.MoveTo(targetl, 0), this); + + case State.Land: + + if (!CanDropHere()) + { + state = State.Transport; + return this; + } + + if (HeliFly.AdjustAltitude(self, helicopter, helicopter.Info.LandAltitude)) + return this; + else + { + state = State.Release; + return Util.SequenceActivities(new Wait(15), this); + } + + case State.Release: + + if (!CanDropHere()) + { + state = State.Transport; + return this; + } + + positionable.SetPosition(carryable, self.Location, SubCell.FullCell); + + cFacing.Facing = sFacing.Facing; + + // Put back into world + self.World.AddFrameEndTask(w => carryable.World.Add(carryable)); + + // Unlock carryable + aca.CarryableReleased(); + c.Dropped(); + + state = State.Done; + return Util.SequenceActivities(new Wait(10), this); + + case State.Done: + + self.Trait().UnreserveCarryable(); + return NextActivity; + } + + return NextActivity; + } + + public override void Cancel(Actor self) + { + // TODO: Drop the unit at the nearest available cell + } + } +} diff --git a/OpenRA.Mods.D2k/AutoCarryall/Carryable.cs b/OpenRA.Mods.D2k/AutoCarryall/Carryable.cs new file mode 100644 index 0000000000..5444f8174c --- /dev/null +++ b/OpenRA.Mods.D2k/AutoCarryall/Carryable.cs @@ -0,0 +1,166 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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 System.Linq; +using OpenRA.Mods.Common; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.D2k +{ + [Desc("Can be carried by units with the trait `AutoCarryall`.")] + public class CarryableInfo : ITraitInfo + { + [Desc("Required distance away from destination before requesting a pickup.")] + public int MinDistance = 6; + + public object Create(ActorInitializer init) { return new Carryable(init.self, this); } + } + + public class Carryable : IDisableMove, INotifyHarvesterAction + { + readonly CarryableInfo info; + readonly Actor self; + + public bool Reserved { get; private set; } + + // If we're locked there isnt much we can do. We'll have to wait for the carrier to finish with us. We should not move or get new orders! + bool locked; + + public bool WantsTransport { get; private set; } + public CPos Destination; + Activity afterLandActivity; + + public Carryable(Actor self, CarryableInfo info) + { + this.info = info; + this.self = self; + + locked = false; + Reserved = false; + WantsTransport = false; + } + + public void MovingToResources(Actor self, CPos targetCell, Activity next) { RequestTransport(targetCell, next); } + public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { RequestTransport(targetCell, next); } + + void RequestTransport(CPos destination, Activity afterLandActivity) + { + if (locked || Reserved) + return; + + if (destination == CPos.Zero) + return; + + if ((self.Location - destination).Length < info.MinDistance) + return; + + Destination = destination; + this.afterLandActivity = afterLandActivity; + WantsTransport = true; + + // Inform all idle carriers + var carriers = self.World.ActorsWithTrait() + .Where(c => !c.Trait.Busy && !c.Actor.IsDead && c.Actor.Owner == self.Owner) + .OrderBy(p => (self.Location - p.Actor.Location).LengthSquared); + + foreach (var carrier in carriers) + { + // Notify the carrier and see if he's willing to transport us.. + if (carrier.Trait.RequestTransportNotify(self)) + break; // If true then we're done + } + } + + // No longer want to be carried + public void MovementCancelled(Actor self) + { + if (locked) + return; + + WantsTransport = false; + Reserved = false; + + // TODO: We could implement something like a carrier.Trait().CancelTransportNotify(self) and call it here + } + + // We do not handle Harvested notification + public void Harvested(Actor self, ResourceType resource) { } + + public Actor GetClosestIdleCarrier() + { + // Find carriers + var carriers = self.World.ActorsWithTrait() + .Where(p => p.Actor.Owner == self.Owner && !p.Trait.Busy) + .Select(h => h.Actor); + + return WorldUtils.ClosestTo(carriers, self); + } + + // This gets called by carrier after we touched down + public void Dropped() + { + WantsTransport = false; + locked = false; + + if (afterLandActivity != null) + self.QueueActivity(false, afterLandActivity); + } + + public bool Reserve(Actor carrier) + { + if ((self.Location - Destination).Length < info.MinDistance) + { + MovementCancelled(self); + return false; + } + + Reserved = true; + + return true; + } + + public void UnReserve(Actor carrier) + { + Reserved = false; + locked = false; + } + + // Prepare for transport pickup + public bool StandbyForPickup(Actor carrier) + { + if (Destination == CPos.Zero) + return false; + + if (locked || !WantsTransport) + return false; + + // Last change to change our mind... + if ((self.Location - Destination).Length < info.MinDistance) + { + MovementCancelled(self); + return false; + } + + // Cancel our activities + self.CancelActivity(); + locked = true; + + return true; + } + + // IMoveDisabled + public bool MoveDisabled(Actor self) + { + // We do not want to move while being locked. The carrier will try to pick us up. + return locked; + } + } +} diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj index 5530059d4a..1cb2379b4f 100644 --- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj +++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj @@ -86,6 +86,9 @@ + + + diff --git a/OpenRA.Mods.RA/Activities/Air/HeliFly.cs b/OpenRA.Mods.RA/Activities/Air/HeliFly.cs index 37c0f424fd..e517cc9281 100644 --- a/OpenRA.Mods.RA/Activities/Air/HeliFly.cs +++ b/OpenRA.Mods.RA/Activities/Air/HeliFly.cs @@ -14,7 +14,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Activities { - class HeliFly : Activity + public class HeliFly : Activity { readonly Helicopter helicopter; readonly Target target; diff --git a/OpenRA.Mods.RA/Activities/Air/HeliFlyCircle.cs b/OpenRA.Mods.RA/Activities/Air/HeliFlyCircle.cs new file mode 100644 index 0000000000..5229d18f59 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/Air/HeliFlyCircle.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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 System.Collections.Generic; +using OpenRA.Traits; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + public class HeliFlyCircle : Activity + { + readonly Helicopter helicopter; + + public HeliFlyCircle(Actor self) + { + helicopter = self.Trait(); + } + + public override Activity Tick(Actor self) + { + if (IsCanceled) + return NextActivity; + + if (HeliFly.AdjustAltitude(self, helicopter, helicopter.Info.CruiseAltitude)) + return this; + + var move = helicopter.FlyStep(helicopter.Facing); + helicopter.SetPosition(self, helicopter.CenterPosition + move); + + var desiredFacing = helicopter.Facing + 64; + helicopter.Facing = Util.TickFacing(helicopter.Facing, desiredFacing, helicopter.ROT / 3); + + return this; + } + } +} diff --git a/OpenRA.Mods.RA/Activities/Air/HeliLand.cs b/OpenRA.Mods.RA/Activities/Air/HeliLand.cs index 23cf807699..8efda467b6 100644 --- a/OpenRA.Mods.RA/Activities/Air/HeliLand.cs +++ b/OpenRA.Mods.RA/Activities/Air/HeliLand.cs @@ -13,7 +13,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Activities { - class HeliLand : Activity + public class HeliLand : Activity { bool requireSpace; diff --git a/OpenRA.Mods.RA/Activities/DeliverResources.cs b/OpenRA.Mods.RA/Activities/DeliverResources.cs index 9bbb93e504..d1f39f503e 100755 --- a/OpenRA.Mods.RA/Activities/DeliverResources.cs +++ b/OpenRA.Mods.RA/Activities/DeliverResources.cs @@ -9,6 +9,7 @@ #endregion using System.Drawing; +using OpenRA.Mods.Common; using OpenRA.Traits; namespace OpenRA.Mods.RA.Activities @@ -55,7 +56,14 @@ namespace OpenRA.Mods.RA.Activities self.SetTargetLine(Target.FromActor(proc), Color.Green, false); if (self.Location != proc.Location + iao.DeliverOffset) + { + var notify = self.TraitsImplementing(); + var next = new DeliverResources(); + foreach (var n in notify) + n.MovingToRefinery(self, proc.Location + iao.DeliverOffset, next); + return Util.SequenceActivities(movement.MoveTo(proc.Location + iao.DeliverOffset, 0), this); + } if (!isDocking) { diff --git a/OpenRA.Mods.RA/Activities/FindResources.cs b/OpenRA.Mods.RA/Activities/FindResources.cs index eca23337fc..2f3181f7a1 100755 --- a/OpenRA.Mods.RA/Activities/FindResources.cs +++ b/OpenRA.Mods.RA/Activities/FindResources.cs @@ -111,6 +111,12 @@ namespace OpenRA.Mods.RA.Activities harv.LastOrderLocation = path[0]; self.SetTargetLine(Target.FromCell(self.World, path[0]), Color.Red, false); + + var notify = self.TraitsImplementing(); + var next = new FindResources(); + foreach (var n in notify) + n.MovingToResources(self, path[0], next); + return Util.SequenceActivities(mobile.MoveTo(path[0], 1), new HarvestResource(), new FindResources()); } @@ -163,7 +169,7 @@ namespace OpenRA.Mods.RA.Activities harv.AcceptResource(resource); - foreach (var t in self.TraitsImplementing()) + foreach (var t in self.TraitsImplementing()) t.Harvested(self, resource); return Util.SequenceActivities(new Wait(harvInfo.LoadTicksPerBale), this); diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index 265faaeee3..b5160885ce 100644 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -176,7 +176,12 @@ namespace OpenRA.Mods.RA var territory = self.World.WorldActor.TraitOrDefault(); if (territory != null) territory.ClaimResource(self, moveTo); - self.QueueActivity(new FindResources()); + var notify = self.TraitsImplementing(); + var next = new FindResources(); + foreach (var n in notify) + n.MovingToResources(self, moveTo, next); + + self.QueueActivity(next); return; } } @@ -302,6 +307,11 @@ namespace OpenRA.Mods.RA self.QueueActivity(mobile.MoveTo(loc, 0)); self.SetTargetLine(Target.FromCell(self.World, loc), Color.Red); + var notify = self.TraitsImplementing(); + var next = new FindResources(); + foreach (var n in notify) + n.MovingToResources(self, loc, next); + LastOrderLocation = loc; } else @@ -341,9 +351,18 @@ namespace OpenRA.Mods.RA self.CancelActivity(); self.QueueActivity(new DeliverResources()); + + var notify = self.TraitsImplementing(); + var next = new FindResources(); + foreach (var n in notify) + n.MovingToResources(self, order.TargetLocation, next); } else if (order.OrderString == "Stop" || order.OrderString == "Move") { + var notify = self.TraitsImplementing(); + foreach (var n in notify) + n.MovementCancelled(self); + // Turn off idle smarts to obey the stop/move: idleSmart = false; } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index f89d465c1b..8c06bc2700 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -476,6 +476,7 @@ + diff --git a/OpenRA.Mods.RA/Render/RenderHarvester.cs b/OpenRA.Mods.RA/Render/RenderHarvester.cs index c47e99a12d..1ae9ec1842 100644 --- a/OpenRA.Mods.RA/Render/RenderHarvester.cs +++ b/OpenRA.Mods.RA/Render/RenderHarvester.cs @@ -10,6 +10,7 @@ using OpenRA.Graphics; using OpenRA.Traits; +using OpenRA.Mods.Common; namespace OpenRA.Mods.RA.Render { @@ -19,7 +20,7 @@ namespace OpenRA.Mods.RA.Render public override object Create(ActorInitializer init) { return new RenderHarvester(init.self, this); } } - class RenderHarvester : RenderUnit, INotifyHarvest + class RenderHarvester : RenderUnit, INotifyHarvesterAction { Harvester harv; RenderHarvesterInfo info; @@ -51,5 +52,9 @@ namespace OpenRA.Mods.RA.Render if (DefaultAnimation.CurrentSequence.Name != "harvest") PlayCustomAnim(self, "harvest"); } + + public void MovingToResources(Actor self, CPos targetCell, Activity next) { } + public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { } + public void MovementCancelled(Actor self) { } } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/Render/WithHarvestAnimation.cs b/OpenRA.Mods.RA/Render/WithHarvestAnimation.cs index eeba193aff..cafb2d677f 100644 --- a/OpenRA.Mods.RA/Render/WithHarvestAnimation.cs +++ b/OpenRA.Mods.RA/Render/WithHarvestAnimation.cs @@ -9,6 +9,7 @@ #endregion using OpenRA.Graphics; +using OpenRA.Mods.Common; using OpenRA.Mods.Common.Traits.Render; using OpenRA.Traits; @@ -26,7 +27,7 @@ namespace OpenRA.Mods.RA.Render public object Create(ActorInitializer init) { return new WithHarvestAnimation(init.self, this); } } - class WithHarvestAnimation : INotifyHarvest + class WithHarvestAnimation : INotifyHarvesterAction { WithHarvestAnimationInfo info; Animation anim; @@ -55,5 +56,9 @@ namespace OpenRA.Mods.RA.Render visible = true; anim.PlayThen(info.Sequence, () => visible = false); } + + public void MovingToResources(Actor self, CPos targetCell, Activity next) { } + public void MovingToRefinery(Actor self, CPos targetCell, Activity next) { } + public void MovementCancelled(Actor self) { } } } diff --git a/OpenRA.Mods.RA/Traits/Air/Helicopter.cs b/OpenRA.Mods.RA/Traits/Air/Helicopter.cs index 2e303216ea..ccb71305cb 100644 --- a/OpenRA.Mods.RA/Traits/Air/Helicopter.cs +++ b/OpenRA.Mods.RA/Traits/Air/Helicopter.cs @@ -18,7 +18,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Traits { - class HelicopterInfo : AircraftInfo, IMoveInfo + public class HelicopterInfo : AircraftInfo, IMoveInfo { [Desc("Allow the helicopter land after it has no more commands.")] public readonly bool LandWhenIdle = true; @@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA.Traits public override object Create(ActorInitializer init) { return new Helicopter(init, this); } } - class Helicopter : Aircraft, ITick, IResolveOrder, IMove + public class Helicopter : Aircraft, ITick, IResolveOrder, IMove { public HelicopterInfo Info; Actor self; diff --git a/mods/d2k/rules/ai.yaml b/mods/d2k/rules/ai.yaml index 0daaaa3e00..d5f2893331 100644 --- a/mods/d2k/rules/ai.yaml +++ b/mods/d2k/rules/ai.yaml @@ -78,6 +78,9 @@ Player: pwrh: 10% pwro: 10% UnitsToBuild: + carryalla: 1% + carryallh: 1% + carryallo: 1% rifle: 6% bazooka: 5% medic: 1% @@ -237,6 +240,9 @@ Player: pwrh: 12% pwro: 12% UnitsToBuild: + carryalla: 1% + carryallh: 1% + carryallo: 1% rifle: 2% bazooka: 2% medic: 0.5% @@ -396,6 +402,9 @@ Player: pwrh: 10% pwro: 10% UnitsToBuild: + carryalla: 1% + carryallh: 1% + carryallo: 1% rifle: 15% bazooka: 13% medic: 2% diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index c32836876e..d0e0f3a433 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -4,31 +4,27 @@ Cost: 1200 Tooltip: Name: Carryall - Description: Fast drop ship.\n Unarmed + Description: Fully automated Carryall.\n Automatically transports your harvesters. Health: HP: 250 Armor: Type: Light - RevealsShroud: - Range: 12c0 Helicopter: + CruiseAltitude: 2100 InitialFacing: 0 ROT: 4 - Speed: 210 + Speed: 160 LandableTerrainTypes: Sand, Rock, Transition, Spice, Dune RepairBuildings: repaira,repairo,repairh RearmBuildings: starporta,starporto,starporth - LandAltitude: 800 - RenderUnit: - WithCargo: - LocalOffset: 0,0,-854 + Repulsable: False + LandAltitude: 100 + LandWhenIdle: False WithShadow: - Cargo: - Types: Vehicle - MaxWeight: 1 - PipCount: 1 LeavesHusk: HuskActor: CARRYALL.Husk + -Selectable: + AutoCarryall: FRIGATE: ParaDrop: diff --git a/mods/d2k/rules/atreides.yaml b/mods/d2k/rules/atreides.yaml index d6b49db6cc..ab8e0dbfa0 100644 --- a/mods/d2k/rules/atreides.yaml +++ b/mods/d2k/rules/atreides.yaml @@ -122,11 +122,17 @@ CARRYALLA: Inherits: ^CARRYALL RenderUnit: Image: CARRYALL + Buildable: + Queue: Armor + Prerequisites: ~heavya, refinery, hightech + BuildPaletteOrder: 10 CARRYALLA.starport: Inherits: CARRYALLA Valued: Cost: 1500 + Buildable: + Queue: Starport COMBATA: Inherits: ^COMBAT diff --git a/mods/d2k/rules/harkonnen.yaml b/mods/d2k/rules/harkonnen.yaml index 2b41aaf1ed..c3a359d195 100644 --- a/mods/d2k/rules/harkonnen.yaml +++ b/mods/d2k/rules/harkonnen.yaml @@ -126,11 +126,17 @@ CARRYALLH: Inherits: ^CARRYALL RenderUnit: Image: CARRYALL + Buildable: + Queue: Armor + Prerequisites: ~heavyh, refinery, hightech + BuildPaletteOrder: 10 CARRYALLH.starport: Inherits: CARRYALLH Valued: Cost: 1500 + Buildable: + Queue: Starport COMBATH: Inherits: ^COMBAT diff --git a/mods/d2k/rules/ordos.yaml b/mods/d2k/rules/ordos.yaml index cf1d2f53e6..2545190566 100644 --- a/mods/d2k/rules/ordos.yaml +++ b/mods/d2k/rules/ordos.yaml @@ -202,11 +202,17 @@ CARRYALLO: Inherits: ^CARRYALL RenderUnit: Image: CARRYALL + Buildable: + Queue: Armor + Prerequisites: ~heavyo, refinery, hightech + BuildPaletteOrder: 10 CARRYALLO.starport: Inherits: CARRYALLO Valued: Cost: 1500 + Buildable: + Queue: Starport DEVIATORTANK: Inherits: ^Tank diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index 25b1c9dc80..3706689135 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -63,6 +63,7 @@ HARVESTER: UnloadTicksPerBale: 5 SearchFromProcRadius: 24 SearchFromOrderRadius: 12 + Carryable: Health: HP: 1000 Armor: