Merge pull request #3063 from pchote/repairable-bridges

Repairable bridges, closes #2619
This commit is contained in:
Matthias Mailänder
2013-04-15 05:49:56 -07:00
41 changed files with 704 additions and 200 deletions

View File

@@ -54,9 +54,12 @@ namespace OpenRA.Orders
.OrderByDescending(a => a.SelectionPriority())
.FirstOrDefault();
if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())
if (underCursor != null && underCursor.HasTrait<Selectable>())
if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any()))
{
var selectable = underCursor.TraitOrDefault<Selectable>();
if (selectable != null && selectable.Info.Selectable)
useSelect = true;
}
var orders = world.Selection.Actors
.Select(a => OrderForUnit(a, xy, mi, underCursor))

View File

@@ -69,6 +69,35 @@ namespace OpenRA.Traits
}
}
public void Resurrect(Actor self, Actor repairer)
{
if (!IsDead)
return;
hp = MaxHP;
var ai = new AttackInfo
{
Attacker = repairer,
Damage = -MaxHP,
DamageState = this.DamageState,
PreviousDamageState = DamageState.Dead,
Warhead = null,
};
foreach (var nd in self.TraitsImplementing<INotifyDamage>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyDamage>()))
nd.Damaged(self, ai);
foreach (var nd in self.TraitsImplementing<INotifyDamageStateChanged>())
nd.DamageStateChanged(self, ai);
if (repairer != null && repairer.IsInWorld && !repairer.IsDead())
foreach (var nd in repairer.TraitsImplementing<INotifyAppliedDamage>()
.Concat(repairer.Owner.PlayerActor.TraitsImplementing<INotifyAppliedDamage>()))
nd.AppliedDamage(repairer, self, ai);
}
public void InflictDamage(Actor self, Actor attacker, int damage, WarheadInfo warhead, bool ignoreModifiers)
{
if (IsDead) return; /* overkill! don't count extra hits as more kills! */

View File

@@ -16,21 +16,30 @@ namespace OpenRA.Traits
{
public class SelectableInfo : ITraitInfo
{
public readonly bool Selectable = true;
public readonly int Priority = 10;
public readonly int[] Bounds = null;
[VoiceReference] public readonly string Voice = null;
public object Create(ActorInitializer init) { return new Selectable(init.self); }
public object Create(ActorInitializer init) { return new Selectable(init.self, this); }
}
public class Selectable : IPostRenderSelection
{
public SelectableInfo Info;
Actor self;
public Selectable(Actor self) { this.self = self; }
public Selectable(Actor self, SelectableInfo info)
{
this.self = self;
Info = info;
}
public void RenderAfterWorld(WorldRenderer wr)
{
if (!Info.Selectable)
return;
var bounds = self.Bounds.Value;
var xy = new float2(bounds.Left, bounds.Top);
@@ -44,6 +53,9 @@ namespace OpenRA.Traits
public void DrawRollover(WorldRenderer wr, Actor self)
{
if (!Info.Selectable)
return;
var bounds = self.Bounds.Value;
var xy = new float2(bounds.Left, bounds.Top);

View File

@@ -184,7 +184,7 @@ namespace OpenRA.Widgets
IEnumerable<Actor> SelectActorsInBox(World world, PPos a, PPos b, Func<Actor, bool> cond)
{
return world.FindUnits(a, b)
.Where(x => x.HasTrait<Selectable>() && !world.FogObscures(x) && cond(x))
.Where(x => x.HasTrait<Selectable>() && x.Trait<Selectable>().Info.Selectable && !world.FogObscures(x) && cond(x))
.GroupBy(x => x.GetSelectionPriority())
.OrderByDescending(g => g.Key)
.Select(g => g.AsEnumerable())

View File

@@ -16,37 +16,35 @@ namespace OpenRA.Mods.RA.Activities
{
class Demolish : Activity
{
Actor target;
Target target;
int delay;
public Demolish( Actor target, int delay )
public Demolish(Actor target, int delay)
{
this.target = target;
this.target = Target.FromActor(target);
this.delay = delay;
}
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid)
return NextActivity;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
{
// Can't demolish an already dead actor
if (target.IsDead())
if (!target.IsValid)
return;
// Invulnerable actors can't be demolished
var modifier = (float)target.TraitsImplementing<IDamageModifier>()
var modifier = (float)target.Actor.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null)).Product();
if (target.IsInWorld && modifier > 0)
target.Kill(self);
if (modifier > 0)
target.Actor.Kill(self);
})));
return NextActivity;
}
}

View File

@@ -16,26 +16,26 @@ namespace OpenRA.Mods.RA.Activities
{
class DonateSupplies : Activity
{
Actor target;
Target target;
int payload;
public DonateSupplies(Actor target, int payload)
{
this.target = target;
this.target = Target.FromActor(target);
this.payload = payload;
}
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if (!target.OccupiesSpace.OccupiedCells().Any(x => x.First == self.Location))
if (IsCanceled || !target.IsValid)
return NextActivity;
target.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
var targetPlayer = target.Actor.Owner;
targetPlayer.PlayerActor.Trait<PlayerResources>().GiveCash(payload);
self.Destroy();
if (self.World.LocalPlayer == null || self.Owner.Stances[self.World.LocalPlayer] == Stance.Ally)
self.World.AddFrameEndTask(w => w.Add(new CashTick(payload, 30, 2, target.CenterLocation, target.Owner.ColorRamp.GetColor(0))));
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
self.World.AddFrameEndTask(w => w.Add(new CashTick(payload, 30, 2, target.CenterLocation, targetPlayer.ColorRamp.GetColor(0))));
return this;
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.Linq;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -15,20 +16,38 @@ namespace OpenRA.Mods.RA.Activities
{
public class Enter : Activity
{
readonly Actor target;
public Enter( Actor target ) { this.target = target; }
readonly Target target;
readonly Activity inner;
public override Activity Tick( Actor self )
public Enter(Actor target, Activity inner)
{
if( IsCanceled || target.Destroyed || !target.IsInWorld )
this.target = Target.FromActor(target);
this.inner = inner;
}
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
return NextActivity;
var mobile = self.Trait<Mobile>();
var nearest = target.OccupiesSpace.NearestCellTo( mobile.toCell );
if( ( nearest - mobile.toCell ).LengthSquared > 2 )
return Util.SequenceActivities( new MoveAdjacentTo( Target.FromActor(target) ), this );
if (!Util.AdjacentCells(target).Any(c => c == self.Location))
return Util.SequenceActivities(new MoveAdjacentTo(target), this);
return Util.SequenceActivities( mobile.MoveTo( nearest, target ), NextActivity );
// Move to the middle of the target, ignoring impassable tiles
var mobile = self.Trait<Mobile>();
var to = target.CenterLocation;
var from = self.CenterLocation;
var speed = mobile.MovementSpeedForCell(self, self.Location);
var length = speed > 0 ? (int)((to - from).Length * 3 / speed) : 0;
return Util.SequenceActivities(
new Turn(Util.GetFacing(to - from, mobile.Facing)),
new Drag(from, to, length),
inner,
new Turn(Util.GetFacing(from - to, mobile.Facing)),
new Drag(to, from, length),
NextActivity
);
}
}
}

View File

@@ -16,31 +16,23 @@ namespace OpenRA.Mods.RA.Activities
{
class Infiltrate : Activity
{
Actor target;
public Infiltrate(Actor target) { this.target = target; }
Target target;
public Infiltrate(Actor target) { this.target = Target.FromActor(target); }
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if (target.Owner == self.Owner) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner)
return NextActivity;
foreach (var t in target.TraitsImplementing<IAcceptInfiltrator>())
t.OnInfiltrate(target, self);
foreach (var t in target.Actor.TraitsImplementing<IAcceptInfiltrator>())
t.OnInfiltrate(target.Actor, self);
if (self.HasTrait<DontDestroyWhenInfiltrating>())
self.World.AddFrameEndTask(w =>
{
if (self.Destroyed) return;
w.Remove(self);
});
self.World.AddFrameEndTask(w => { if (!self.Destroyed) w.Remove(self); });
else
self.Destroy();
if (target.HasTrait<Building>())
if (target.Actor.HasTrait<Building>())
Sound.PlayToPlayer(self.Owner, "bldginf1.aud");
return this;

View File

@@ -17,33 +17,34 @@ namespace OpenRA.Mods.RA.Activities
{
readonly Target target;
public MoveAdjacentTo( Target target ) { this.target = target; }
public MoveAdjacentTo(Target target) { this.target = target; }
public override Activity Tick( Actor self )
public override Activity Tick(Actor self)
{
if( IsCanceled || !target.IsValid) return NextActivity;
if (IsCanceled || !target.IsValid)
return NextActivity;
var mobile = self.Trait<Mobile>();
var ps1 = new PathSearch( self.World, mobile.Info, self )
var ps1 = new PathSearch(self.World, mobile.Info, self)
{
checkForBlocked = true,
heuristic = location => 0,
inReverse = true
};
foreach( var cell in Util.AdjacentCells(target) )
foreach (var cell in Util.AdjacentCells(target))
{
if (cell == self.Location)
return NextActivity;
else
ps1.AddInitialCell( cell );
ps1.AddInitialCell(cell);
}
ps1.heuristic = PathSearch.DefaultEstimator( mobile.toCell );
ps1.heuristic = PathSearch.DefaultEstimator(mobile.toCell);
var ps2 = PathSearch.FromPoint(self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true);
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath(ps1, ps2);
var ps2 = PathSearch.FromPoint( self.World, mobile.Info, self, mobile.toCell, target.CenterLocation.ToCPos(), true );
var ret = self.World.WorldActor.Trait<PathFinder>().FindBidiPath( ps1, ps2 );
return Util.SequenceActivities( mobile.MoveTo( () => ret ), this );
return Util.SequenceActivities(mobile.MoveTo(() => ret), this);
}
}
}

View File

@@ -0,0 +1,37 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Activities
{
class RepairBridge : Activity
{
Target target;
public RepairBridge(Actor target) { this.target = Target.FromActor(target); }
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
return NextActivity;
var hut = target.Actor.Trait<BridgeHut>();
if (hut.BridgeDamageState == DamageState.Undamaged)
return NextActivity;
hut.Repair(self);
self.Destroy();
return this;
}
}
}

View File

@@ -15,22 +15,20 @@ namespace OpenRA.Mods.RA.Activities
{
class RepairBuilding : Activity
{
Actor target;
Target target;
public RepairBuilding(Actor target) { this.target = target; }
public RepairBuilding(Actor target) { this.target = Target.FromActor(target); }
public override Activity Tick(Actor self)
{
if (IsCanceled) return NextActivity;
if (target == null || !target.IsInWorld || target.IsDead()) return NextActivity;
if( !target.OccupiesSpace.OccupiedCells().Any( x => x.First == self.Location ) )
if (IsCanceled || !target.IsValid)
return NextActivity;
var health = target.Trait<Health>();
var health = target.Actor.Trait<Health>();
if (health.DamageState == DamageState.Undamaged)
return NextActivity;
target.InflictDamage(self, -health.MaxHP, null);
target.Actor.InflictDamage(self, -health.MaxHP, null);
self.Destroy();
return this;

View File

@@ -8,11 +8,14 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -21,6 +24,8 @@ namespace OpenRA.Mods.RA
{
public readonly bool Long = false;
[Desc("Delay (in ticks) between repairing adjacent spans in long bridges")]
public readonly int RepairPropagationDelay = 20;
public readonly ushort Template = 0;
public readonly ushort DamagedTemplate = 0;
@@ -149,11 +154,6 @@ namespace OpenRA.Mods.RA
yield return new Renderable(t.Value, t.Key.ToPPos().ToFloat2(), terrainPalette, Game.CellSize * t.Key.Y);
}
bool IsIntact(Bridge b)
{
return b != null && !b.self.IsDead();
}
void KillUnitsOnBridge()
{
foreach (var c in TileSprites[currentTemplate].Keys)
@@ -162,35 +162,55 @@ namespace OpenRA.Mods.RA
a.Kill(self);
}
bool dead = false;
void UpdateState()
bool NeighbourIsDeadShore(Bridge neighbour)
{
// If this is a long bridge next to a destroyed shore piece, we need die to give clean edges to the break
if (Info.Long && Health.DamageState != DamageState.Dead &&
((southNeighbour != null && Info.ShorePieces.Contains(southNeighbour.Type) && !IsIntact(southNeighbour)) ||
(northNeighbour != null && Info.ShorePieces.Contains(northNeighbour.Type) && !IsIntact(northNeighbour))))
{
self.Kill(self); // this changes the damagestate
}
var oldTemplate = currentTemplate;
var ds = Health.DamageState;
currentTemplate = (ds == DamageState.Dead && Info.DestroyedTemplate > 0) ? Info.DestroyedTemplate :
(ds >= DamageState.Heavy && Info.DamagedTemplate > 0) ? Info.DamagedTemplate : Info.Template;
return neighbour != null && Info.ShorePieces.Contains(neighbour.Type) && neighbour.Health.IsDead;
}
if (Info.Long && ds == DamageState.Dead)
bool LongBridgeSegmentIsDead()
{
// The long bridge artwork requires a hack to display correctly
// if the adjacent shore piece is dead
if (!Info.Long)
return Health.IsDead;
if (NeighbourIsDeadShore(northNeighbour))
return true;
if (NeighbourIsDeadShore(southNeighbour))
return true;
return Health.IsDead;
}
ushort ChooseTemplate()
{
if (Info.Long && LongBridgeSegmentIsDead())
{
// Long bridges have custom art for multiple segments being destroyed
bool waterToSouth = !IsIntact(southNeighbour);
bool waterToNorth = !IsIntact(northNeighbour);
var northIsDead = northNeighbour != null && northNeighbour.LongBridgeSegmentIsDead();
var southIsDead = southNeighbour != null && southNeighbour.LongBridgeSegmentIsDead();
if (northIsDead && southIsDead)
return Info.DestroyedPlusBothTemplate;
if (northIsDead)
return Info.DestroyedPlusNorthTemplate;
if (southIsDead)
return Info.DestroyedPlusSouthTemplate;
if (waterToSouth && waterToNorth)
currentTemplate = Info.DestroyedPlusBothTemplate;
else if (waterToNorth)
currentTemplate = Info.DestroyedPlusNorthTemplate;
else if (waterToSouth)
currentTemplate = Info.DestroyedPlusSouthTemplate;
return Info.DestroyedTemplate;
}
var ds = Health.DamageState;
return (ds == DamageState.Dead && Info.DestroyedTemplate > 0) ? Info.DestroyedTemplate :
(ds >= DamageState.Heavy && Info.DamagedTemplate > 0) ? Info.DamagedTemplate : Info.Template;
}
bool killedUnits = false;
void UpdateState()
{
var oldTemplate = currentTemplate;
currentTemplate = ChooseTemplate();
if (currentTemplate == oldTemplate)
return;
@@ -198,18 +218,81 @@ namespace OpenRA.Mods.RA
foreach (var c in TileSprites[currentTemplate].Keys)
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
if (ds == DamageState.Dead && !dead)
if (LongBridgeSegmentIsDead() && !killedUnits)
{
dead = true;
killedUnits = true;
KillUnitsOnBridge();
}
}
public void Repair(Actor repairer, bool continueNorth, bool continueSouth)
{
// Repair self
var initialDamage = Health.DamageState;
self.World.AddFrameEndTask(w =>
{
if (Health.IsDead)
{
Health.Resurrect(self, repairer);
killedUnits = false;
KillUnitsOnBridge();
}
else
Health.InflictDamage(self, repairer, -Health.MaxHP, null, true);
});
// Repair adjacent spans (long bridges)
if (continueNorth && northNeighbour != null)
{
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(northNeighbour) ?
0 : Info.RepairPropagationDelay;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
northNeighbour.Repair(repairer, true, false))));
}
if (continueSouth && southNeighbour != null)
{
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(southNeighbour) ?
0 : Info.RepairPropagationDelay;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
southNeighbour.Repair(repairer, false, true))));
}
}
public void DamageStateChanged(Actor self, AttackInfo e)
{
UpdateState();
if (northNeighbour != null) northNeighbour.UpdateState();
if (southNeighbour != null) southNeighbour.UpdateState();
if (northNeighbour != null)
northNeighbour.UpdateState();
if (southNeighbour != null)
southNeighbour.UpdateState();
// Need to update the neighbours neighbour to correctly
// display the broken shore hack
if (Info.ShorePieces.Contains(Type))
{
if (northNeighbour != null && northNeighbour.northNeighbour != null)
northNeighbour.northNeighbour.UpdateState();
if (southNeighbour != null && southNeighbour.southNeighbour != null)
southNeighbour.southNeighbour.UpdateState();
}
}
public DamageState AggregateDamageState()
{
// Find the worst span damage in the entire bridge
var br = this;
while (br.northNeighbour != null)
br = br.northNeighbour;
var damage = Health.DamageState;
for (var b = br; b != null; b = b.southNeighbour)
if (b.Health.DamageState > damage)
damage = b.Health.DamageState;
return damage;
}
}
}

View File

@@ -0,0 +1,41 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class BridgeHutInfo : ITraitInfo
{
public object Create(ActorInitializer init) { return new BridgeHut(init); }
}
class BridgeHut
{
public Bridge bridge;
public BridgeHut(ActorInitializer init)
{
bridge = init.Get<ParentActorInit>().value.Trait<Bridge>();
}
public void Repair(Actor repairer)
{
bridge.Repair(repairer, true, true);
}
public DamageState BridgeDamageState { get { return bridge.AggregateDamageState(); } }
}
}

View File

@@ -85,16 +85,13 @@ namespace OpenRA.Mods.RA
// For each subtile in the template
for (byte ind = 0; ind < template.Size.X*template.Size.Y; ind++)
{
// Is this tile actually included in the bridge template?
if (!template.Tiles.Keys.Contains(ind))
continue;
// Where do we expect to find the subtile
var x = ni + ind % template.Size.X;
var y = nj + ind / template.Size.X;
// This isn't the bridge you're looking for
if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].index != ind)
if (!w.Map.IsInMap(x, y) || w.Map.MapTiles.Value[x, y].type != tile ||
w.Map.MapTiles.Value[x, y].index != ind)
continue;
subTiles.Add(new CPos(x, y), ind);

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.RA
public IEnumerable<IOrderTargeter> Orders
{
get { yield return new UnitTraitOrderTargeter<C4Demolishable>("C4", 6, "c4", true, false); }
get { yield return new TargetTypeOrderTargeter("C4", "C4", 6, "c4", true, false); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
@@ -52,11 +52,8 @@ namespace OpenRA.Mods.RA
{
self.SetTargetLine(Target.FromOrder(order), Color.Red);
var mobile = self.Trait<Mobile>();
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new Demolish(order.TargetActor, Info.C4Delay));
self.QueueActivity(mobile.MoveTo(self.Location, 0));
self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay)));
}
}
@@ -65,7 +62,4 @@ namespace OpenRA.Mods.RA
return (order.OrderString == "C4") ? "Attack" : null;
}
}
class C4DemolishableInfo : TraitInfo<C4Demolishable> { }
class C4Demolishable { }
}

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
{
if( order.OrderID == "CaptureActor" )
if (order.OrderID == "CaptureActor")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
@@ -64,7 +64,8 @@ namespace OpenRA.Mods.RA
{
if (order.OrderString == "CaptureActor")
{
if (!CanCapture(order.TargetActor)) return;
if (!CanCapture(order.TargetActor))
return;
self.SetTargetLine(Target.FromOrder(order), Color.Red);
@@ -76,17 +77,17 @@ namespace OpenRA.Mods.RA
bool CanCapture(Actor target)
{
var c = target.TraitOrDefault<Capturable>();
return c != null && ( !c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally );
return c != null && (!c.CaptureInProgress || c.Captor.Owner.Stances[self.Owner] != Stance.Ally);
}
}
class CaptureOrderTargeter : UnitTraitOrderTargeter<Capturable>
class CaptureOrderTargeter : UnitOrderTargeter
{
readonly string[] captureTypes;
readonly Func<Actor, bool> useEnterCursor;
public CaptureOrderTargeter(string[] captureTypes, Func<Actor, bool> useEnterCursor)
: base( "CaptureActor", 6, "enter", true, true)
: base("CaptureActor", 6, "enter", true, true)
{
this.captureTypes = captureTypes;
this.useEnterCursor = useEnterCursor;
@@ -94,19 +95,26 @@ namespace OpenRA.Mods.RA
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if( !base.CanTargetActor( self, target, forceAttack, forceQueued, ref cursor ) ) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
var ci = target.Info.Traits.Get<CapturableInfo>();
var playerRelationship = self.Owner.Stances[ target.Owner ];
var ci = target.Info.Traits.GetOrDefault<CapturableInfo>();
if (ci == null)
return false;
if( playerRelationship == Stance.Ally && !ci.AllowAllies ) return false;
if( playerRelationship == Stance.Enemy && !ci.AllowEnemies ) return false;
if( playerRelationship == Stance.Neutral && !ci.AllowNeutral ) return false;
var playerRelationship = self.Owner.Stances[target.Owner];
if (playerRelationship == Stance.Ally && !ci.AllowAllies)
return false;
if (playerRelationship == Stance.Enemy && !ci.AllowEnemies)
return false;
if (playerRelationship == Stance.Neutral && !ci.AllowNeutral)
return false;
IsQueued = forceQueued;
var Info = self.Info.Traits.Get<CapturesInfo>();
if (captureTypes.Contains(ci.Type))
{
cursor = (Info.WastedAfterwards) ? (useEnterCursor(target) ? "enter" : "enter-blocked") : "attack";

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new UnitTraitOrderTargeter<Building>("DemoAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new TargetTypeOrderTargeter("DemoTruck", "DemoAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new DeployOrderTargeter("DemoDeploy", 5);
}
}

View File

@@ -26,9 +26,9 @@ namespace OpenRA.Mods.RA
get { yield return new EngineerRepairOrderTargeter(); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if( order.OrderID == "EngineerRepair" )
if (order.OrderID == "EngineerRepair")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
@@ -36,41 +36,43 @@ namespace OpenRA.Mods.RA
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "EngineerRepair"
&& order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
return (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "EngineerRepair"
&& order.TargetActor.GetDamageState() > DamageState.Undamaged)
if (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged)
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new RepairBuilding(order.TargetActor));
self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor)));
}
}
class EngineerRepairOrderTargeter : UnitTraitOrderTargeter<Building>
class EngineerRepairOrderTargeter : UnitOrderTargeter
{
public EngineerRepairOrderTargeter()
: base( "EngineerRepair", 6, "goldwrench", false, true ) { }
: base("EngineerRepair", 6, "goldwrench", false, true) { }
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if( !base.CanTargetActor( self, target, forceAttack, forceQueued, ref cursor ) ) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (!target.HasTrait<EngineerRepairable>())
return false;
if (self.Owner.Stances[ target.Owner ] != Stance.Ally)
if (self.Owner.Stances[target.Owner] != Stance.Ally)
return false;
IsQueued = forceQueued;
if( target.GetDamageState() == DamageState.Undamaged )
if (target.GetDamageState() == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
}

View File

@@ -34,21 +34,22 @@ namespace OpenRA.Mods.RA
{
public FreeActor(ActorInitializer init, FreeActorInfo info)
{
if (init.Contains<FreeActorInit>() && !init.Get<FreeActorInit>().value) return;
if (init.Contains<FreeActorInit>() && !init.Get<FreeActorInit>().value)
return;
init.self.World.AddFrameEndTask(
w =>
init.self.World.AddFrameEndTask(w =>
{
var a = w.CreateActor(info.Actor, new TypeDictionary
{
var a = w.CreateActor(info.Actor, new TypeDictionary
{
new LocationInit( init.self.Location + (CVec)info.SpawnOffset ),
new OwnerInit( init.self.Owner ),
new FacingInit( info.Facing ),
});
if (info.InitialActivity != null)
a.QueueActivity(Game.CreateObject<Activity>(info.InitialActivity));
new ParentActorInit(init.self),
new LocationInit(init.self.Location + (CVec)info.SpawnOffset),
new OwnerInit(init.self.Owner),
new FacingInit(info.Facing),
});
if (info.InitialActivity != null)
a.QueueActivity(Game.CreateObject<Activity>(info.InitialActivity));
});
}
}
@@ -60,4 +61,11 @@ namespace OpenRA.Mods.RA
public FreeActorInit(bool init) { value = init; }
public bool Value(World world) { return value; }
}
public class ParentActorInit : IActorInit<Actor>
{
public readonly Actor value;
public ParentActorInit(Actor parent) { value = parent; }
public Actor Value(World world) { return value; }
}
}

View File

@@ -67,8 +67,7 @@ namespace OpenRA.Mods.RA
self.SetTargetLine(Target.FromOrder(order), Color.Red);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new Infiltrate(order.TargetActor));
self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor)));
}
}
@@ -89,13 +88,14 @@ namespace OpenRA.Mods.RA
return false;
}
class InfiltratorOrderTargeter : UnitTraitOrderTargeter<IAcceptInfiltrator>
class InfiltratorOrderTargeter : UnitOrderTargeter
{
readonly Func<Actor, bool> useEnterCursor;
public InfiltratorOrderTargeter(Func<Actor, bool> useEnterCursor) : base("Infiltrate", 7, "enter", true, false)
public InfiltratorOrderTargeter(Func<Actor, bool> useEnterCursor)
: base("Infiltrate", 7, "enter", true, false)
{
ForceAttack=false;
ForceAttack = false;
this.useEnterCursor = useEnterCursor;
}
@@ -103,7 +103,10 @@ namespace OpenRA.Mods.RA
{
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (!target.HasTrait<IAcceptInfiltrator>())
return false;
if (!useEnterCursor(target))
cursor = "enter-blocked";

View File

@@ -109,7 +109,8 @@ namespace OpenRA.Mods.RA.Move
return true;
var blockingActors = world.ActorMap.GetUnitsAt(cell)
.Where(x => x != ignoreActor)
// Don't fail if the unit is already in this cell
.Where(x => x != ignoreActor && x != self)
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
.Where(x => blockedByMovers || ((self.Owner.Stances[x.Owner] != Stance.Ally) || !IsMovingInMyDirection(self, x)))
.ToList();

View File

@@ -423,6 +423,9 @@
<Compile Include="Buildings\BaseProvider.cs" />
<Compile Include="Render\WithSpinner.cs" />
<Compile Include="Widgets\Logic\ObserverShroudSelectorLogic.cs" />
<Compile Include="RepairsBridges.cs" />
<Compile Include="Activities\RepairBridge.cs" />
<Compile Include="BridgeHut.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -12,14 +12,14 @@ using System;
namespace OpenRA.Mods.RA.Orders
{
public class EnterOrderTargeter<T> : UnitTraitOrderTargeter<T>
public class EnterOrderTargeter<T> : UnitOrderTargeter
{
readonly Func<Actor, bool> canTarget;
readonly Func<Actor, bool> useEnterCursor;
public EnterOrderTargeter( string order, int priority, bool targetEnemy, bool targetAlly,
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor )
: base( order, priority, "enter", targetEnemy, targetAlly )
public EnterOrderTargeter(string order, int priority, bool targetEnemy, bool targetAlly,
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor)
: base (order, priority, "enter", targetEnemy, targetAlly)
{
this.canTarget = canTarget;
this.useEnterCursor = useEnterCursor;
@@ -27,8 +27,15 @@ namespace OpenRA.Mods.RA.Orders
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if( !base.CanTargetActor( self, target, forceAttack, forceQueued, ref cursor ) ) return false;
if( !canTarget( target ) ) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (!target.HasTrait<T>())
return false;
if (!canTarget(target))
return false;
cursor = useEnterCursor(target) ? "enter" : "enter-blocked";
IsQueued = forceQueued;
return true;

View File

@@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
@@ -57,17 +58,23 @@ namespace OpenRA.Mods.RA.Orders
public virtual bool IsQueued { get; protected set; }
}
public class UnitTraitOrderTargeter<T> : UnitOrderTargeter
public class TargetTypeOrderTargeter : UnitOrderTargeter
{
public UnitTraitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits )
: base( order, priority, cursor, targetEnemyUnits, targetAllyUnits )
string targetType;
public TargetTypeOrderTargeter(string targetType, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits)
: base(order, priority, cursor, targetEnemyUnits, targetAllyUnits)
{
this.targetType = targetType;
}
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if( !base.CanTargetActor( self, target, forceAttack, forceQueued, ref cursor ) ) return false;
if( !target.HasTrait<T>() ) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (!target.TraitsImplementing<ITargetable>().Any(t => t.TargetTypes.Contains(targetType)))
return false;
IsQueued = forceQueued;

View File

@@ -0,0 +1,96 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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.Collections.Generic;
using System.Drawing;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
class RepairsBridgesInfo : TraitInfo<RepairsBridges> {}
class RepairsBridges : IIssueOrder, IResolveOrder, IOrderVoice
{
public IEnumerable<IOrderTargeter> Orders
{
get { yield return new RepairBridgeOrderTargeter(); }
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "RepairBridge")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString != "RepairBridge")
return null;
var bridge = order.TargetActor.TraitOrDefault<BridgeHut>();
if (bridge == null)
return null;
return bridge.BridgeDamageState > DamageState.Undamaged ? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "RepairBridge")
{
var bridge = order.TargetActor.TraitOrDefault<BridgeHut>();
if (bridge == null)
return;
if (bridge.BridgeDamageState == DamageState.Undamaged)
return;
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new RepairBridge(order.TargetActor)));
}
}
class RepairBridgeOrderTargeter : UnitOrderTargeter
{
public RepairBridgeOrderTargeter()
: base("RepairBridge", 6, "goldwrench", true, true) { }
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
var bridge = target.TraitOrDefault<BridgeHut>();
if (bridge == null)
return false;
// Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise
var damage = bridge.BridgeDamageState;
if (!forceAttack && damage != DamageState.Dead)
return false;
IsQueued = forceQueued;
// Can't repair an undamaged bridge
if (damage == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
}
}
}

View File

@@ -84,7 +84,7 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new UnitTraitOrderTargeter<RenderInfantry>("Disguise", 7, "ability", true, true) { ForceAttack=false };
yield return new TargetTypeOrderTargeter("Disguise", "Disguise", 7, "ability", true, true) { ForceAttack=false };
}
}

View File

@@ -59,12 +59,11 @@ namespace OpenRA.Mods.RA
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor));
self.QueueActivity(new DonateSupplies(order.TargetActor, Info.Payload));
self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload)));
}
}
class SupplyTruckOrderTargeter : UnitTraitOrderTargeter<Building>
class SupplyTruckOrderTargeter : UnitOrderTargeter
{
public SupplyTruckOrderTargeter()
: base("DeliverSupplies", 5, "enter", false, true)
@@ -73,9 +72,14 @@ namespace OpenRA.Mods.RA
public override bool CanTargetActor(Actor self, Actor target, bool forceAttack, bool forceQueued, ref string cursor)
{
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor)) return false;
if (target.AppearsHostileTo(self)) return false;
if (!target.HasTrait<AcceptsSupplies>()) return false;
if (!base.CanTargetActor(self, target, forceAttack, forceQueued, ref cursor))
return false;
if (target.AppearsHostileTo(self))
return false;
if (!target.HasTrait<AcceptsSupplies>())
return false;
IsQueued = forceQueued;
return true;

View File

@@ -145,6 +145,12 @@ BRIDGE1:
Building:
Footprint: ____ ____ ____ ____
Dimensions: 4,4
FreeActor@north:
Actor: bridgehut
SpawnOffset: 2,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 0,2
BRIDGE2:
Inherits: ^Bridge
@@ -154,6 +160,12 @@ BRIDGE2:
Building:
Footprint: _____ _____ _____ _____ _____
Dimensions: 5,5
FreeActor@north:
Actor: bridgehut
SpawnOffset: 0,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 2,2
BRIDGE3:
Inherits: ^Bridge
@@ -163,6 +175,12 @@ BRIDGE3:
Building:
Footprint: ______ ______ ______ ______ ______
Dimensions: 6,5
FreeActor@north:
Actor: bridgehut
SpawnOffset: 3,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 1,2
BRIDGE4:
Inherits: ^Bridge
@@ -172,6 +190,23 @@ BRIDGE4:
Building:
Footprint: ______ ______ ______ ______
Dimensions: 6,4
FreeActor@north:
Actor: bridgehut
SpawnOffset: 1,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 3,2
BRIDGEHUT:
Building:
Footprint: __ __
Dimensions: 2,2
Selectable:
Selectable: false
Bounds: 48,48
BridgeHut:
TargetableBuilding:
TargetTypes: BridgeHut
C1:
Inherits: ^CivInfantry

View File

@@ -208,7 +208,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4
Armor:
Type: Wood
RepairableBuilding:
@@ -399,6 +399,3 @@
SoundOnDamageTransition:
DamagedSound: xplos.aud
DestroyedSound: xplobig4.aud
Building:
Footprint: ______ ______ ______ ______
Dimensions: 6,4

View File

@@ -182,6 +182,7 @@ E6:
Passenger:
PipType: Yellow
EngineerRepair:
RepairsBridges:
Captures:
CaptureTypes: building, husk
-AutoTarget:

View File

@@ -302,6 +302,12 @@ BRIDGE1:
Building:
Footprint: ____ ____ ____ ____
Dimensions: 4,4
FreeActor@north:
Actor: bridgehut
SpawnOffset: 2,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 0,2
BRIDGE2:
Inherits: ^Bridge
@@ -311,6 +317,12 @@ BRIDGE2:
Building:
Footprint: _____ _____ _____ _____ _____
Dimensions: 5,5
FreeActor@north:
Actor: bridgehut
SpawnOffset: 0,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 2,2
BRIDGE3:
Inherits: ^Bridge
@@ -320,6 +332,12 @@ BRIDGE3:
Building:
Footprint: ______ ______ ______ ______ ______
Dimensions: 6,5
FreeActor@north:
Actor: bridgehut
SpawnOffset: 3,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 1,2
BRIDGE4:
Inherits: ^Bridge
@@ -329,6 +347,23 @@ BRIDGE4:
Building:
Footprint: ______ ______ ______ ______
Dimensions: 6,4
FreeActor@north:
Actor: bridgehut
SpawnOffset: 1,0
FreeActor@south:
Actor: bridgehut
SpawnOffset: 3,2
BRIDGEHUT:
Building:
Footprint: __ __
Dimensions: 2,2
Selectable:
Selectable: false
Bounds: 48,48
BridgeHut:
TargetableBuilding:
TargetTypes: BridgeHut
C1:
Inherits: ^CivInfantry

View File

@@ -225,7 +225,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4
Armor:
Type: Wood
RepairableBuilding:
@@ -267,7 +267,6 @@
Sellable:
Capturable:
CapturableBar:
C4Demolishable:
DebugMuzzlePositions:
^CivBuilding:
@@ -339,7 +338,7 @@
Adjacent: 7
TerrainTypes: Clear,Road
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4
Wall:
CrushClasses: wall
CrushSound: sandbag2.aud
@@ -356,7 +355,6 @@
RelativeToTopLeft: yes
AutoTargetIgnore:
Sellable:
C4Demolishable:
^Tree:
Tooltip:
@@ -419,10 +417,7 @@
TargetTypes: Ground, Water
BelowUnits:
Health:
# HP: 500
HP: 500
SoundOnDamageTransition:
DamagedSound: xplos.aud
DestroyedSound: xplobig4.aud
Building:
Footprint: ______ ______ ______ ______
Dimensions: 6,4

View File

@@ -166,6 +166,7 @@ E6:
Passenger:
PipType: Yellow
EngineerRepair:
RepairsBridges:
Captures:
CaptureTypes: building, husk
-AutoTarget:

View File

@@ -215,7 +215,7 @@
Selectable:
Priority: 2
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4
Building:
Dimensions: 1,1
Footprint: x
@@ -263,6 +263,5 @@
Types:Building
Sellable:
GivesBounty:
C4Demolishable:
DebugMuzzlePositions:
Bib:

View File

@@ -353,7 +353,7 @@ WALL:
#Selectable:
# Priority: 1
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4
RenderBuildingWall:
HasMakeAnimation: false
#GivesExperience:

View File

@@ -232,6 +232,9 @@ BR1:
DamagedTemplate: 236
DestroyedTemplate: 237
SouthOffset: 0,2
FreeActor:
Actor: bridgehut
SpawnOffset: 2,0
BR2:
Inherits: ^Bridge
@@ -240,6 +243,9 @@ BR2:
DamagedTemplate: 239
DestroyedTemplate: 240
NorthOffset: 3,0
FreeActor:
Actor: bridgehut
SpawnOffset: 1,1
BR3:
Inherits: ^Bridge
@@ -264,6 +270,12 @@ BRIDGE1:
Building:
Footprint: _____ _____ _____
Dimensions: 5,3
FreeActor@north:
Actor: bridgehut
SpawnOffset: 2,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 0,1
BRIDGE2:
Inherits: ^Bridge
@@ -273,4 +285,21 @@ BRIDGE2:
DestroyedTemplate: 134
Building:
Footprint: _____ _____
Dimensions: 5,2
Dimensions: 5,2
FreeActor@north:
Actor: bridgehut
SpawnOffset: 0,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 2,1
BRIDGEHUT:
Building:
Footprint: __ __
Dimensions: 2,2
Selectable:
Selectable: false
Bounds: 48,48
BridgeHut:
TargetableBuilding:
TargetTypes: BridgeHut

View File

@@ -83,7 +83,7 @@
Selectable:
Voice: GenericVoice
TargetableUnit:
TargetTypes: Ground
TargetTypes: Ground, Disguise
RenderInfantry:
AutoTarget:
AttackMove:
@@ -164,7 +164,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4, DemoTruck
Building:
Dimensions: 1,1
Footprint: x
@@ -210,7 +210,7 @@
Selectable:
Priority: 1
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4, DemoTruck
RenderBuildingWall:
HasMakeAnimation: false
Palette: terrain

View File

@@ -162,6 +162,7 @@ E6:
Passenger:
PipType: Yellow
EngineerRepair:
RepairsBridges:
Captures:
Sabotage: yes
TakeCover:

View File

@@ -246,7 +246,8 @@ BARL:
AutoTargetIgnore:
Armor:
Type: Light
-C4Demolishable:
TargetableBuilding:
TargetTypes: Ground, DemoTruck
BRL3:
Inherits: ^TechBuilding
@@ -261,7 +262,8 @@ BRL3:
AutoTargetIgnore:
Armor:
Type: Light
-C4Demolishable:
TargetableBuilding:
TargetTypes: Ground, DemoTruck
MISS:
Inherits: ^TechBuilding
@@ -320,6 +322,9 @@ BR1:
DamagedTemplate: 236
DestroyedTemplate: 237
SouthOffset: 0,2
FreeActor:
Actor: bridgehut
SpawnOffset: 2,0
BR2:
Inherits: ^Bridge
@@ -328,6 +333,9 @@ BR2:
DamagedTemplate: 239
DestroyedTemplate: 240
NorthOffset: 3,0
FreeActor:
Actor: bridgehut
SpawnOffset: 1,1
BR3:
Inherits: ^Bridge
@@ -352,6 +360,12 @@ BRIDGE1:
Building:
Footprint: _____ _____ _____
Dimensions: 5,3
FreeActor@north:
Actor: bridgehut
SpawnOffset: 2,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 0,1
BRIDGE2:
Inherits: ^Bridge
@@ -362,6 +376,12 @@ BRIDGE2:
Building:
Footprint: _____ _____
Dimensions: 5,2
FreeActor@north:
Actor: bridgehut
SpawnOffset: 0,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 2,1
SBRIDGE1:
Inherits: ^SVBridge
@@ -372,6 +392,13 @@ SBRIDGE1:
Building:
Footprint: ___ ___
Dimensions: 3,2
FreeActor@north:
Actor: bridgehut.small
SpawnOffset: 1,0
FreeActor@south:
Actor: bridgehut.small
SpawnOffset: 1,1
SBRIDGE2:
Inherits: ^SHBridge
Bridge:
@@ -381,6 +408,13 @@ SBRIDGE2:
Building:
Footprint: __ __ __
Dimensions: 2,3
FreeActor@west:
Actor: bridgehut.small
SpawnOffset: 0,1
FreeActor@east:
Actor: bridgehut.small
SpawnOffset: 1,1
SBRIDGE3:
Inherits: ^STDBridge
Bridge:
@@ -390,6 +424,13 @@ SBRIDGE3:
Building:
Footprint: ____ ____
Dimensions: 4,2
FreeActor@north:
Actor: bridgehut
SpawnOffset: 2,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 0,1
SBRIDGE4:
Inherits: ^STDBridge
Bridge:
@@ -399,6 +440,34 @@ SBRIDGE4:
Building:
Footprint: ____ ____
Dimensions: 4,2
FreeActor@north:
Actor: bridgehut
SpawnOffset: 0,-1
FreeActor@south:
Actor: bridgehut
SpawnOffset: 2,1
BRIDGEHUT:
Building:
Footprint: __ __
Dimensions: 2,2
Selectable:
Selectable: false
Bounds: 48,48
BridgeHut:
TargetableBuilding:
TargetTypes: BridgeHut
BRIDGEHUT.small:
Building:
Footprint: _
Dimensions: 1,1
Selectable:
Selectable: false
Bounds: 24,24
BridgeHut:
TargetableBuilding:
TargetTypes: BridgeHut
#Desert Terrain Expansion
V20:

View File

@@ -96,7 +96,7 @@
Selectable:
Voice: GenericVoice
TargetableUnit:
TargetTypes: Ground
TargetTypes: Ground, Disguise
RenderInfantry:
AutoTarget:
DebugRetiliateAgainstAggressor:
@@ -196,7 +196,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4, DemoTruck
Building:
Dimensions: 1,1
Footprint: x
@@ -227,7 +227,6 @@
AcceptsSupplies:
GivesBounty:
UpdatesPlayerStatistics:
C4Demolishable:
DebugMuzzlePositions:
^Wall:
@@ -249,7 +248,7 @@
Selectable:
Priority: 1
TargetableBuilding:
TargetTypes: Ground
TargetTypes: Ground, C4, DemoTruck
RenderBuildingWall:
HasMakeAnimation: false
Palette: terrain
@@ -262,7 +261,6 @@
Types:Wall
Sellable:
UpdatesPlayerStatistics:
C4Demolishable:
^TechBuilding:
Inherits: ^Building

View File

@@ -167,6 +167,7 @@ E6:
Passenger:
PipType: Yellow
EngineerRepair:
RepairsBridges:
Captures:
TakeCover:
-AutoTarget: