Move ValidRelations from Capturable to Captures

To better match weapon definitions
This commit is contained in:
Gustas
2023-09-05 17:55:14 +03:00
committed by Matthias Mailänder
parent 161f4cbdff
commit 5cc59ae3ac
12 changed files with 125 additions and 81 deletions

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Activities
} }
if (!targetIsDeadOrHiddenActor && target.Type != TargetType.FrozenActor && if (!targetIsDeadOrHiddenActor && target.Type != TargetType.FrozenActor &&
(enterCaptureManager == null || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager))) (enterCaptureManager == null || !manager.CanTarget(enterCaptureManager)))
Cancel(self, true); Cancel(self, true);
} }
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Common.Activities
// Make sure we can still capture the target before entering // Make sure we can still capture the target before entering
// (but not before, because this may stop the actor in the middle of nowhere) // (but not before, because this may stop the actor in the middle of nowhere)
if (enterCaptureManager == null || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager)) if (enterCaptureManager == null || !manager.CanTarget(enterCaptureManager))
{ {
Cancel(self, true); Cancel(self, true);
return false; return false;
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Activities
// StartCapture returns false when a capture delay is enabled // StartCapture returns false when a capture delay is enabled
// We wait until it returns true before allowing entering the target // We wait until it returns true before allowing entering the target
if (!manager.StartCapture(self, enterActor, enterCaptureManager, out var captures)) if (!manager.StartCapture(enterCaptureManager, out var captures))
return false; return false;
if (!captures.Info.ConsumedByCapture) if (!captures.Info.ConsumedByCapture)
@@ -80,11 +80,11 @@ namespace OpenRA.Mods.Common.Activities
if (enterActor != targetActor) if (enterActor != targetActor)
return; return;
if (enterCaptureManager.BeingCaptured || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager)) if (enterCaptureManager.BeingCaptured || !manager.CanTarget(enterCaptureManager))
return; return;
// Prioritize capturing over sabotaging // Prioritize capturing over sabotaging
var captures = manager.ValidCapturesWithLowestSabotageThreshold(self, enterActor, enterCaptureManager); var captures = manager.ValidCapturesWithLowestSabotageThreshold(enterCaptureManager);
if (captures == null) if (captures == null)
return; return;
@@ -134,25 +134,25 @@ namespace OpenRA.Mods.Common.Activities
protected override void OnLastRun(Actor self) protected override void OnLastRun(Actor self)
{ {
CancelCapture(self); CancelCapture();
base.OnLastRun(self); base.OnLastRun(self);
} }
protected override void OnActorDispose(Actor self) protected override void OnActorDispose(Actor self)
{ {
CancelCapture(self); CancelCapture();
base.OnActorDispose(self); base.OnActorDispose(self);
} }
public override void Cancel(Actor self, bool keepQueue = false) public override void Cancel(Actor self, bool keepQueue = false)
{ {
CancelCapture(self); CancelCapture();
base.Cancel(self, keepQueue); base.Cancel(self, keepQueue);
} }
void CancelCapture(Actor self) void CancelCapture()
{ {
manager.CancelCapture(self, enterActor, enterCaptureManager); manager.CancelCapture(enterCaptureManager);
} }
} }
} }

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Scripting
public bool CanCapture(Actor target) public bool CanCapture(Actor target)
{ {
var targetManager = target.TraitOrDefault<CaptureManager>(); var targetManager = target.TraitOrDefault<CaptureManager>();
return targetManager != null && targetManager.CanBeTargetedBy(target, Self, captureManager); return targetManager != null && captureManager.CanTarget(targetManager);
} }
} }
} }

View File

@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Traits
if (captureManager == null) if (captureManager == null)
return false; return false;
return capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait)); return capturers.Any(tp => tp.Trait.CanTarget(captureManager));
}) })
.OrderByDescending(target => target.GetSellValue()) .OrderByDescending(target => target.GetSellValue())
.Take(maximumCaptureTargetOptions); .Take(maximumCaptureTargetOptions);

View File

@@ -22,9 +22,6 @@ namespace OpenRA.Mods.Common.Traits
[Desc("CaptureTypes (from the Captures trait) that are able to capture this.")] [Desc("CaptureTypes (from the Captures trait) that are able to capture this.")]
public readonly BitSet<CaptureType> Types = default; public readonly BitSet<CaptureType> Types = default;
[Desc("What player relationships the target's owner needs to be captured by this actor.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Cancel the actor's current activity when getting captured.")] [Desc("Cancel the actor's current activity when getting captured.")]
public readonly bool CancelActivity = false; public readonly bool CancelActivity = false;

View File

@@ -38,32 +38,21 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Should units friendly to the capturing actor auto-target this actor while it is being captured?")] [Desc("Should units friendly to the capturing actor auto-target this actor while it is being captured?")]
public readonly bool PreventsAutoTarget = true; public readonly bool PreventsAutoTarget = true;
public override object Create(ActorInitializer init) { return new CaptureManager(this); } public override object Create(ActorInitializer init) { return new CaptureManager(init.Self, this); }
public bool CanBeTargetedBy(FrozenActor frozenActor, Actor captor, Captures captures)
{
if (captures.IsTraitDisabled)
return false;
// TODO: FrozenActors don't yet have a way of caching conditions, so we can't filter disabled traits
// This therefore assumes that all Capturable traits are enabled, which is probably wrong.
// Actors with FrozenUnderFog should therefore not disable the Capturable trait.
var stance = captor.Owner.RelationshipWith(frozenActor.Owner);
return frozenActor.Info.TraitInfos<CapturableInfo>()
.Any(c => c.ValidRelationships.HasRelationship(stance) && captures.Info.CaptureTypes.Overlaps(c.Types));
}
} }
public class CaptureManager : INotifyCreated, INotifyCapture, ITick, IDisableEnemyAutoTarget public class CaptureManager : INotifyCreated, INotifyCapture, ITick, IDisableEnemyAutoTarget
{ {
readonly Actor self;
readonly CaptureManagerInfo info; readonly CaptureManagerInfo info;
IMove move; IMove move;
ICaptureProgressWatcher[] progressWatchers; ICaptureProgressWatcher[] progressWatchers;
BitSet<CaptureType> allyCapturableTypes; BitSet<CaptureType> allyCapturesTypes;
BitSet<CaptureType> neutralCapturableTypes; BitSet<CaptureType> neutralCapturesTypes;
BitSet<CaptureType> enemyCapturableTypes; BitSet<CaptureType> enemyCapturesTypes;
BitSet<CaptureType> capturesTypes; BitSet<CaptureType> capturableTypes;
IEnumerable<Capturable> enabledCapturable; IEnumerable<Capturable> enabledCapturable;
IEnumerable<Captures> enabledCaptures; IEnumerable<Captures> enabledCaptures;
@@ -81,8 +70,9 @@ namespace OpenRA.Mods.Common.Traits
public bool BeingCaptured { get; private set; } public bool BeingCaptured { get; private set; }
public CaptureManager(CaptureManagerInfo info) public CaptureManager(Actor self, CaptureManagerInfo info)
{ {
this.self = self;
this.info = info; this.info = info;
} }
@@ -103,69 +93,74 @@ namespace OpenRA.Mods.Common.Traits
RefreshCapturable(); RefreshCapturable();
} }
public void RefreshCapturable()
{
allyCapturableTypes = neutralCapturableTypes = enemyCapturableTypes = default;
foreach (var c in enabledCapturable)
{
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Ally))
allyCapturableTypes = allyCapturableTypes.Union(c.Info.Types);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Neutral))
neutralCapturableTypes = neutralCapturableTypes.Union(c.Info.Types);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Enemy))
enemyCapturableTypes = enemyCapturableTypes.Union(c.Info.Types);
}
}
public void RefreshCaptures() public void RefreshCaptures()
{ {
capturesTypes = enabledCaptures.Aggregate( allyCapturesTypes = neutralCapturesTypes = enemyCapturesTypes = default;
foreach (var c in enabledCaptures)
{
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Ally))
allyCapturesTypes = allyCapturesTypes.Union(c.Info.CaptureTypes);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Neutral))
neutralCapturesTypes = neutralCapturesTypes.Union(c.Info.CaptureTypes);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Enemy))
enemyCapturesTypes = enemyCapturesTypes.Union(c.Info.CaptureTypes);
}
}
public void RefreshCapturable()
{
capturableTypes = enabledCapturable.Aggregate(
default(BitSet<CaptureType>), default(BitSet<CaptureType>),
(a, b) => a.Union(b.Info.CaptureTypes)); (a, b) => a.Union(b.Info.Types));
} }
public bool CanBeTargetedBy(Actor self, Actor captor, CaptureManager captorManager) /// <summary>Should only be called from the captor's CaptureManager.</summary>
public bool CanTarget(CaptureManager target)
{ {
var relationship = captor.Owner.RelationshipWith(self.Owner); return CanTarget(target.self.Owner, target.capturableTypes);
}
/// <summary>Should only be called from the captor CaptureManager.</summary>
public bool CanTarget(FrozenActor target)
{
if (!target.Info.HasTraitInfo<CaptureManagerInfo>())
return false;
// TODO: FrozenActors don't yet have a way of caching conditions, so we can't filter disabled traits
// This therefore assumes that all Capturable traits are enabled, which is probably wrong.
// Actors with FrozenUnderFog should therefore not disable the Capturable trait.
var targetTypes = target.Info.TraitInfos<CapturableInfo>().Aggregate(
default(BitSet<CaptureType>),
(a, b) => a.Union(b.Types));
return CanTarget(target.Owner, targetTypes);
}
bool CanTarget(Player target, BitSet<CaptureType> captureTypes)
{
var relationship = self.Owner.RelationshipWith(target);
if (relationship.HasRelationship(PlayerRelationship.Enemy)) if (relationship.HasRelationship(PlayerRelationship.Enemy))
return captorManager.capturesTypes.Overlaps(enemyCapturableTypes); return captureTypes.Overlaps(enemyCapturesTypes);
if (relationship.HasRelationship(PlayerRelationship.Neutral)) if (relationship.HasRelationship(PlayerRelationship.Neutral))
return captorManager.capturesTypes.Overlaps(neutralCapturableTypes); return captureTypes.Overlaps(neutralCapturesTypes);
if (relationship.HasRelationship(PlayerRelationship.Ally)) if (relationship.HasRelationship(PlayerRelationship.Ally))
return captorManager.capturesTypes.Overlaps(allyCapturableTypes); return captureTypes.Overlaps(allyCapturesTypes);
return false; return false;
} }
public bool CanBeTargetedBy(Actor self, Actor captor, Captures captures) public Captures ValidCapturesWithLowestSabotageThreshold(CaptureManager target)
{ {
if (captures.IsTraitDisabled) if (target.self.IsDead)
return false;
var relationship = captor.Owner.RelationshipWith(self.Owner);
if (relationship.HasRelationship(PlayerRelationship.Enemy))
return captures.Info.CaptureTypes.Overlaps(enemyCapturableTypes);
if (relationship.HasRelationship(PlayerRelationship.Neutral))
return captures.Info.CaptureTypes.Overlaps(neutralCapturableTypes);
if (relationship.HasRelationship(PlayerRelationship.Ally))
return captures.Info.CaptureTypes.Overlaps(allyCapturableTypes);
return false;
}
public Captures ValidCapturesWithLowestSabotageThreshold(Actor self, Actor captee, CaptureManager capteeManager)
{
if (captee.IsDead)
return null; return null;
var relationship = self.Owner.RelationshipWith(target.self.Owner);
foreach (var c in enabledCaptures.OrderBy(c => c.Info.SabotageThreshold).ThenBy(c => c.Info.CaptureDelay)) foreach (var c in enabledCaptures.OrderBy(c => c.Info.SabotageThreshold).ThenBy(c => c.Info.CaptureDelay))
if (capteeManager.CanBeTargetedBy(captee, self, c)) if (c.Info.ValidRelationships.HasRelationship(relationship) && target.capturableTypes.Overlaps(c.Info.CaptureTypes))
return c; return c;
return null; return null;
@@ -182,7 +177,7 @@ namespace OpenRA.Mods.Common.Traits
/// This method grants the capturing conditions on the captor and target and returns /// This method grants the capturing conditions on the captor and target and returns
/// true if the captor is able to start entering or false if it needs to wait. /// true if the captor is able to start entering or false if it needs to wait.
/// </summary> /// </summary>
public bool StartCapture(Actor self, Actor target, CaptureManager targetManager, out Captures captures) public bool StartCapture(CaptureManager targetManager, out Captures captures)
{ {
captures = null; captures = null;
@@ -190,10 +185,11 @@ namespace OpenRA.Mods.Common.Traits
if (self.WillDispose) if (self.WillDispose)
return false; return false;
var target = targetManager.self;
if (target != currentTarget) if (target != currentTarget)
{ {
if (currentTarget != null) if (currentTargetManager != null)
CancelCapture(self, currentTarget, currentTargetManager); CancelCapture(currentTargetManager);
targetManager.currentCaptors.Add(self); targetManager.currentCaptors.Add(self);
currentTarget = target; currentTarget = target;
@@ -209,7 +205,7 @@ namespace OpenRA.Mods.Common.Traits
if (targetManager.beingCapturedToken == Actor.InvalidConditionToken) if (targetManager.beingCapturedToken == Actor.InvalidConditionToken)
targetManager.beingCapturedToken = target.GrantCondition(targetManager.info.BeingCapturedCondition); targetManager.beingCapturedToken = target.GrantCondition(targetManager.info.BeingCapturedCondition);
captures = ValidCapturesWithLowestSabotageThreshold(self, target, targetManager); captures = ValidCapturesWithLowestSabotageThreshold(targetManager);
if (captures == null) if (captures == null)
return false; return false;
@@ -243,11 +239,12 @@ namespace OpenRA.Mods.Common.Traits
/// This method revokes the capturing conditions on the captor and target /// This method revokes the capturing conditions on the captor and target
/// and resets any capturing progress. /// and resets any capturing progress.
/// </summary> /// </summary>
public void CancelCapture(Actor self, Actor target, CaptureManager targetManager) public void CancelCapture(CaptureManager targetManager)
{ {
if (currentTarget == null) if (currentTarget == null)
return; return;
var target = targetManager.self;
foreach (var w in progressWatchers) foreach (var w in progressWatchers)
w.Update(self, self, target, 0, 0); w.Update(self, self, target, 0, 0);

View File

@@ -43,6 +43,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Experience granted to the capturing player.")] [Desc("Experience granted to the capturing player.")]
public readonly int PlayerExperience = 0; public readonly int PlayerExperience = 0;
[Desc("What player relationships the target's owner needs to be captured by this actor.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Neutral | PlayerRelationship.Enemy;
[Desc("Relationships that the structure's previous owner needs to have for the capturing player to receive Experience.")] [Desc("Relationships that the structure's previous owner needs to have for the capturing player to receive Experience.")]
public readonly PlayerRelationship PlayerExperienceRelationships = PlayerRelationship.Enemy; public readonly PlayerRelationship PlayerExperienceRelationships = PlayerRelationship.Enemy;
@@ -69,12 +72,12 @@ namespace OpenRA.Mods.Common.Traits
public class Captures : ConditionalTrait<CapturesInfo>, IIssueOrder, IResolveOrder, IOrderVoice public class Captures : ConditionalTrait<CapturesInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{ {
readonly CaptureManager captureManager; public readonly CaptureManager CaptureManager;
public Captures(Actor self, CapturesInfo info) public Captures(Actor self, CapturesInfo info)
: base(info) : base(info)
{ {
captureManager = self.Trait<CaptureManager>(); CaptureManager = self.Trait<CaptureManager>();
} }
public IEnumerable<IOrderTargeter> Orders public IEnumerable<IOrderTargeter> Orders
@@ -110,8 +113,8 @@ namespace OpenRA.Mods.Common.Traits
self.ShowTargetLines(); self.ShowTargetLines();
} }
protected override void TraitEnabled(Actor self) { captureManager.RefreshCaptures(); } protected override void TraitEnabled(Actor self) { CaptureManager.RefreshCaptures(); }
protected override void TraitDisabled(Actor self) { captureManager.RefreshCaptures(); } protected override void TraitDisabled(Actor self) { CaptureManager.RefreshCaptures(); }
sealed class CaptureOrderTargeter : UnitOrderTargeter sealed class CaptureOrderTargeter : UnitOrderTargeter
{ {
@@ -125,8 +128,8 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{ {
var captureManager = target.TraitOrDefault<CaptureManager>(); var targetManager = target.TraitOrDefault<CaptureManager>();
if (captureManager == null || !captureManager.CanBeTargetedBy(target, self, captures)) if (targetManager == null || !captures.CaptureManager.CanTarget(targetManager))
{ {
cursor = captures.Info.EnterBlockedCursor; cursor = captures.Info.EnterBlockedCursor;
return false; return false;
@@ -147,8 +150,7 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{ {
var captureManagerInfo = target.Info.TraitInfoOrDefault<CaptureManagerInfo>(); if (!captures.CaptureManager.CanTarget(target))
if (captureManagerInfo == null || !captureManagerInfo.CanBeTargetedBy(target, self, captures))
{ {
cursor = captures.Info.EnterBlockedCursor; cursor = captures.Info.EnterBlockedCursor;
return false; return false;

View File

@@ -0,0 +1,46 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 RemoveValidRelationsFromCapturable : UpdateRule
{
public override string Name => "Remove ValidRelations property from Capturable.";
public override string Description => "ValidRelations has been moved from Capturable to Captures to match weapon definitions.";
readonly List<string> locations = new();
public override IEnumerable<string> AfterUpdate(ModData modData)
{
if (locations.Any())
yield return Description + "\n" +
"ValidRelations have been removed from:\n" +
UpdateUtils.FormatMessageList(locations);
locations.Clear();
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
{
foreach (var capturable in actorNode.ChildrenMatching("Capturable"))
{
if (capturable.RemoveNodes("ValidRelations") > 0)
locations.Add($"{actorNode.Key}: {capturable.Key} ({actorNode.Location.Filename})");
}
yield break;
}
}
}

View File

@@ -108,6 +108,7 @@ namespace OpenRA.Mods.Common.UpdateRules
new UpdatePath("playtest-20230801", new UpdateRule[] new UpdatePath("playtest-20230801", new UpdateRule[]
{ {
// bleed only changes here. // bleed only changes here.
new RemoveValidRelationsFromCapturable(),
// Execute these rules last to avoid premature yaml merge crashes. // Execute these rules last to avoid premature yaml merge crashes.
new AbstractDocking(), new AbstractDocking(),

View File

@@ -1076,7 +1076,6 @@
CaptureManager: CaptureManager:
Capturable: Capturable:
Types: husk Types: husk
ValidRelationships: Enemy, Neutral, Ally
TransformOnCapture: TransformOnCapture:
ForceHealthPercentage: 25 ForceHealthPercentage: 25
Tooltip: Tooltip:

View File

@@ -213,8 +213,12 @@ E6:
SabotageThreshold: 55 SabotageThreshold: 55
PlayerExperience: 10 PlayerExperience: 10
Captures@CAPTURES: Captures@CAPTURES:
CaptureTypes: building, husk CaptureTypes: building
PlayerExperience: 10 PlayerExperience: 10
Captures@CATURESHUSK:
CaptureTypes: husk
PlayerExperience: 10
ValidRelationships: Enemy, Neutral, Ally
-AttackFrontal: -AttackFrontal:
RMBO: RMBO:

View File

@@ -25,7 +25,6 @@ MISS:
Name: Soviet Air Force HQ Name: Soviet Air Force HQ
Capturable: Capturable:
Types: building Types: building
ValidRelationships: Enemy
CaptureManager: CaptureManager:
TENT: TENT:

View File

@@ -1062,7 +1062,6 @@
CaptureManager: CaptureManager:
Capturable: Capturable:
Types: husk Types: husk
ValidRelationships: Enemy, Neutral
TransformOnCapture: TransformOnCapture:
ForceHealthPercentage: 15 ForceHealthPercentage: 15
InfiltrateForTransform: InfiltrateForTransform: