#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.Graphics; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.D2k.Activities; using OpenRA.Traits; namespace OpenRA.Mods.D2k.Traits { [Desc("Automatically transports harvesters with the Carryable trait between resource fields and refineries.")] public class CarryallInfo : ITraitInfo, Requires { [Desc("Set to false when the carryall should not automatically get new jobs.")] public readonly bool Automatic = true; public object Create(ActorInitializer init) { return new Carryall(init.Self, this); } } public class Carryall : INotifyBecomingIdle, INotifyKilled, ISync, IRender, INotifyActorDisposing { readonly Actor self; readonly WRange carryHeight; readonly CarryallInfo info; // The actor we are currently carrying. [Sync] public Actor Carrying { get; internal set; } public bool IsCarrying { get; internal set; } // TODO: Use ActorPreviews so that this can support actors with multiple sprites Animation anim; public bool IsBusy { get; internal set; } public Carryall(Actor self, CarryallInfo info) { this.self = self; this.info = info; IsBusy = false; IsCarrying = false; carryHeight = self.Trait().Info.LandAltitude; } public void OnBecomingIdle(Actor self) { if (info.Automatic) FindCarryableForTransport(); if (!IsBusy) self.QueueActivity(new HeliFlyCircle(self)); } // A carryable notifying us that he'd like to be carried public bool RequestTransportNotify(Actor carryable) { if (IsBusy || !info.Automatic) return false; if (ReserveCarryable(carryable)) { self.QueueActivity(false, new PickupUnit(self, carryable)); self.QueueActivity(true, new DeliverUnit(self)); return true; } return false; } void FindCarryableForTransport() { if (!self.IsInWorld) return; // 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; if (actor.IsIdle) 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 PickupUnit(self, p.Actor)); self.QueueActivity(true, new DeliverUnit(self)); break; } } } // Reserve the carryable so its ours exclusively public bool ReserveCarryable(Actor carryable) { if (Carrying != null) return false; if (carryable.Trait().Reserve(self)) { Carrying = carryable; IsBusy = 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; } CarryableReleased(); } // INotifyKilled public void Killed(Actor self, AttackInfo e) { if (Carrying != null) { if (IsCarrying && Carrying.IsInWorld && !Carrying.IsDead) Carrying.Kill(e.Attacker); Carrying = null; } UnreserveCarryable(); } public void Disposing(Actor self) { if (Carrying != null && IsCarrying) { Carrying.Dispose(); Carrying = null; } } // Called when carryable is inside. public void AttachCarryable(Actor carryable) { IsBusy = true; IsCarrying = true; Carrying = carryable; // Create a new animation for our carryable unit var rs = carryable.Trait(); anim = new Animation(self.World, rs.GetImage(carryable), RenderSprites.MakeFacingFunc(self)); anim.PlayRepeating("idle"); anim.IsDecoration = true; } // Called when released public void CarryableReleased() { IsBusy = false; IsCarrying = false; 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; } } } }