Files
OpenRA/OpenRA.Mods.D2k/Traits/AutoCarryall.cs
huwpascoe 804cdee8f0 BugFix: harvester-carryall remote death
The d2k harvester would suddenly explode if the approaching carryall was
destroyed. This is because there was no distinction between the unit being
carried and the unit being reserved. To test you'll probably have to tweak
the turret missile in weapons.yaml so you can forcefire on the carryall.

* added a bool to check if carryall is actually holding it's target
* fixed harvester being stuck in reserved mode
2015-02-03 22:43:35 +00:00

181 lines
4.2 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.Mods.RA;
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<IBodyOrientationInfo>
{
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;
bool isCarrying;
// 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<Helicopter>().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<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;
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<Carryable>().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<Carryable>().UnReserve(self);
carrying = null;
}
Busy = false;
}
// INotifyKilled
public void Killed(Actor self, AttackInfo e)
{
if (carrying != null)
{
if (isCarrying && carrying.IsInWorld && !carrying.IsDead)
carrying.Kill(e.Attacker);
}
UnreserveCarryable();
}
// Called when carryable is inside.
public void AttachCarryable(Actor carryable)
{
isCarrying = true;
// 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()
{
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.Range), wr.Palette("player" + carrying.Owner.InternalName));
foreach (var rr in renderables)
yield return rr;
}
}
}
}