Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backporting Garrison logic from romanovs-vengeance #681

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions OpenRA.Mods.RA2/Activities/EnterGarrison.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.RA2.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;

namespace OpenRA.Mods.RA2.Activities
{
class EnterGarrison : Activity
{
enum EnterState { Approaching, Entering, Exiting, Finished }

readonly IMove move;
readonly Color? targetLineColor;
readonly Garrisoner garrisoner;
Target target;
Target lastVisibleTarget;
bool useLastVisibleTarget;
EnterState lastState = EnterState.Approaching;

// EnterGarrison Properties
Actor enterActor;
Garrisonable enterGarrison;
Aircraft enterAircraft;

public EnterGarrison(Actor self, Target target, Color? targetLineColor = null)
{
// Base - Enter Properties
move = self.Trait<IMove>();
this.target = target;
this.targetLineColor = targetLineColor;

// EnterGarrison Properties
garrisoner = self.TraitsImplementing<Garrisoner>().Single();
}

protected bool TryStartEnter(Actor self, Actor targetActor)
{
enterActor = targetActor;
enterGarrison = targetActor.TraitOrDefault<Garrisonable>();
enterAircraft = targetActor.TraitOrDefault<Aircraft>();

// Make sure we can still enter the transport
// (but not before, because this may stop the actor in the middle of nowhere)
if (enterGarrison == null || enterGarrison.IsTraitDisabled || enterGarrison.IsTraitPaused || !garrisoner.Reserve(self, enterGarrison))
{
Cancel(self, true);
return false;
}

if (enterAircraft != null && !enterAircraft.AtLandAltitude)
return false;

return true;
}

protected void OnCancel(Actor self) { }

protected void OnEnterComplete(Actor self, Actor targetActor)
{
self.World.AddFrameEndTask(w =>
{
if (self.IsDead)
return;

// Make sure the target hasn't changed while entering
// OnEnterComplete is only called if targetActor is alive
if (targetActor != enterActor)
return;

if (!enterGarrison.CanLoad(enterActor, self))
return;

enterGarrison.Load(enterActor, self);
w.Remove(self);
});
}

// Base Enter Methods Below
public override bool Tick(Actor self)
{
// Update our view of the target
bool targetIsHiddenActor;
target = target.Recalculate(self.Owner, out targetIsHiddenActor);

// Re-acquire the target after change owner has happened.
if (target.Type == TargetType.Invalid)
target = Target.FromActor(target.Actor);

if (!targetIsHiddenActor && target.Type == TargetType.Actor)
lastVisibleTarget = Target.FromTargetPositions(target);

var oldUseLastVisibleTarget = useLastVisibleTarget;
useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

// Cancel immediately if the target died while we were entering it
if (!IsCanceling && useLastVisibleTarget && lastState == EnterState.Entering)
Cancel(self, true);

// We need to wait for movement to finish before transitioning to
// the next state or next activity
if (!TickChild(self))
return false;

// Note that lastState refers to what we have just *finished* doing
switch (lastState)
{
case EnterState.Approaching:
{
// NOTE: We can safely cancel in this case because we know the
// actor has finished any in-progress move activities
if (IsCanceling)
return true;

// Lost track of the target
if (useLastVisibleTarget && lastVisibleTarget.Type == TargetType.Invalid)
return true;

// We are not next to the target - lets fix that
if (target.Type != TargetType.Invalid && !move.CanEnterTargetNow(self, target))
{
// Target lines are managed by this trait, so we do not pass targetLineColor
var initialTargetPosition = (useLastVisibleTarget ? lastVisibleTarget : target).CenterPosition;
QueueChild(move.MoveToTarget(self, target, initialTargetPosition));
return false;
}

// We are next to where we thought the target should be, but it isn't here
// There's not much more we can do here
if (useLastVisibleTarget || target.Type != TargetType.Actor)
return true;

// Are we ready to move into the target?
if (TryStartEnter(self, target.Actor))
{
lastState = EnterState.Entering;
QueueChild(move.MoveIntoTarget(self, target));
return false;
}

// Subclasses can cancel the activity during TryStartEnter
// Return immediately to avoid an extra tick's delay
if (IsCanceling)
return true;

return false;
}

case EnterState.Entering:
{
// Check that we reached the requested position
var targetPos = target.Positions.PositionClosestTo(self.CenterPosition);
if (!IsCanceling && self.CenterPosition == targetPos && target.Type == TargetType.Actor)
OnEnterComplete(self, target.Actor);

lastState = EnterState.Exiting;
return false;
}

case EnterState.Exiting:
{
QueueChild(move.ReturnToCell(self));
lastState = EnterState.Finished;
return false;
}
}

return true;
}

public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
{
if (targetLineColor != null)
yield return new TargetLineNode(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value);
}
}
}
150 changes: 150 additions & 0 deletions OpenRA.Mods.RA2/Activities/UnloadGarrison.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#region Copyright & License Information
/*
* Copyright 2007-2018 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion

using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.RA2.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;

namespace OpenRA.Mods.RA2.Activities
{
public class UnloadGarrison : Activity
{
readonly Actor self;
readonly Garrisonable garrison;
readonly INotifyUnload[] notifiers;
readonly bool unloadAll;
readonly Aircraft aircraft;
readonly Mobile mobile;
readonly bool assignTargetOnFirstRun;
readonly WDist unloadRange;

Target destination;
bool takeOffAfterUnload;

public UnloadGarrison(Actor self, WDist unloadRange, bool unloadAll = true)
: this(self, Target.Invalid, unloadRange, unloadAll)
{
assignTargetOnFirstRun = true;
}

public UnloadGarrison(Actor self, Target destination, WDist unloadRange, bool unloadAll = true)
{
this.self = self;
garrison = self.Trait<Garrisonable>();
notifiers = self.TraitsImplementing<INotifyUnload>().ToArray();
this.unloadAll = unloadAll;
aircraft = self.TraitOrDefault<Aircraft>();
mobile = self.TraitOrDefault<Mobile>();
this.destination = destination;
this.unloadRange = unloadRange;
}

public Pair<CPos, SubCell>? ChooseExitSubCell(Actor passenger)
{
var pos = passenger.Trait<IPositionable>();

return garrison.CurrentAdjacentCells
.Shuffle(self.World.SharedRandom)
.Select(c => Pair.New(c, pos.GetAvailableSubCell(c)))
.Cast<Pair<CPos, SubCell>?>()
.FirstOrDefault(s => s.Value.Second != SubCell.Invalid);
}

IEnumerable<CPos> BlockedExitCells(Actor passenger)
{
var pos = passenger.Trait<IPositionable>();

// Find the cells that are blocked by transient actors
return garrison.CurrentAdjacentCells
.Where(c => pos.CanEnterCell(c, null, true) != pos.CanEnterCell(c, null, false));
}

protected override void OnFirstRun(Actor self)
{
if (assignTargetOnFirstRun)
destination = Target.FromCell(self.World, self.Location);

// Move to the target destination
if (aircraft != null)
{
// Queue the activity even if already landed in case self.Location != destination
QueueChild(new Land(self, destination, unloadRange));
takeOffAfterUnload = !aircraft.AtLandAltitude;
}
else if (mobile != null)
{
var cell = self.World.Map.Clamp(this.self.World.Map.CellContaining(destination.CenterPosition));
QueueChild(new Move(self, cell, unloadRange));
}

QueueChild(new Wait(garrison.Info.BeforeUnloadDelay));
}

public override bool Tick(Actor self)
{
if (IsCanceling || garrison.IsEmpty(self))
return true;

if (garrison.CanUnload())
{
foreach (var inu in notifiers)
inu.Unloading(self);

var actor = garrison.Peek(self);
var spawn = self.CenterPosition;

var exitSubCell = ChooseExitSubCell(actor);
if (exitSubCell == null)
{
self.NotifyBlocker(BlockedExitCells(actor));

Queue(new Wait(10));
return false;
}

garrison.Unload(self);
self.World.AddFrameEndTask(w =>
{
if (actor.Disposed)
return;

var move = actor.Trait<IMove>();
var pos = actor.Trait<IPositionable>();

pos.SetPosition(actor, exitSubCell.Value.First, exitSubCell.Value.Second);
pos.SetVisualPosition(actor, spawn);

actor.CancelActivity();
w.Add(actor);
});
}

if (!unloadAll || !garrison.CanUnload())
{
if (garrison.Info.AfterUnloadDelay > 0)
QueueChild(new Wait(garrison.Info.AfterUnloadDelay, false));

if (takeOffAfterUnload)
QueueChild(new TakeOff(self));

return true;
}

return false;
}
}
}
4 changes: 4 additions & 0 deletions OpenRA.Mods.RA2/OpenRA.Mods.RA2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
</PropertyGroup>

<ItemGroup>
<Reference Include="Eluant">
<HintPath>../engine/thirdparty/download/Eluant.dll</HintPath>
Mailaender marked this conversation as resolved.
Show resolved Hide resolved
<Private>False</Private>
</Reference>
<ProjectReference Include="..\engine\OpenRA.Game\OpenRA.Game.csproj" />
<ProjectReference Include="..\engine\OpenRA.Mods.Cnc\OpenRA.Mods.Cnc.csproj" />
<ProjectReference Include="..\engine\OpenRA.Mods.Common\OpenRA.Mods.Common.csproj" />
Expand Down
Loading