#region Copyright & License Information /* * Copyright 2007-2017 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 OpenRA.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; namespace OpenRA.Mods.Common.Activities { public class PickupUnit : Activity { readonly Actor cargo; readonly IMove movement; readonly Carryall carryall; readonly IFacing carryallFacing; readonly Carryable carryable; readonly IFacing carryableFacing; readonly BodyOrientation carryableBody; readonly int delay; enum PickupState { Intercept, LockCarryable, MoveToCarryable, Turn, Land, Wait, Pickup, Aborted } PickupState state; Activity innerActivity; public PickupUnit(Actor self, Actor cargo, int delay) { this.cargo = cargo; this.delay = delay; carryable = cargo.Trait(); carryableFacing = cargo.Trait(); carryableBody = cargo.Trait(); movement = self.Trait(); carryall = self.Trait(); carryallFacing = self.Trait(); state = PickupState.Intercept; } public override Activity Tick(Actor self) { if (innerActivity != null) { innerActivity = ActivityUtils.RunActivity(self, innerActivity); return this; } if (cargo != carryall.Carryable) return NextActivity; if (cargo.IsDead || IsCanceled || carryable.IsTraitDisabled) { carryall.UnreserveCarryable(self); return NextActivity; } if (carryall.State == Carryall.CarryallState.Idle) return NextActivity; switch (state) { case PickupState.Intercept: innerActivity = movement.MoveWithinRange(Target.FromActor(cargo), WDist.FromCells(4)); state = PickupState.LockCarryable; return this; case PickupState.LockCarryable: state = PickupState.MoveToCarryable; if (!carryable.LockForPickup(cargo, self)) state = PickupState.Aborted; return this; case PickupState.MoveToCarryable: { // Line up with the attachment point var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset); if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0) { // Run the first tick of the move activity immediately to avoid a one-frame pause innerActivity = ActivityUtils.RunActivity(self, new HeliFly(self, Target.FromPos(targetPosition))); return this; } state = PickupState.Turn; return this; } case PickupState.Turn: if (carryallFacing.Facing != carryableFacing.Facing) { innerActivity = new Turn(self, carryableFacing.Facing); return this; } state = PickupState.Land; return this; case PickupState.Land: { var localOffset = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation)); var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset); if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0 || carryallFacing.Facing != carryableFacing.Facing) { state = PickupState.MoveToCarryable; return this; } if (targetPosition.Z != self.CenterPosition.Z) { innerActivity = new HeliLand(self, false, self.World.Map.DistanceAboveTerrain(targetPosition)); return this; } state = delay > 0 ? PickupState.Wait : PickupState.Pickup; return this; } case PickupState.Wait: state = PickupState.Pickup; innerActivity = new Wait(delay, false); return this; case PickupState.Pickup: // Remove our carryable from world Attach(self); return NextActivity; case PickupState.Aborted: // We got cancelled carryall.UnreserveCarryable(self); break; } return NextActivity; } void Attach(Actor self) { self.World.AddFrameEndTask(w => { cargo.World.Remove(cargo); carryable.Attached(cargo); carryall.AttachCarryable(self, cargo); }); } public override bool Cancel(Actor self) { if (!IsCanceled && innerActivity != null && !innerActivity.Cancel(self)) return false; return base.Cancel(self); } } }