#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; using OpenRA.Mods.D2k.Activities; using OpenRA.Mods.RA; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Traits; using OpenRA.Traits; namespace OpenRA.Mods.D2k.Traits { [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"); anim.IsDecoration = true; } // 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; } } } }