Reimplement demolition lock using conditions.

This commit is contained in:
Paul Chote
2018-10-27 17:09:30 +00:00
committed by abcdefg30
parent 0901a7d9de
commit 18b84750aa
25 changed files with 239 additions and 71 deletions

View File

@@ -52,23 +52,13 @@ namespace OpenRA.Mods.Common.Activities
if (target.IsDead)
return;
w.Add(new FlashTarget(target, count: flashes, delay: flashesDelay, interval: flashInterval));
foreach (var ind in notifiers)
ind.Demolishing(self);
w.Add(new FlashTarget(target, count: flashes, delay: flashesDelay, interval: flashInterval));
w.Add(new DelayedAction(delay, () =>
{
if (target.IsDead)
return;
var modifiers = target.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null));
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
demolishables.Do(d => d.Demolish(target, self));
}));
foreach (var d in demolishables)
d.Demolish(target, self, delay);
});
}
}

View File

@@ -589,6 +589,7 @@
<Compile Include="Traits\World\WeatherOverlay.cs" />
<Compile Include="Traits\World\ActorSpawnManager.cs" />
<Compile Include="Traits\ActorSpawner.cs" />
<Compile Include="UpdateRules\Rules\20180923\RemovedDemolishLocking.cs" />
<Compile Include="UpdateRules\Rules\20180923\RenameCrateActionNotification.cs" />
<Compile Include="UpdateRules\Rules\20180923\MergeCaptureTraits.cs" />
<Compile Include="UpdateRules\Rules\20180923\RemovedNotifyBuildComplete.cs" />

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -164,7 +165,24 @@ namespace OpenRA.Mods.Common.Traits
repairDelay = Info.RepairPropagationDelay;
}
public void Demolish(Actor self, Actor saboteur)
bool IDemolishable.IsValidTarget(Actor self, Actor saboteur)
{
return true;
}
void IDemolishable.Demolish(Actor self, Actor saboteur, int delay)
{
// TODO: Handle using ITick
self.World.Add(new DelayedAction(delay, () =>
{
if (self.IsDead)
return;
var modifiers = self.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null));
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
{
if (Info.DemolishPropagationDelay > 0)
{
@@ -176,6 +194,8 @@ namespace OpenRA.Mods.Common.Traits
foreach (var s in segments.Values)
s.Demolish(saboteur);
}
}));
}
public void DemolishStep()
{
@@ -202,11 +222,6 @@ namespace OpenRA.Mods.Common.Traits
demolishStep++;
}
public bool IsValidTarget(Actor self, Actor saboteur)
{
return true;
}
public DamageState BridgeDamageState
{
get

View File

@@ -244,7 +244,7 @@ namespace OpenRA.Mods.Common.Traits
}
public class Building : IOccupySpace, ITargetableCells, INotifySold, INotifyTransform, ISync, INotifyCreated,
INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyDemolition
INotifyAddedToWorld, INotifyRemovedFromWorld
{
public readonly bool SkipMakeAnimation;
public readonly BuildingInfo Info;
@@ -333,11 +333,6 @@ namespace OpenRA.Mods.Common.Traits
notify.BuildingComplete(self);
}
void INotifyDemolition.Demolishing(Actor self)
{
Lock();
}
void INotifySold.Selling(Actor self)
{
if (Info.RemoveSmudgesOnSell)

View File

@@ -10,6 +10,7 @@
#endregion
using System.Linq;
using OpenRA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
@@ -43,14 +44,26 @@ namespace OpenRA.Mods.Common.Traits
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)
bool IDemolishable.IsValidTarget(Actor self, Actor saboteur)
{
return BridgeDamageState != DamageState.Dead;
}
void IDemolishable.Demolish(Actor self, Actor saboteur, int delay)
{
// TODO: Handle using ITick
self.World.Add(new DelayedAction(delay, () =>
{
if (self.IsDead)
return;
var modifiers = self.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null));
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
Bridge.Do((b, d) => b.Demolish(saboteur, d));
}));
}
}
}

View File

@@ -9,43 +9,91 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Handle demolitions from C4 explosives.")]
public class DemolishableInfo : IDemolishableInfo, ITraitInfo
public class DemolishableInfo : ConditionalTraitInfo, IDemolishableInfo, ITraitInfo
{
public bool IsValidTarget(ActorInfo actorInfo, Actor saboteur) { return true; }
[Desc("If true and this actor has EjectOnDeath, no actor will be spawned.")]
public readonly bool PreventsEjectOnDeath = false;
[GrantedConditionReference]
[Desc("Condition to grant during demolition countdown.")]
public readonly string Condition = null;
public object Create(ActorInitializer init) { return new Demolishable(init.Self, this); }
public override object Create(ActorInitializer init) { return new Demolishable(this); }
}
public class Demolishable : IDemolishable, IPreventsEjectOnDeath
public class Demolishable : ConditionalTrait<DemolishableInfo>, IDemolishable, ITick
{
readonly DemolishableInfo info;
class DemolishAction
{
public readonly Actor Saboteur;
public readonly int Token;
public int Delay;
public Demolishable(Actor self, DemolishableInfo info)
public DemolishAction(Actor saboteur, int delay, int token)
{
this.info = info;
Saboteur = saboteur;
Delay = delay;
Token = token;
}
}
public bool PreventsEjectOnDeath(Actor self)
ConditionManager conditionManager;
List<DemolishAction> actions = new List<DemolishAction>();
public Demolishable(DemolishableInfo info)
: base(info) { }
protected override void Created(Actor self)
{
return info.PreventsEjectOnDeath;
base.Created(self);
conditionManager = self.TraitOrDefault<ConditionManager>();
}
public void Demolish(Actor self, Actor saboteur)
bool IDemolishable.IsValidTarget(Actor self, Actor saboteur)
{
self.Kill(saboteur);
return !IsTraitDisabled;
}
public bool IsValidTarget(Actor self, Actor saboteur)
void IDemolishable.Demolish(Actor self, Actor saboteur, int delay)
{
return true;
if (IsTraitDisabled)
return;
var token = ConditionManager.InvalidConditionToken;
if (conditionManager != null && !string.IsNullOrEmpty(Info.Condition))
token = conditionManager.GrantCondition(self, Info.Condition);
actions.Add(new DemolishAction(saboteur, delay, token));
}
void ITick.Tick(Actor self)
{
if (IsTraitDisabled)
return;
foreach (var a in actions)
{
if (a.Delay-- <= 0)
{
var modifiers = self.TraitsImplementing<IDamageModifier>()
.Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(self, null));
if (Util.ApplyPercentageModifiers(100, modifiers) > 0)
self.Kill(a.Saboteur);
else if (a.Token != ConditionManager.InvalidConditionToken)
conditionManager.RevokeCondition(self, a.Token);
}
}
}
}
}

View File

@@ -40,8 +40,6 @@ namespace OpenRA.Mods.Common.Traits
public override object Create(ActorInitializer init) { return new EjectOnDeath(init.Self, this); }
}
public interface IPreventsEjectOnDeath { bool PreventsEjectOnDeath(Actor self); }
public class EjectOnDeath : ConditionalTrait<EjectOnDeathInfo>, INotifyKilled
{
public EjectOnDeath(Actor self, EjectOnDeathInfo info)
@@ -52,10 +50,6 @@ namespace OpenRA.Mods.Common.Traits
if (IsTraitDisabled || self.Owner.WinState == WinState.Lost || !self.World.Map.Contains(self.Location))
return;
foreach (var condition in self.TraitsImplementing<IPreventsEjectOnDeath>())
if (condition.PreventsEjectOnDeath(self))
return;
var r = self.World.SharedRandom.Next(1, 100);
if (r <= 100 - Info.SuccessRate)

View File

@@ -71,8 +71,8 @@ namespace OpenRA.Mods.Common.Traits
public interface IDemolishableInfo : ITraitInfoInterface { bool IsValidTarget(ActorInfo actorInfo, Actor saboteur); }
public interface IDemolishable
{
void Demolish(Actor self, Actor saboteur);
bool IsValidTarget(Actor self, Actor saboteur);
void Demolish(Actor self, Actor saboteur, int delay);
}
// Type tag for crush class bits

View File

@@ -0,0 +1,71 @@
#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;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RemovedDemolishLocking : UpdateRule
{
public override string Name { get { return "Traits are no longer automatically disabled during the Demolition countdown"; } }
public override string Description
{
get
{
return "Traits are no longer force-disabled during the Demolishing trait destruction countdown.\n" +
"This affects the Production*, Transforms, Sellable, EjectOnDeath and ToggleConditionOnOrder traits.\n" +
"Affected actors are listed so that conditions may be manually defined.";
}
}
static readonly string[] Traits =
{
"Production",
"ProductionAirdrop",
"ProductionFromMapEdge",
"ProductionParadrop",
"Transforms",
"Sellable",
"ToggleConditionOnOrder",
"EjectOnDeath"
};
readonly Dictionary<string, List<string>> locations = new Dictionary<string, List<string>>();
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (locations.Any())
yield return "Review the following definitions and, if the actor is Demolishable,\n" +
"define Demolishable.Condition and use this condition to disable them:\n" +
UpdateUtils.FormatMessageList(locations.Select(
kv => kv.Key + ":\n" + UpdateUtils.FormatMessageList(kv.Value)));
locations.Clear();
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
{
var used = new List<string>();
foreach (var t in Traits)
if (actorNode.LastChildMatching(t, includeRemovals: false) != null)
used.Add(t);
if (used.Any())
{
var location = "{0} ({1})".F(actorNode.Key, actorNode.Location.Filename);
locations[location] = used;
}
yield break;
}
}
}

View File

@@ -102,6 +102,7 @@ namespace OpenRA.Mods.Common.UpdateRules
new RemoveRepairBuildingsFromAircraft(),
new AddRearmable(),
new MergeAttackPlaneAndHeli(),
new RemovedDemolishLocking(),
})
};

View File

@@ -13,16 +13,22 @@ World:
NUKE:
-Sellable:
Demolishable:
-Condition:
Buildable:
BuildLimit: 1
PYLE:
-Sellable:
Demolishable:
-Condition:
Buildable:
BuildLimit: 1
FACT:
-Sellable:
Demolishable:
-Condition:
PROC:
Buildable:

View File

@@ -144,6 +144,8 @@ HPAD.IN:
BuildSounds: placbldg.aud, build5.aud
UndeploySounds: cashturn.aud
-Sellable:
Demolishable:
-Condition:
-Power:
ORCA.IN:

View File

@@ -744,7 +744,10 @@
SpawnActorsOnSell:
ActorTypes: e6,e1,e1,e1
EngineerRepairable:
Demolishable:
Condition: being-demolished
Sellable:
RequiresCondition: !being-demolished
SellSounds: cashturn.aud
CaptureManager:
Capturable:

View File

@@ -20,10 +20,11 @@ FACT:
Production:
Produces: Building.GDI, Building.Nod, Defence.GDI, Defence.Nod
Transforms:
RequiresCondition: factundeploy
PauseOnCondition: being-demolished
IntoActor: mcv
Offset: 1,1
Facing: 108
RequiresCondition: factundeploy
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:
Condition: factundeploy
Prerequisites: global-factundeploy

View File

@@ -144,6 +144,8 @@ sietch:
-Sellable:
-Capturable:
-RepairableBuilding:
Demolishable:
-Condition:
ProvidesPrerequisite@buildingname:
-WithMakeAnimation:
-WithCrumbleOverlay:

View File

@@ -409,11 +409,13 @@
ActorLostNotification:
Notification: BuildingLost
ShakeOnDeath:
Demolishable:
Condition: being-demolished
Sellable:
RequiresCondition: !being-demolished
SellSounds: BUILD1.WAV
Guardable:
Range: 3c0
Demolishable:
DamagedByTerrain:
Damage: 500
DamageInterval: 100

View File

@@ -75,6 +75,8 @@ ATEK:
CaptureManager:
-BeingCapturedCondition:
-Sellable:
Demolishable:
-Condition:
PDOX:
Inherits@IDISABLE: ^DisableOnLowPower
@@ -85,6 +87,8 @@ PDOX:
CaptureManager:
-BeingCapturedCondition:
-Sellable:
Demolishable:
-Condition:
IRON:
Buildable:

View File

@@ -170,6 +170,8 @@ TENT:
Production:
Produces: Infantry, Soldier, Dog, Defense
-Sellable:
Demolishable:
-Condition:
BaseProvider:
Range: 12c0
Power:

View File

@@ -39,13 +39,19 @@ AFLD:
DropItems: E1,E1,E1,E2,E2
-RallyPoint:
-Sellable:
Demolishable:
-Condition:
DOME:
CaptureManager:
-BeingCapturedCondition:
-Sellable:
Demolishable:
-Condition:
POWR:
CaptureManager:
-BeingCapturedCondition:
-Sellable:
Demolishable:
-Condition:

View File

@@ -33,6 +33,8 @@ FTUR:
CaptureManager:
-BeingCapturedCondition:
-Sellable:
Demolishable:
-Condition:
PBOX:
-AutoTarget:

View File

@@ -663,8 +663,10 @@
RequiredForShortGame: true
GpsDot:
String: Structure
Demolishable:
Condition: being-demolished
Sellable:
RequiresCondition: !being-captured
RequiresCondition: !being-captured && !being-demolished
SellSounds: cashturn.aud
WithBuildingRepairDecoration:
Image: allyrepair

View File

@@ -1127,13 +1127,13 @@ FACT:
ActorTypes: e1,e1,e1,tecn,tecn,e6
BaseBuilding:
Transforms:
PauseOnCondition: chrono-vortex || being-captured
RequiresCondition: factundeploy
PauseOnCondition: chrono-vortex || being-captured || being-demolished
IntoActor: mcv
Offset: 1,1
Facing: 96
RequiresCondition: factundeploy
Sellable:
RequiresCondition: !chrono-vortex && !being-captured
RequiresCondition: !chrono-vortex && !being-captured && !being-demolished
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:
Condition: factundeploy
Prerequisites: global-factundeploy

View File

@@ -369,7 +369,10 @@
MustBeDestroyed:
RequiredForShortGame: true
CaptureNotification:
Demolishable:
Condition: being-demolished
Sellable:
RequiresCondition: !being-demolished
SellSounds: cashturn.aud
WithMakeAnimation:
ThrowsShrapnel@SMALL:
@@ -478,9 +481,11 @@
TargetTypes: Ground, Wall, C4
WithWallSpriteBody:
Type: wall
Sellable:
SellSounds: cashturn.aud
Demolishable:
Condition: being-demolished
Sellable:
RequiresCondition: !being-demolished
SellSounds: cashturn.aud
ScriptTriggers:
ConditionManager:
Health:

View File

@@ -93,6 +93,8 @@ NAFNCE:
Types: laserfence
-Crushable:
-Sellable:
Demolishable:
-Condition:
-Targetable:
-Building:
EnergyWall:

View File

@@ -28,11 +28,12 @@ GACNST:
Value: 2500
BaseBuilding:
Transforms:
RequiresCondition: factundeploy
PauseOnCondition: being-demolished
IntoActor: mcv
Offset: 1,1
Facing: 96
DeployCursor: undeploy
RequiresCondition: factundeploy
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:
Condition: factundeploy
Prerequisites: global-factundeploy