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 &&
(enterCaptureManager == null || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager)))
(enterCaptureManager == null || !manager.CanTarget(enterCaptureManager)))
Cancel(self, true);
}
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Common.Activities
// Make sure we can still capture the target before entering
// (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);
return false;
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Activities
// StartCapture returns false when a capture delay is enabled
// 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;
if (!captures.Info.ConsumedByCapture)
@@ -80,11 +80,11 @@ namespace OpenRA.Mods.Common.Activities
if (enterActor != targetActor)
return;
if (enterCaptureManager.BeingCaptured || !enterCaptureManager.CanBeTargetedBy(enterActor, self, manager))
if (enterCaptureManager.BeingCaptured || !manager.CanTarget(enterCaptureManager))
return;
// Prioritize capturing over sabotaging
var captures = manager.ValidCapturesWithLowestSabotageThreshold(self, enterActor, enterCaptureManager);
var captures = manager.ValidCapturesWithLowestSabotageThreshold(enterCaptureManager);
if (captures == null)
return;
@@ -134,25 +134,25 @@ namespace OpenRA.Mods.Common.Activities
protected override void OnLastRun(Actor self)
{
CancelCapture(self);
CancelCapture();
base.OnLastRun(self);
}
protected override void OnActorDispose(Actor self)
{
CancelCapture(self);
CancelCapture();
base.OnActorDispose(self);
}
public override void Cancel(Actor self, bool keepQueue = false)
{
CancelCapture(self);
CancelCapture();
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)
{
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)
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())
.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.")]
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.")]
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?")]
public readonly bool PreventsAutoTarget = true;
public override object Create(ActorInitializer init) { return new CaptureManager(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 override object Create(ActorInitializer init) { return new CaptureManager(init.Self, this); }
}
public class CaptureManager : INotifyCreated, INotifyCapture, ITick, IDisableEnemyAutoTarget
{
readonly Actor self;
readonly CaptureManagerInfo info;
IMove move;
ICaptureProgressWatcher[] progressWatchers;
BitSet<CaptureType> allyCapturableTypes;
BitSet<CaptureType> neutralCapturableTypes;
BitSet<CaptureType> enemyCapturableTypes;
BitSet<CaptureType> capturesTypes;
BitSet<CaptureType> allyCapturesTypes;
BitSet<CaptureType> neutralCapturesTypes;
BitSet<CaptureType> enemyCapturesTypes;
BitSet<CaptureType> capturableTypes;
IEnumerable<Capturable> enabledCapturable;
IEnumerable<Captures> enabledCaptures;
@@ -81,8 +70,9 @@ namespace OpenRA.Mods.Common.Traits
public bool BeingCaptured { get; private set; }
public CaptureManager(CaptureManagerInfo info)
public CaptureManager(Actor self, CaptureManagerInfo info)
{
this.self = self;
this.info = info;
}
@@ -103,69 +93,74 @@ namespace OpenRA.Mods.Common.Traits
RefreshCapturable();
}
public void RefreshCapturable()
public void RefreshCaptures()
{
allyCapturableTypes = neutralCapturableTypes = enemyCapturableTypes = default;
foreach (var c in enabledCapturable)
allyCapturesTypes = neutralCapturesTypes = enemyCapturesTypes = default;
foreach (var c in enabledCaptures)
{
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Ally))
allyCapturableTypes = allyCapturableTypes.Union(c.Info.Types);
allyCapturesTypes = allyCapturesTypes.Union(c.Info.CaptureTypes);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Neutral))
neutralCapturableTypes = neutralCapturableTypes.Union(c.Info.Types);
neutralCapturesTypes = neutralCapturesTypes.Union(c.Info.CaptureTypes);
if (c.Info.ValidRelationships.HasRelationship(PlayerRelationship.Enemy))
enemyCapturableTypes = enemyCapturableTypes.Union(c.Info.Types);
enemyCapturesTypes = enemyCapturesTypes.Union(c.Info.CaptureTypes);
}
}
public void RefreshCaptures()
public void RefreshCapturable()
{
capturesTypes = enabledCaptures.Aggregate(
capturableTypes = enabledCapturable.Aggregate(
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);
if (relationship.HasRelationship(PlayerRelationship.Enemy))
return captorManager.capturesTypes.Overlaps(enemyCapturableTypes);
if (relationship.HasRelationship(PlayerRelationship.Neutral))
return captorManager.capturesTypes.Overlaps(neutralCapturableTypes);
if (relationship.HasRelationship(PlayerRelationship.Ally))
return captorManager.capturesTypes.Overlaps(allyCapturableTypes);
return false;
return CanTarget(target.self.Owner, target.capturableTypes);
}
public bool CanBeTargetedBy(Actor self, Actor captor, Captures captures)
/// <summary>Should only be called from the captor CaptureManager.</summary>
public bool CanTarget(FrozenActor target)
{
if (captures.IsTraitDisabled)
if (!target.Info.HasTraitInfo<CaptureManagerInfo>())
return false;
var relationship = captor.Owner.RelationshipWith(self.Owner);
// 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))
return captures.Info.CaptureTypes.Overlaps(enemyCapturableTypes);
return captureTypes.Overlaps(enemyCapturesTypes);
if (relationship.HasRelationship(PlayerRelationship.Neutral))
return captures.Info.CaptureTypes.Overlaps(neutralCapturableTypes);
return captureTypes.Overlaps(neutralCapturesTypes);
if (relationship.HasRelationship(PlayerRelationship.Ally))
return captures.Info.CaptureTypes.Overlaps(allyCapturableTypes);
return captureTypes.Overlaps(allyCapturesTypes);
return false;
}
public Captures ValidCapturesWithLowestSabotageThreshold(Actor self, Actor captee, CaptureManager capteeManager)
public Captures ValidCapturesWithLowestSabotageThreshold(CaptureManager target)
{
if (captee.IsDead)
if (target.self.IsDead)
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))
if (capteeManager.CanBeTargetedBy(captee, self, c))
if (c.Info.ValidRelationships.HasRelationship(relationship) && target.capturableTypes.Overlaps(c.Info.CaptureTypes))
return c;
return null;
@@ -182,7 +177,7 @@ namespace OpenRA.Mods.Common.Traits
/// 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.
/// </summary>
public bool StartCapture(Actor self, Actor target, CaptureManager targetManager, out Captures captures)
public bool StartCapture(CaptureManager targetManager, out Captures captures)
{
captures = null;
@@ -190,10 +185,11 @@ namespace OpenRA.Mods.Common.Traits
if (self.WillDispose)
return false;
var target = targetManager.self;
if (target != currentTarget)
{
if (currentTarget != null)
CancelCapture(self, currentTarget, currentTargetManager);
if (currentTargetManager != null)
CancelCapture(currentTargetManager);
targetManager.currentCaptors.Add(self);
currentTarget = target;
@@ -209,7 +205,7 @@ namespace OpenRA.Mods.Common.Traits
if (targetManager.beingCapturedToken == Actor.InvalidConditionToken)
targetManager.beingCapturedToken = target.GrantCondition(targetManager.info.BeingCapturedCondition);
captures = ValidCapturesWithLowestSabotageThreshold(self, target, targetManager);
captures = ValidCapturesWithLowestSabotageThreshold(targetManager);
if (captures == null)
return false;
@@ -243,11 +239,12 @@ namespace OpenRA.Mods.Common.Traits
/// This method revokes the capturing conditions on the captor and target
/// and resets any capturing progress.
/// </summary>
public void CancelCapture(Actor self, Actor target, CaptureManager targetManager)
public void CancelCapture(CaptureManager targetManager)
{
if (currentTarget == null)
return;
var target = targetManager.self;
foreach (var w in progressWatchers)
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.")]
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.")]
public readonly PlayerRelationship PlayerExperienceRelationships = PlayerRelationship.Enemy;
@@ -69,12 +72,12 @@ namespace OpenRA.Mods.Common.Traits
public class Captures : ConditionalTrait<CapturesInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
readonly CaptureManager captureManager;
public readonly CaptureManager CaptureManager;
public Captures(Actor self, CapturesInfo info)
: base(info)
{
captureManager = self.Trait<CaptureManager>();
CaptureManager = self.Trait<CaptureManager>();
}
public IEnumerable<IOrderTargeter> Orders
@@ -110,8 +113,8 @@ namespace OpenRA.Mods.Common.Traits
self.ShowTargetLines();
}
protected override void TraitEnabled(Actor self) { captureManager.RefreshCaptures(); }
protected override void TraitDisabled(Actor self) { captureManager.RefreshCaptures(); }
protected override void TraitEnabled(Actor self) { CaptureManager.RefreshCaptures(); }
protected override void TraitDisabled(Actor self) { CaptureManager.RefreshCaptures(); }
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)
{
var captureManager = target.TraitOrDefault<CaptureManager>();
if (captureManager == null || !captureManager.CanBeTargetedBy(target, self, captures))
var targetManager = target.TraitOrDefault<CaptureManager>();
if (targetManager == null || !captures.CaptureManager.CanTarget(targetManager))
{
cursor = captures.Info.EnterBlockedCursor;
return false;
@@ -147,8 +150,7 @@ namespace OpenRA.Mods.Common.Traits
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var captureManagerInfo = target.Info.TraitInfoOrDefault<CaptureManagerInfo>();
if (captureManagerInfo == null || !captureManagerInfo.CanBeTargetedBy(target, self, captures))
if (!captures.CaptureManager.CanTarget(target))
{
cursor = captures.Info.EnterBlockedCursor;
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[]
{
// bleed only changes here.
new RemoveValidRelationsFromCapturable(),
// Execute these rules last to avoid premature yaml merge crashes.
new AbstractDocking(),

View File

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

View File

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

View File

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

View File

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