context: #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 System.Collections.Generic;
  using System.Drawing;
  using System.Linq;
  using OpenRA.Graphics;
  using OpenRA.Mods.Common.Activities;
  using OpenRA.Mods.Common.Graphics;
  using OpenRA.Mods.Common.Orders;
  using OpenRA.Primitives;
  using OpenRA.Traits;
  
  namespace OpenRA.Mods.Common.Traits
  {
  	[Desc("Transports actors with the `Carryable` trait.")]
  	public class CarryallInfo : ITraitInfo, Requires<BodyOrientationInfo>, Requires<AircraftInfo>
  	{
  		[Desc("Delay on the ground while attaching an actor to the carryall.")]
  		public readonly int LoadingDelay = 0;
  
  		[Desc("Delay on the ground while detacting an actor to the carryall.")]
  		public readonly int UnloadingDelay = 0;
  
  		[Desc("Carryable attachment point relative to body.")]
  		public readonly WVec LocalOffset = WVec.Zero;
  
  		[Desc("Radius around the target drop location that are considered if the target tile is blocked.")]
  		public readonly WDist DropRange = WDist.FromCells(5);
  
  		[VoiceReference]
  		public readonly string Voice = "Action";
  
  		public virtual object Create(ActorInitializer init) { return new Carryall(init.Self, this); }
  	}
  
  	public class Carryall : INotifyKilled, ISync, ITick, IRender, INotifyActorDisposing, IIssueOrder, IResolveOrder, IOrderVoice
  	{
  		public enum CarryallState
  		{
  			Idle,
  			Reserved,
  			Carrying
  		}
  
  		public readonly CarryallInfo Info;
  		readonly AircraftInfo aircraftInfo;
  		readonly BodyOrientation body;
  		readonly IMove move;
  		readonly IFacing facing;
  
  		// The actor we are currently carrying.
  		[Sync] public Actor Carryable { get; private set; }
  		public CarryallState State { get; private set; }
  
  		int cachedFacing;
  		IActorPreview[] carryablePreview = null;
  
  		/// <summary>Offset between the carryall's and the carried actor's CenterPositions</summary>
  		public WVec CarryableOffset { get; private set; }
  
  		public Carryall(Actor self, CarryallInfo info)
  		{
  			Info = info;
  
  			Carryable = null;
  			State = CarryallState.Idle;
  
  			aircraftInfo = self.Info.TraitInfoOrDefault<AircraftInfo>();
  			body = self.Trait<BodyOrientation>();
  			move = self.Trait<IMove>();
  			facing = self.Trait<IFacing>();
  		}
  
  		void ITick.Tick(Actor self)
  		{
  			// Cargo may be killed in the same tick as, but after they are attached
  			if (Carryable != null && Carryable.IsDead)
  				DetachCarryable(self);
  
  			// HACK: We don't have an efficient way to know when the preview
  			// bounds change, so assume that we need to update the screen map
  			// (only) when the facing changes
  			if (facing.Facing != cachedFacing && carryablePreview != null)
  			{
  				self.World.ScreenMap.AddOrUpdate(self);
  				cachedFacing = facing.Facing;
  			}
  		}
  
  		void INotifyActorDisposing.Disposing(Actor self)
  		{
  			if (State == CarryallState.Carrying)
  			{
  				Carryable.Dispose();
  				Carryable = null;
  			}
  
  			UnreserveCarryable(self);
  		}
  
  		void INotifyKilled.Killed(Actor self, AttackInfo e)
  		{
  			if (State == CarryallState.Carrying)
  			{
  				if (Carryable.IsInWorld && !Carryable.IsDead)
  					Carryable.Kill(e.Attacker);
  				Carryable = null;
  			}
  
  			UnreserveCarryable(self);
  		}
  
  		public virtual bool RequestTransportNotify(Actor self, Actor carryable, CPos destination)
  		{
  			return false;
  		}
  
  		public virtual WVec OffsetForCarryable(Actor self, Actor carryable)
  		{
  			return Info.LocalOffset - carryable.Info.TraitInfo<CarryableInfo>().LocalOffset;
  		}
  
  		public virtual bool AttachCarryable(Actor self, Actor carryable)
  		{
  			if (State == CarryallState.Carrying)
  				return false;
  
  			Carryable = carryable;
  			State = CarryallState.Carrying;
  			self.World.ScreenMap.AddOrUpdate(self);
  
  			CarryableOffset = OffsetForCarryable(self, carryable);
  			return true;
  		}
  
  		public virtual void DetachCarryable(Actor self)
  		{
  			UnreserveCarryable(self);
  			self.World.ScreenMap.AddOrUpdate(self);
  
  			carryablePreview = null;
  			CarryableOffset = WVec.Zero;
  		}
  
  		public virtual bool ReserveCarryable(Actor self, Actor carryable)
  		{
  			if (State == CarryallState.Reserved)
  				UnreserveCarryable(self);
  
  			if (State != CarryallState.Idle || !carryable.Trait<Carryable>().Reserve(carryable, self))
  				return false;
  
  			Carryable = carryable;
  			State = CarryallState.Reserved;
  			return true;
  		}
  
  		public virtual void UnreserveCarryable(Actor self)
  		{
  			if (Carryable != null && Carryable.IsInWorld && !Carryable.IsDead)
  				Carryable.Trait<Carryable>().UnReserve(Carryable);
  
  			Carryable = null;
  			State = CarryallState.Idle;
  		}
  
  		IEnumerable<IRenderable> IRender.Render(Actor self, WorldRenderer wr)
  		{
  			if (State == CarryallState.Carrying && !Carryable.IsDead)
  			{
  				if (carryablePreview == null)
  				{
  					var carryableInits = new TypeDictionary()
  					{
  						new OwnerInit(Carryable.Owner),
  						new DynamicFacingInit(() => facing.Facing),
  					};
  
  					foreach (var api in Carryable.TraitsImplementing<IActorPreviewInitModifier>())
  						api.ModifyActorPreviewInit(Carryable, carryableInits);
  
  					var init = new ActorPreviewInitializer(Carryable.Info, wr, carryableInits);
  					carryablePreview = Carryable.Info.TraitInfos<IRenderActorPreviewInfo>()
  						.SelectMany(rpi => rpi.RenderPreview(init))
  						.ToArray();
  				}
  
  				var offset = body.LocalToWorld(CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)));
  				var previewRenderables = carryablePreview
  					.SelectMany(p => p.Render(wr, self.CenterPosition + offset))
  					.OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey);
  
  				foreach (var r in previewRenderables)
  					yield return r;
  			}
  		}
  
  		IEnumerable<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr)
  		{
  			if (carryablePreview == null)
  				yield break;
  
  			var pos = self.CenterPosition;
  			foreach (var p in carryablePreview)
  
gt: 				foreach (var b in p.ScreenBounds(wr, pos))