context: #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 System.Collections.Generic;
  using System.Linq;
  using OpenRA.Mods.Common.Activities;
  using OpenRA.Mods.Common.Orders;
  using OpenRA.Primitives;
  using OpenRA.Traits;
  
  namespace OpenRA.Mods.Common.Traits
  {
  	[Desc("This actor can transport Passenger actors.")]
  	public class CargoInfo : ITraitInfo, Requires<IOccupySpaceInfo>
  	{
  		public readonly int MaxWeight = 0;
  		public readonly int PipCount = 0;
  		public readonly string[] Types = { };
  		public readonly string[] InitialUnits = { };
  		public readonly bool EjectOnSell = true;
  
  		[Desc("Which direction the passenger will face (relative to the transport) when unloading.")]
  		public readonly int PassengerFacing = 128;
  
  		public object Create(ActorInitializer init) { return new Cargo(init, this); }
  	}
  
  	public class Cargo : IPips, IIssueOrder, IResolveOrder, IOrderVoice, INotifyCreated, INotifyKilled, INotifyOwnerChanged, INotifyAddedToWorld, ITick, INotifySold, IDisableMove
  	{
  		public readonly CargoInfo Info;
  		readonly Actor self;
  		readonly Stack<Actor> cargo = new Stack<Actor>();
  		readonly HashSet<Actor> reserves = new HashSet<Actor>();
  		readonly Lazy<IFacing> facing;
  
  		int totalWeight = 0;
  		int reservedWeight = 0;
  		Helicopter helicopter;
  
  		CPos currentCell;
  		public IEnumerable<CPos> CurrentAdjacentCells { get; private set; }
  		public bool Unloading { get; internal set; }
  		public IEnumerable<Actor> Passengers { get { return cargo; } }
  		public int PassengerCount { get { return cargo.Count; } }
  
  		public Cargo(ActorInitializer init, CargoInfo info)
  		{
  			self = init.Self;
  			Info = info;
  			Unloading = false;
  
  			if (init.Contains<RuntimeCargoInit>())
  			{
  				cargo = new Stack<Actor>(init.Get<RuntimeCargoInit, Actor[]>());
  				totalWeight = cargo.Sum(c => GetWeight(c));
  			}
  			else if (init.Contains<CargoInit>())
  			{
  				foreach (var u in init.Get<CargoInit, string[]>())
  				{
  					var unit = self.World.CreateActor(false, u.ToLowerInvariant(),
  						new TypeDictionary { new OwnerInit(self.Owner) });
  
  					cargo.Push(unit);
  				}
  
  				totalWeight = cargo.Sum(c => GetWeight(c));
  			}
  			else
  			{
  				foreach (var u in info.InitialUnits)
  				{
  					var unit = self.World.CreateActor(false, u.ToLowerInvariant(),
  						new TypeDictionary { new OwnerInit(self.Owner) });
  
  					cargo.Push(unit);
  				}
  
  				totalWeight = cargo.Sum(c => GetWeight(c));
  			}
  
  			facing = Exts.Lazy(self.TraitOrDefault<IFacing>);
  		}
  
  		public void Created(Actor self)
  		{
  			helicopter = self.TraitOrDefault<Helicopter>();
  		}
  
  		static int GetWeight(Actor a) { return a.Info.Traits.Get<PassengerInfo>().Weight; }
  
  		public IEnumerable<IOrderTargeter> Orders
  		{
  			get { yield return new DeployOrderTargeter("Unload", 10, CanUnload); }
  		}
  
  		public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
  		{
  			if (order.OrderID == "Unload")
  				return new Order(order.OrderID, self, queued);
  
  			return null;
  		}
  
  		public void ResolveOrder(Actor self, Order order)
  		{
  			if (order.OrderString == "Unload")
  			{
  				if (!CanUnload())
  					return;
  
  				Unloading = true;
  				self.CancelActivity();
  				if (helicopter != null)
  					self.QueueActivity(new HeliLand(true));
  				self.QueueActivity(new UnloadCargo(self, true));
  			}
  		}
  
  		IEnumerable<CPos> GetAdjacentCells()
  		{
  			return Util.AdjacentCells(self.World, Target.FromActor(self)).Where(c => self.Location != c);
  		}
  
  		bool CanUnload()
  		{
  			return !IsEmpty(self) && (helicopter == null || helicopter.CanLand(self.Location))
  				&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => p.Trait<IPositionable>().CanEnterCell(c)));
  		}
  
  		public bool CanLoad(Actor self, Actor a)
  		{
  			return (reserves.Contains(a) || HasSpace(GetWeight(a))) && self.CenterPosition.Z == 0;
  		}
  
  		internal bool ReserveSpace(Actor a)
  		{
  			if (reserves.Contains(a))
  				return true;
  
  			var w = GetWeight(a);
  			if (!HasSpace(w))
  				return false;
  
  			reserves.Add(a);
  			reservedWeight += w;
  
  			return true;
  		}
  
  		internal void UnreserveSpace(Actor a)
  		{
  			if (!reserves.Contains(a))
  				return;
  
  			reservedWeight -= GetWeight(a);
  			reserves.Remove(a);
  		}
  
  		public string CursorForOrder(Actor self, Order order)
  		{
  			if (order.OrderString != "Unload")
  				return null;
  
  			return CanUnload() ? "deploy" : "deploy-blocked";
  		}
  
  		public string VoicePhraseForOrder(Actor self, Order order)
  		{
  			if (order.OrderString != "Unload" || IsEmpty(self))
  				return null;
  
  			return self.HasVoice("Unload") ? "Unload" : "Move";
  		}
  
  		public bool MoveDisabled(Actor self) { return reserves.Any(); }
  		public bool HasSpace(int weight) { return totalWeight + reservedWeight + weight <= Info.MaxWeight; }
  		public bool IsEmpty(Actor self) { return cargo.Count == 0; }
  
  		public Actor Peek(Actor self) { return cargo.Peek(); }
  
  		public Actor Unload(Actor self)
  		{
  			var a = cargo.Pop();
  
  			totalWeight -= GetWeight(a);
  
  			SetPassengerFacing(a);
  
  			foreach (var npe in self.TraitsImplementing<INotifyPassengerExited>())
  				npe.PassengerExited(self, a);
  
  			var p = a.Trait<Passenger>();
  			p.Transport = null;
  
  			foreach (var u in p.Info.GrantUpgrades)
  				self.Trait<UpgradeManager>().RevokeUpgrade(self, u, p);
  
  			return a;
  		}
  
  		void SetPassengerFacing(Actor passenger)
  		{
  			if (facing.Value == null)
  				return;
  
  			var passengerFacing = passenger.TraitOrDefault<IFacing>();
  			if (passengerFacing != null)
  				passengerFacing.Facing = facing.Value.Facing + Info.PassengerFacing;
  
  			var passengerTurreted = passenger.TraitOrDefault<Turreted>();
  			if (passengerTurreted != null)
  				passengerTurreted.TurretFacing = facing.Value.Facing + Info.PassengerFacing;
  		}
  
  		public IEnumerable<PipType> GetPips(Actor self)
  		{
  			var numPips = Info.PipCount;
  
  			for (var i = 0; i < numPips; i++)
  				yield return GetPipAt(i);
  		}
  
  		PipType GetPipAt(int i)
  		{
  			var n = i * Info.MaxWeight / Info.PipCount;
  
  			foreach (var c in cargo)
  			{
  				var pi = c.Info.Traits.Get<PassengerInfo>();
  				if (n < pi.Weight)
  					return pi.PipType;
  				else
  					n -= pi.Weight;
  			}
  
  			return PipType.Transparent;
  		}
  
  		public void Load(Actor self, Actor a)
  		{
  			cargo.Push(a);
  
gt: 			var w = GetWeight(a);