216 lines
5.0 KiB
C#
216 lines
5.0 KiB
C#
#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<BodyOrientationInfo>
|
|
{
|
|
[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 WDist 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<Helicopter>().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<Carryable>()
|
|
.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<Carryable>().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<Carryable>().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<RenderSprites>();
|
|
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<IRenderable> 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.Length),
|
|
wr.Palette("player" + Carrying.Owner.InternalName));
|
|
|
|
foreach (var rr in renderables)
|
|
yield return rr;
|
|
}
|
|
}
|
|
}
|
|
}
|