Skip to content

Commit d7bfc56

Browse files
committed
Add Garrisonable Framework
Add Tech Bunker
1 parent 7e23c0a commit d7bfc56

18 files changed

Lines changed: 1660 additions & 5 deletions

File tree

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#region Copyright & License Information
2+
/**
3+
* Copyright (c) The OpenRA Combined Arms Developers (see CREDITS).
4+
* This file is part of OpenRA Combined Arms, which is free software.
5+
* It is made available to you under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation, either version 3 of the License,
7+
* or (at your option) any later version. For more information, see COPYING.
8+
*/
9+
#endregion
10+
11+
using System.Collections.Generic;
12+
using OpenRA.Activities;
13+
using OpenRA.Mods.CA.Traits;
14+
using OpenRA.Mods.Common;
15+
using OpenRA.Mods.Common.Traits;
16+
using OpenRA.Primitives;
17+
using OpenRA.Traits;
18+
19+
namespace OpenRA.Mods.CA.Activities
20+
{
21+
class EnterGarrison : Activity
22+
{
23+
enum EnterState { Approaching, Entering, Exiting, Finished }
24+
25+
readonly IMove move;
26+
readonly Color? targetLineColor;
27+
readonly Garrisoner garrisoner;
28+
Target target;
29+
Target lastVisibleTarget;
30+
bool useLastVisibleTarget;
31+
EnterState lastState = EnterState.Approaching;
32+
33+
// EnterGarrison Properties
34+
Actor enterActor;
35+
Garrisonable enterGarrison;
36+
Aircraft enterAircraft;
37+
38+
public EnterGarrison(Actor self, Target target, Color? targetLineColor = null)
39+
{
40+
// Base - Enter Properties
41+
move = self.Trait<IMove>();
42+
this.target = target;
43+
this.targetLineColor = targetLineColor;
44+
45+
// EnterGarrison Properties
46+
garrisoner = self.Trait<Garrisoner>();
47+
}
48+
49+
protected bool TryStartEnter(Actor self, Actor targetActor)
50+
{
51+
enterActor = targetActor;
52+
enterGarrison = targetActor.TraitOrDefault<Garrisonable>();
53+
enterAircraft = targetActor.TraitOrDefault<Aircraft>();
54+
55+
// Make sure we can still enter the transport
56+
// (but not before, because this may stop the actor in the middle of nowhere)
57+
if (enterGarrison == null || enterGarrison.IsTraitDisabled || enterGarrison.IsTraitPaused || !garrisoner.Reserve(self, enterGarrison))
58+
{
59+
Cancel(self, true);
60+
return false;
61+
}
62+
63+
if (enterAircraft != null && !enterAircraft.AtLandAltitude)
64+
return false;
65+
66+
return true;
67+
}
68+
69+
protected void OnCancel() { }
70+
71+
protected void OnEnterComplete(Actor self, Actor targetActor)
72+
{
73+
self.World.AddFrameEndTask(w =>
74+
{
75+
if (self.IsDead)
76+
return;
77+
78+
// Make sure the target hasn't changed while entering
79+
// OnEnterComplete is only called if targetActor is alive
80+
if (targetActor != enterActor)
81+
return;
82+
83+
if (enterActor.AppearsHostileTo(self))
84+
return;
85+
86+
if (!enterGarrison.CanLoad(self))
87+
return;
88+
89+
enterGarrison.Load(enterActor, self);
90+
w.Remove(self);
91+
});
92+
}
93+
94+
// Base Enter Methods Below
95+
public override bool Tick(Actor self)
96+
{
97+
// Update our view of the target
98+
target = target.Recalculate(self.Owner, out var targetIsHiddenActor);
99+
100+
// Re-acquire the target after change owner has happened.
101+
if (target.Type == TargetType.Invalid)
102+
target = Target.FromActor(target.Actor);
103+
104+
if (!targetIsHiddenActor && target.Type == TargetType.Actor)
105+
lastVisibleTarget = Target.FromTargetPositions(target);
106+
107+
useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);
108+
109+
// Cancel immediately if the target died while we were entering it
110+
if (!IsCanceling && useLastVisibleTarget && lastState == EnterState.Entering)
111+
Cancel(self, true);
112+
113+
// We need to wait for movement to finish before transitioning to
114+
// the next state or next activity
115+
if (!TickChild(self))
116+
return false;
117+
118+
// Note that lastState refers to what we have just *finished* doing
119+
switch (lastState)
120+
{
121+
case EnterState.Approaching:
122+
{
123+
// NOTE: We can safely cancel in this case because we know the
124+
// actor has finished any in-progress move activities
125+
if (IsCanceling)
126+
return true;
127+
128+
// Lost track of the target
129+
if (useLastVisibleTarget && lastVisibleTarget.Type == TargetType.Invalid)
130+
return true;
131+
132+
// We are not next to the target - lets fix that
133+
if (target.Type != TargetType.Invalid && !move.CanEnterTargetNow(self, target))
134+
{
135+
// Target lines are managed by this trait, so we do not pass targetLineColor
136+
var initialTargetPosition = (useLastVisibleTarget ? lastVisibleTarget : target).CenterPosition;
137+
QueueChild(move.MoveToTarget(self, target, initialTargetPosition));
138+
return false;
139+
}
140+
141+
// We are next to where we thought the target should be, but it isn't here
142+
// There's not much more we can do here
143+
if (useLastVisibleTarget || target.Type != TargetType.Actor)
144+
return true;
145+
146+
// Are we ready to move into the target?
147+
if (TryStartEnter(self, target.Actor))
148+
{
149+
lastState = EnterState.Entering;
150+
QueueChild(move.MoveIntoTarget(self, target));
151+
return false;
152+
}
153+
154+
// Subclasses can cancel the activity during TryStartEnter
155+
// Return immediately to avoid an extra tick's delay
156+
if (IsCanceling)
157+
return true;
158+
159+
return false;
160+
}
161+
162+
case EnterState.Entering:
163+
{
164+
// Re-acquire the target after change owner has happened.
165+
if (target.Type == TargetType.Invalid)
166+
target = Target.FromActor(target.Actor);
167+
168+
// Check that we reached the requested position
169+
var targetPos = target.Positions.PositionClosestTo(self.CenterPosition);
170+
if (!IsCanceling && self.CenterPosition == targetPos && target.Type == TargetType.Actor)
171+
OnEnterComplete(self, target.Actor);
172+
else
173+
{
174+
// Need to move again as we have re-aquired a target
175+
QueueChild(move.MoveToTarget(self, target, targetPos));
176+
lastState = EnterState.Approaching;
177+
return false;
178+
}
179+
180+
lastState = EnterState.Exiting;
181+
return false;
182+
}
183+
184+
case EnterState.Exiting:
185+
{
186+
QueueChild(move.ReturnToCell(self));
187+
lastState = EnterState.Finished;
188+
return false;
189+
}
190+
}
191+
192+
return true;
193+
}
194+
195+
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
196+
{
197+
if (targetLineColor != null)
198+
yield return new TargetLineNode(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value);
199+
}
200+
}
201+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#region Copyright & License Information
2+
/*
3+
* Copyright 2015- OpenRA.Mods.AS Developers (see AUTHORS)
4+
* This file is a part of a third-party plugin for OpenRA, which is
5+
* free software. It is made available to you under the terms of the
6+
* GNU General Public License as published by the Free Software
7+
* Foundation. For more information, see COPYING.
8+
*/
9+
#endregion
10+
11+
using System.Collections.Generic;
12+
using System.Linq;
13+
using OpenRA.Activities;
14+
using OpenRA.Mods.CA.Traits;
15+
using OpenRA.Mods.Common;
16+
using OpenRA.Mods.Common.Activities;
17+
using OpenRA.Mods.Common.Traits;
18+
using OpenRA.Traits;
19+
20+
namespace OpenRA.Mods.CA.Activities
21+
{
22+
public class UnloadGarrison : Activity
23+
{
24+
readonly Actor self;
25+
readonly Garrisonable garrison;
26+
readonly INotifyUnloadCargo[] notifiers;
27+
readonly bool unloadAll;
28+
readonly Aircraft aircraft;
29+
readonly Mobile mobile;
30+
readonly bool assignTargetOnFirstRun;
31+
readonly WDist unloadRange;
32+
33+
Target destination;
34+
bool takeOffAfterUnload;
35+
36+
public UnloadGarrison(Actor self, WDist unloadRange, bool unloadAll = true)
37+
: this(self, Target.Invalid, unloadRange, unloadAll)
38+
{
39+
assignTargetOnFirstRun = true;
40+
}
41+
42+
public UnloadGarrison(Actor self, Target destination, WDist unloadRange, bool unloadAll = true)
43+
{
44+
this.self = self;
45+
garrison = self.Trait<Garrisonable>();
46+
notifiers = self.TraitsImplementing<INotifyUnloadCargo>().ToArray();
47+
this.unloadAll = unloadAll;
48+
aircraft = self.TraitOrDefault<Aircraft>();
49+
mobile = self.TraitOrDefault<Mobile>();
50+
this.destination = destination;
51+
this.unloadRange = unloadRange;
52+
}
53+
54+
public (CPos Cell, SubCell SubCell)? ChooseExitSubCell(Actor passenger)
55+
{
56+
var pos = passenger.Trait<IPositionable>();
57+
58+
return garrison.CurrentAdjacentCells()
59+
.Shuffle(self.World.SharedRandom)
60+
.Select(c => (c, pos.GetAvailableSubCell(c)))
61+
.Cast<(CPos, SubCell SubCell)?>()
62+
.FirstOrDefault(s => s.Value.SubCell != SubCell.Invalid);
63+
}
64+
65+
IEnumerable<CPos> BlockedExitCells(Actor passenger)
66+
{
67+
var pos = passenger.Trait<IPositionable>();
68+
69+
// Find the cells that are blocked by transient actors
70+
return garrison.CurrentAdjacentCells()
71+
.Where(c => pos.CanEnterCell(c, null, BlockedByActor.All) != pos.CanEnterCell(c, null, BlockedByActor.None));
72+
}
73+
74+
protected override void OnFirstRun(Actor self)
75+
{
76+
if (assignTargetOnFirstRun)
77+
destination = Target.FromCell(self.World, self.Location);
78+
79+
// Move to the target destination
80+
if (aircraft != null)
81+
{
82+
// Queue the activity even if already landed in case self.Location != destination
83+
QueueChild(new Land(self, destination, unloadRange));
84+
takeOffAfterUnload = !aircraft.AtLandAltitude;
85+
}
86+
else if (mobile != null)
87+
{
88+
var cell = self.World.Map.Clamp(this.self.World.Map.CellContaining(destination.CenterPosition));
89+
QueueChild(new Move(self, cell, unloadRange));
90+
}
91+
92+
QueueChild(new Wait(garrison.Info.BeforeUnloadDelay));
93+
}
94+
95+
public override bool Tick(Actor self)
96+
{
97+
if (IsCanceling || garrison.IsEmpty())
98+
return true;
99+
100+
if (garrison.CanUnload())
101+
{
102+
foreach (var inu in notifiers)
103+
inu.Unloading(self);
104+
105+
var actor = garrison.Peek();
106+
var spawn = self.CenterPosition;
107+
108+
var exitSubCell = ChooseExitSubCell(actor);
109+
if (exitSubCell == null)
110+
{
111+
self.NotifyBlocker(BlockedExitCells(actor));
112+
113+
Queue(new Wait(10));
114+
return false;
115+
}
116+
117+
garrison.Unload(self);
118+
self.World.AddFrameEndTask(w =>
119+
{
120+
if (actor.Disposed)
121+
return;
122+
123+
var move = actor.Trait<IMove>();
124+
var pos = actor.Trait<IPositionable>();
125+
126+
pos.SetPosition(actor, exitSubCell.Value.Cell, exitSubCell.Value.SubCell);
127+
pos.SetCenterPosition(actor, spawn);
128+
129+
actor.CancelActivity();
130+
w.Add(actor);
131+
});
132+
}
133+
134+
if (!unloadAll || !garrison.CanUnload())
135+
{
136+
if (garrison.Info.AfterUnloadDelay > 0)
137+
QueueChild(new Wait(garrison.Info.AfterUnloadDelay, false));
138+
139+
if (takeOffAfterUnload)
140+
QueueChild(new TakeOff(self));
141+
142+
return true;
143+
}
144+
145+
return false;
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)