Make bridges repairable.
This commit is contained in:
@@ -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)
|
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! */
|
if (IsDead) return; /* overkill! don't count extra hits as more kills! */
|
||||||
|
|||||||
@@ -8,11 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Effects;
|
||||||
using OpenRA.FileFormats;
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Mods.RA.Move;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA
|
namespace OpenRA.Mods.RA
|
||||||
@@ -21,6 +24,8 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
public readonly bool Long = false;
|
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 Template = 0;
|
||||||
public readonly ushort DamagedTemplate = 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);
|
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()
|
void KillUnitsOnBridge()
|
||||||
{
|
{
|
||||||
foreach (var c in TileSprites[currentTemplate].Keys)
|
foreach (var c in TileSprites[currentTemplate].Keys)
|
||||||
@@ -162,35 +162,55 @@ namespace OpenRA.Mods.RA
|
|||||||
a.Kill(self);
|
a.Kill(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dead = false;
|
bool NeighbourIsDeadShore(Bridge neighbour)
|
||||||
void UpdateState()
|
|
||||||
{
|
{
|
||||||
// If this is a long bridge next to a destroyed shore piece, we need die to give clean edges to the break
|
return neighbour != null && Info.ShorePieces.Contains(neighbour.Type) && neighbour.Health.IsDead;
|
||||||
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;
|
|
||||||
|
|
||||||
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
|
// Long bridges have custom art for multiple segments being destroyed
|
||||||
bool waterToSouth = !IsIntact(southNeighbour);
|
var northIsDead = northNeighbour != null && northNeighbour.LongBridgeSegmentIsDead();
|
||||||
bool waterToNorth = !IsIntact(northNeighbour);
|
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)
|
return Info.DestroyedTemplate;
|
||||||
currentTemplate = Info.DestroyedPlusBothTemplate;
|
|
||||||
else if (waterToNorth)
|
|
||||||
currentTemplate = Info.DestroyedPlusNorthTemplate;
|
|
||||||
else if (waterToSouth)
|
|
||||||
currentTemplate = Info.DestroyedPlusSouthTemplate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
if (currentTemplate == oldTemplate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -198,18 +218,81 @@ namespace OpenRA.Mods.RA
|
|||||||
foreach (var c in TileSprites[currentTemplate].Keys)
|
foreach (var c in TileSprites[currentTemplate].Keys)
|
||||||
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
|
self.World.Map.CustomTerrain[c.X, c.Y] = GetTerrainType(c);
|
||||||
|
|
||||||
if (ds == DamageState.Dead && !dead)
|
if (LongBridgeSegmentIsDead() && !killedUnits)
|
||||||
{
|
{
|
||||||
dead = true;
|
killedUnits = true;
|
||||||
KillUnitsOnBridge();
|
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)
|
public void DamageStateChanged(Actor self, AttackInfo e)
|
||||||
{
|
{
|
||||||
UpdateState();
|
UpdateState();
|
||||||
if (northNeighbour != null) northNeighbour.UpdateState();
|
if (northNeighbour != null)
|
||||||
if (southNeighbour != null) southNeighbour.UpdateState();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user