Move everything Bridge-related to Mods.Common
This commit is contained in:
39
OpenRA.Mods.Common/Activities/RepairBridge.cs
Normal file
39
OpenRA.Mods.Common/Activities/RepairBridge.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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 OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Activities
|
||||
{
|
||||
class RepairBridge : Enter
|
||||
{
|
||||
readonly BridgeHut hut;
|
||||
|
||||
public RepairBridge(Actor self, Actor target)
|
||||
: base(self, target)
|
||||
{
|
||||
hut = target.Trait<BridgeHut>();
|
||||
}
|
||||
|
||||
protected override bool CanReserve(Actor self)
|
||||
{
|
||||
return hut.BridgeDamageState != DamageState.Undamaged && !hut.Repairing && hut.Bridge.GetHut(0) != null && hut.Bridge.GetHut(1) != null;
|
||||
}
|
||||
|
||||
protected override void OnInside(Actor self)
|
||||
{
|
||||
if (hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing || hut.Bridge.GetHut(0) == null || hut.Bridge.GetHut(1) == null)
|
||||
return;
|
||||
hut.Repair(self);
|
||||
self.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@
|
||||
<Compile Include="Activities\Rearm.cs" />
|
||||
<Compile Include="Activities\RemoveSelf.cs" />
|
||||
<Compile Include="Activities\Repair.cs" />
|
||||
<Compile Include="Activities\RepairBridge.cs" />
|
||||
<Compile Include="Activities\RepairBuilding.cs" />
|
||||
<Compile Include="Activities\Sell.cs" />
|
||||
<Compile Include="Activities\SimpleTeleport.cs" />
|
||||
@@ -177,6 +178,8 @@
|
||||
<Compile Include="Traits\Buildings\BaseBuilding.cs" />
|
||||
<Compile Include="Traits\Buildings\BaseProvider.cs" />
|
||||
<Compile Include="Traits\Buildings\Bib.cs" />
|
||||
<Compile Include="Traits\Buildings\Bridge.cs" />
|
||||
<Compile Include="Traits\Buildings\BridgeHut.cs" />
|
||||
<Compile Include="Traits\Buildings\Building.cs" />
|
||||
<Compile Include="Traits\Buildings\BuildingInfluence.cs" />
|
||||
<Compile Include="Traits\Buildings\BuildingUtils.cs" />
|
||||
@@ -277,6 +280,7 @@
|
||||
<Compile Include="Traits\Render\WithShadow.cs" />
|
||||
<Compile Include="Traits\Render\WithSmoke.cs" />
|
||||
<Compile Include="Traits\Render\WithTurret.cs" />
|
||||
<Compile Include="Traits\RepairsBridges.cs" />
|
||||
<Compile Include="Traits\SeedsResource.cs" />
|
||||
<Compile Include="Traits\StoresResources.cs" />
|
||||
<Compile Include="Traits\SelfHealing.cs" />
|
||||
@@ -296,6 +300,7 @@
|
||||
<Compile Include="Traits\Upgrades\UpgradeManager.cs" />
|
||||
<Compile Include="Traits\Valued.cs" />
|
||||
<Compile Include="Traits\Wanders.cs" />
|
||||
<Compile Include="Traits\World\BridgeLayer.cs" />
|
||||
<Compile Include="Traits\World\CreateMPPlayers.cs" />
|
||||
<Compile Include="Traits\World\DomainIndex.cs" />
|
||||
<Compile Include="Traits\World\LoadWidgetAtGameStart.cs" />
|
||||
|
||||
356
OpenRA.Mods.Common/Traits/Buildings/Bridge.cs
Normal file
356
OpenRA.Mods.Common/Traits/Buildings/Bridge.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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.Effects;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
class BridgeInfo : ITraitInfo, Requires<HealthInfo>, Requires<BuildingInfo>
|
||||
{
|
||||
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;
|
||||
public readonly ushort DestroyedTemplate = 0;
|
||||
|
||||
// For long bridges
|
||||
public readonly ushort DestroyedPlusNorthTemplate = 0;
|
||||
public readonly ushort DestroyedPlusSouthTemplate = 0;
|
||||
public readonly ushort DestroyedPlusBothTemplate = 0;
|
||||
|
||||
public readonly string[] ShorePieces = { "br1", "br2" };
|
||||
public readonly int[] NorthOffset = null;
|
||||
public readonly int[] SouthOffset = null;
|
||||
|
||||
[Desc("The name of the weapon to use when demolishing the bridge")]
|
||||
public readonly string DemolishWeapon = "Demolish";
|
||||
|
||||
public object Create(ActorInitializer init) { return new Bridge(init.Self, this); }
|
||||
|
||||
public IEnumerable<Pair<ushort, float>> Templates
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Template != 0)
|
||||
yield return Pair.New(Template, 1f);
|
||||
|
||||
if (DamagedTemplate != 0)
|
||||
yield return Pair.New(DamagedTemplate, .5f);
|
||||
|
||||
if (DestroyedTemplate != 0)
|
||||
yield return Pair.New(DestroyedTemplate, 0f);
|
||||
|
||||
if (DestroyedPlusNorthTemplate != 0)
|
||||
yield return Pair.New(DestroyedPlusNorthTemplate, 0f);
|
||||
|
||||
if (DestroyedPlusSouthTemplate != 0)
|
||||
yield return Pair.New(DestroyedPlusSouthTemplate, 0f);
|
||||
|
||||
if (DestroyedPlusBothTemplate != 0)
|
||||
yield return Pair.New(DestroyedPlusBothTemplate, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Bridge : IRender, INotifyDamageStateChanged
|
||||
{
|
||||
readonly Building building;
|
||||
readonly Bridge[] neighbours = new Bridge[2];
|
||||
readonly BridgeHut[] huts = new BridgeHut[2]; // Huts before this / first & after this / last
|
||||
readonly Health health;
|
||||
readonly Actor self;
|
||||
readonly BridgeInfo info;
|
||||
readonly string type;
|
||||
|
||||
readonly Lazy<bool> isDangling;
|
||||
ushort template;
|
||||
Dictionary<CPos, byte> footprint;
|
||||
|
||||
public BridgeHut Hut { get; private set; }
|
||||
public bool IsDangling { get { return isDangling.Value; } }
|
||||
|
||||
public Bridge(Actor self, BridgeInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
health = self.Trait<Health>();
|
||||
health.RemoveOnDeath = false;
|
||||
this.info = info;
|
||||
type = self.Info.Name;
|
||||
isDangling = new Lazy<bool>(() => huts[0] == huts[1] && (neighbours[0] == null || neighbours[1] == null));
|
||||
building = self.Trait<Building>();
|
||||
}
|
||||
|
||||
public Bridge Neighbour(int direction) { return neighbours[direction]; }
|
||||
public IEnumerable<Bridge> Enumerate(int direction, bool includeSelf = false)
|
||||
{
|
||||
for (var b = includeSelf ? this : neighbours[direction]; b != null; b = b.neighbours[direction])
|
||||
yield return b;
|
||||
}
|
||||
|
||||
public void Do(Action<Bridge, int> action)
|
||||
{
|
||||
action(this, -1);
|
||||
for (var d = 0; d <= 1; d++)
|
||||
if (neighbours[d] != null)
|
||||
action(neighbours[d], d);
|
||||
}
|
||||
|
||||
public void Create(ushort template, Dictionary<CPos, byte> footprint)
|
||||
{
|
||||
this.template = template;
|
||||
this.footprint = footprint;
|
||||
|
||||
// Set the initial custom terrain types
|
||||
foreach (var c in footprint.Keys)
|
||||
self.World.Map.CustomTerrain[c] = GetTerrainType(c);
|
||||
}
|
||||
|
||||
byte GetTerrainType(CPos cell)
|
||||
{
|
||||
var dx = cell - self.Location;
|
||||
var index = dx.X + self.World.TileSet.Templates[template].Size.X * dx.Y;
|
||||
return self.World.TileSet.GetTerrainIndex(new TerrainTile(template, (byte)index));
|
||||
}
|
||||
|
||||
public void LinkNeighbouringBridges(World world, BridgeLayer bridges)
|
||||
{
|
||||
for (var d = 0; d <= 1; d++)
|
||||
{
|
||||
if (neighbours[d] != null)
|
||||
continue; // Already linked by reverse lookup
|
||||
|
||||
var offset = d == 0 ? info.NorthOffset : info.SouthOffset;
|
||||
if (offset == null)
|
||||
continue; // End piece type
|
||||
|
||||
neighbours[d] = GetNeighbor(offset, bridges);
|
||||
if (neighbours[d] != null)
|
||||
neighbours[d].neighbours[1 - d] = this; // Save reverse lookup
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddHut(BridgeHut hut)
|
||||
{
|
||||
if (huts[0] == huts[1])
|
||||
huts[1] = hut;
|
||||
if (Hut == null)
|
||||
{
|
||||
Hut = hut; // Assume only one until called again
|
||||
if (huts[0] == null)
|
||||
huts[0] = hut; // Set only first time
|
||||
for (var d = 0; d <= 1; d++)
|
||||
for (var b = neighbours[d]; b != null; b = b.Hut == null ? b.neighbours[d] : null)
|
||||
b.huts[1 - d] = hut;
|
||||
}
|
||||
else
|
||||
Hut = null;
|
||||
}
|
||||
|
||||
public BridgeHut GetHut(int index) { return huts[index]; }
|
||||
public Bridge GetNeighbor(int[] offset, BridgeLayer bridges)
|
||||
{
|
||||
if (offset == null)
|
||||
return null;
|
||||
|
||||
return bridges.GetBridge(self.Location + new CVec(offset[0], offset[1]));
|
||||
}
|
||||
|
||||
IRenderable[] TemplateRenderables(WorldRenderer wr, PaletteReference palette, ushort template)
|
||||
{
|
||||
var offset = FootprintUtils.CenterOffset(self.World, building.Info).Y + 1024;
|
||||
|
||||
return footprint.Select(c => (IRenderable)(new SpriteRenderable(
|
||||
wr.Theater.TileSprite(new TerrainTile(template, c.Value)),
|
||||
wr.World.Map.CenterOfCell(c.Key), WVec.Zero, -offset, palette, 1f, true))).ToArray();
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
Dictionary<ushort, IRenderable[]> renderables;
|
||||
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
var palette = wr.Palette("terrain");
|
||||
renderables = new Dictionary<ushort, IRenderable[]>();
|
||||
foreach (var t in info.Templates)
|
||||
renderables.Add(t.First, TemplateRenderables(wr, palette, t.First));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return renderables[template];
|
||||
}
|
||||
|
||||
void KillUnitsOnBridge()
|
||||
{
|
||||
foreach (var c in footprint.Keys)
|
||||
foreach (var a in self.World.ActorMap.GetUnitsAt(c))
|
||||
if (a.HasTrait<IPositionable>() && !a.Trait<IPositionable>().CanEnterCell(c))
|
||||
a.Kill(self);
|
||||
}
|
||||
|
||||
bool NeighbourIsDeadShore(Bridge neighbour)
|
||||
{
|
||||
return neighbour != null && info.ShorePieces.Contains(neighbour.type) && neighbour.health.IsDead;
|
||||
}
|
||||
|
||||
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(neighbours[0]) || NeighbourIsDeadShore(neighbours[1]))
|
||||
return true;
|
||||
|
||||
return health.IsDead;
|
||||
}
|
||||
|
||||
ushort ChooseTemplate()
|
||||
{
|
||||
if (info.Long && LongBridgeSegmentIsDead())
|
||||
{
|
||||
// Long bridges have custom art for multiple segments being destroyed
|
||||
var previousIsDead = neighbours[0] != null && neighbours[0].LongBridgeSegmentIsDead();
|
||||
var nextIsDead = neighbours[1] != null && neighbours[1].LongBridgeSegmentIsDead();
|
||||
if (previousIsDead && nextIsDead)
|
||||
return info.DestroyedPlusBothTemplate;
|
||||
if (previousIsDead)
|
||||
return info.DestroyedPlusNorthTemplate;
|
||||
if (nextIsDead)
|
||||
return 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 = template;
|
||||
|
||||
template = ChooseTemplate();
|
||||
if (template == oldTemplate)
|
||||
return;
|
||||
|
||||
// Update map
|
||||
foreach (var c in footprint.Keys)
|
||||
self.World.Map.CustomTerrain[c] = GetTerrainType(c);
|
||||
|
||||
// If this bridge repair operation connects two pathfinding domains,
|
||||
// update the domain index.
|
||||
var domainIndex = self.World.WorldActor.TraitOrDefault<DomainIndex>();
|
||||
if (domainIndex != null)
|
||||
domainIndex.UpdateCells(self.World, footprint.Keys);
|
||||
|
||||
if (LongBridgeSegmentIsDead() && !killedUnits)
|
||||
{
|
||||
killedUnits = true;
|
||||
KillUnitsOnBridge();
|
||||
}
|
||||
}
|
||||
|
||||
public void Repair(Actor repairer, int direction, Action onComplete)
|
||||
{
|
||||
// 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);
|
||||
if (direction < 0 ? neighbours[0] == null && neighbours[1] == null : Hut != null || neighbours[direction] == null)
|
||||
onComplete(); // Done if single or reached other hut
|
||||
});
|
||||
|
||||
// Repair adjacent spans onto next hut or end
|
||||
if (direction >= 0 && Hut == null && neighbours[direction] != null)
|
||||
{
|
||||
var delay = initialDamage == DamageState.Undamaged || NeighbourIsDeadShore(neighbours[direction]) ?
|
||||
0 : info.RepairPropagationDelay;
|
||||
|
||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
||||
neighbours[direction].Repair(repairer, direction, onComplete))));
|
||||
}
|
||||
}
|
||||
|
||||
public void DamageStateChanged(Actor self, AttackInfo e)
|
||||
{
|
||||
Do((b, d) => b.UpdateState());
|
||||
|
||||
// Need to update the neighbours neighbour to correctly
|
||||
// display the broken shore hack
|
||||
if (info.ShorePieces.Contains(type))
|
||||
for (var d = 0; d <= 1; d++)
|
||||
if (neighbours[d] != null && neighbours[d].neighbours[d] != null)
|
||||
neighbours[d].neighbours[d].UpdateState();
|
||||
}
|
||||
|
||||
void AggregateDamageState(Bridge b, int d, ref DamageState damage)
|
||||
{
|
||||
if (b.health.DamageState > damage)
|
||||
damage = b.health.DamageState;
|
||||
if (b.Hut == null && d >= 0 && b.neighbours[d] != null)
|
||||
AggregateDamageState(b.neighbours[d], d, ref damage);
|
||||
}
|
||||
|
||||
// Find the worst span damage before other hut
|
||||
public DamageState AggregateDamageState()
|
||||
{
|
||||
var damage = health.DamageState;
|
||||
Do((b, d) => AggregateDamageState(b, d, ref damage));
|
||||
return damage;
|
||||
}
|
||||
|
||||
public void Demolish(Actor saboteur, int direction)
|
||||
{
|
||||
var initialDamage = health.DamageState;
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var weapon = saboteur.World.Map.Rules.Weapons[info.DemolishWeapon.ToLowerInvariant()];
|
||||
|
||||
// Use .FromPos since this actor is killed. Cannot use Target.FromActor
|
||||
weapon.Impact(Target.FromPos(self.CenterPosition), saboteur, Enumerable.Empty<int>());
|
||||
|
||||
self.World.WorldActor.Trait<ScreenShaker>().AddEffect(15, self.CenterPosition, 6);
|
||||
self.Kill(saboteur);
|
||||
});
|
||||
|
||||
// Destroy adjacent spans between (including) huts
|
||||
if (direction >= 0 && Hut == null && neighbours[direction] != null)
|
||||
{
|
||||
var delay = initialDamage == DamageState.Dead || NeighbourIsDeadShore(neighbours[direction]) ?
|
||||
0 : info.RepairPropagationDelay;
|
||||
|
||||
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
|
||||
neighbours[direction].Demolish(saboteur, direction))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs
Normal file
55
OpenRA.Mods.Common/Traits/Buildings/BridgeHut.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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.Common.Traits
|
||||
{
|
||||
[Desc("Allows bridges to be targeted for demolition and repair.")]
|
||||
class BridgeHutInfo : IDemolishableInfo, ITraitInfo
|
||||
{
|
||||
public bool IsValidTarget(ActorInfo actorInfo, Actor saboteur) { return false; } // TODO: bridges don't support frozen under fog
|
||||
|
||||
public object Create(ActorInitializer init) { return new BridgeHut(init); }
|
||||
}
|
||||
|
||||
class BridgeHut : IDemolishable
|
||||
{
|
||||
public readonly Bridge FirstBridge;
|
||||
public readonly Bridge Bridge;
|
||||
public DamageState BridgeDamageState { get { return Bridge.AggregateDamageState(); } }
|
||||
public bool Repairing { get { return repairDirections > 0; } }
|
||||
int repairDirections = 0;
|
||||
|
||||
public BridgeHut(ActorInitializer init)
|
||||
{
|
||||
Bridge = init.Get<ParentActorInit>().ActorValue.Trait<Bridge>();
|
||||
Bridge.AddHut(this);
|
||||
FirstBridge = Bridge.Enumerate(0, true).Last();
|
||||
}
|
||||
|
||||
public void Repair(Actor repairer)
|
||||
{
|
||||
repairDirections = Bridge.GetHut(0) != this && Bridge.GetHut(1) != this ? 2 : 1;
|
||||
Bridge.Do((b, d) => b.Repair(repairer, d, () => repairDirections--));
|
||||
}
|
||||
|
||||
public void Demolish(Actor self, Actor saboteur)
|
||||
{
|
||||
Bridge.Do((b, d) => b.Demolish(saboteur, d));
|
||||
}
|
||||
|
||||
public bool IsValidTarget(Actor self, Actor saboteur)
|
||||
{
|
||||
return BridgeDamageState != DamageState.Dead;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
OpenRA.Mods.Common/Traits/RepairsBridges.cs
Normal file
101
OpenRA.Mods.Common/Traits/RepairsBridges.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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.Common.Activities;
|
||||
using OpenRA.Mods.Common.Orders;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Can enter a BridgeHut to trigger a repair.")]
|
||||
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 hut = order.TargetActor.TraitOrDefault<BridgeHut>();
|
||||
if (hut == null)
|
||||
return null;
|
||||
|
||||
return hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing || hut.Bridge.IsDangling ? null : "Attack";
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "RepairBridge")
|
||||
{
|
||||
var hut = order.TargetActor.TraitOrDefault<BridgeHut>();
|
||||
if (hut == null)
|
||||
return;
|
||||
|
||||
if (hut.BridgeDamageState == DamageState.Undamaged || hut.Repairing || hut.Bridge.IsDangling)
|
||||
return;
|
||||
|
||||
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
|
||||
|
||||
self.CancelActivity();
|
||||
self.QueueActivity(new RepairBridge(self, order.TargetActor));
|
||||
}
|
||||
}
|
||||
|
||||
class RepairBridgeOrderTargeter : UnitOrderTargeter
|
||||
{
|
||||
public RepairBridgeOrderTargeter()
|
||||
: base("RepairBridge", 6, "goldwrench", true, true) { }
|
||||
|
||||
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
|
||||
{
|
||||
var hut = target.TraitOrDefault<BridgeHut>();
|
||||
if (hut == null)
|
||||
return false;
|
||||
|
||||
// Require force attack to heal partially damaged bridges to avoid unnecessary cursor noise
|
||||
var damage = hut.BridgeDamageState;
|
||||
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead)
|
||||
return false;
|
||||
|
||||
// Obey force moving onto bridges
|
||||
if (modifiers.HasModifier(TargetModifiers.ForceMove))
|
||||
return false;
|
||||
|
||||
// Can't repair a bridge that is undamaged, already under repair, or dangling
|
||||
if (damage == DamageState.Undamaged || hut.Repairing || hut.Bridge.IsDangling)
|
||||
cursor = "goldwrench-blocked";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
|
||||
{
|
||||
// TODO: Bridges don't yet support FrozenUnderFog.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
OpenRA.Mods.Common/Traits/World/BridgeLayer.cs
Normal file
118
OpenRA.Mods.Common/Traits/World/BridgeLayer.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2014 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.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
class BridgeLayerInfo : ITraitInfo
|
||||
{
|
||||
[ActorReference]
|
||||
public readonly string[] Bridges = { "bridge1", "bridge2" };
|
||||
|
||||
public object Create(ActorInitializer init) { return new BridgeLayer(init.Self, this); }
|
||||
}
|
||||
|
||||
class BridgeLayer : IWorldLoaded
|
||||
{
|
||||
readonly BridgeLayerInfo info;
|
||||
readonly World world;
|
||||
Dictionary<ushort, Pair<string, float>> bridgeTypes = new Dictionary<ushort, Pair<string, float>>();
|
||||
CellLayer<Bridge> bridges;
|
||||
|
||||
public BridgeLayer(Actor self, BridgeLayerInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
this.world = self.World;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
bridges = new CellLayer<Bridge>(w.Map);
|
||||
|
||||
// Build a list of templates that should be overlayed with bridges
|
||||
foreach (var bridge in info.Bridges)
|
||||
{
|
||||
var bi = w.Map.Rules.Actors[bridge].Traits.Get<BridgeInfo>();
|
||||
foreach (var template in bi.Templates)
|
||||
bridgeTypes.Add(template.First, Pair.New(bridge, template.Second));
|
||||
}
|
||||
|
||||
// Loop through the map looking for templates to overlay
|
||||
for (var i = w.Map.Bounds.Left; i < w.Map.Bounds.Right; i++)
|
||||
{
|
||||
for (var j = w.Map.Bounds.Top; j < w.Map.Bounds.Bottom; j++)
|
||||
{
|
||||
var cell = new CPos(i, j);
|
||||
if (bridgeTypes.ContainsKey(w.Map.MapTiles.Value[cell].Type))
|
||||
ConvertBridgeToActor(w, cell);
|
||||
}
|
||||
}
|
||||
|
||||
// Link adjacent (long)-bridges so that artwork is updated correctly
|
||||
foreach (var b in w.Actors.SelectMany(a => a.TraitsImplementing<Bridge>()))
|
||||
b.LinkNeighbouringBridges(w, this);
|
||||
}
|
||||
|
||||
void ConvertBridgeToActor(World w, CPos cell)
|
||||
{
|
||||
// This cell already has a bridge overlaying it from a previous iteration
|
||||
if (bridges[cell] != null)
|
||||
return;
|
||||
|
||||
// Correlate the tile "image" aka subtile with its position to find the template origin
|
||||
var tile = w.Map.MapTiles.Value[cell].Type;
|
||||
var index = w.Map.MapTiles.Value[cell].Index;
|
||||
var template = w.TileSet.Templates[tile];
|
||||
var ni = cell.X - index % template.Size.X;
|
||||
var nj = cell.Y - index / template.Size.X;
|
||||
|
||||
// Create a new actor for this bridge and keep track of which subtiles this bridge includes
|
||||
var bridge = w.CreateActor(bridgeTypes[tile].First, new TypeDictionary
|
||||
{
|
||||
new LocationInit(new CPos(ni, nj)),
|
||||
new OwnerInit(w.WorldActor.Owner),
|
||||
new HealthInit(bridgeTypes[tile].Second),
|
||||
}).Trait<Bridge>();
|
||||
|
||||
var subTiles = new Dictionary<CPos, byte>();
|
||||
|
||||
// For each subtile in the template
|
||||
for (byte ind = 0; ind < template.Size.X * template.Size.Y; ind++)
|
||||
{
|
||||
// Where do we expect to find the subtile
|
||||
var subtile = new CPos(ni + ind % template.Size.X, nj + ind / template.Size.X);
|
||||
|
||||
// This isn't the bridge you're looking for
|
||||
if (!w.Map.Contains(subtile) || w.Map.MapTiles.Value[subtile].Type != tile ||
|
||||
w.Map.MapTiles.Value[subtile].Index != ind)
|
||||
continue;
|
||||
|
||||
subTiles.Add(subtile, ind);
|
||||
bridges[subtile] = bridge;
|
||||
}
|
||||
|
||||
bridge.Create(tile, subTiles);
|
||||
}
|
||||
|
||||
// Used to check for neighbouring bridges
|
||||
public Bridge GetBridge(CPos cell)
|
||||
{
|
||||
if (!world.Map.Contains(cell))
|
||||
return null;
|
||||
|
||||
return bridges[cell];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user