Improve Replaceable logic:

- Remove BuildingInfluence checks
- Support multiple Replaceable/Replacement traits on the same actors
- Fix description typos
This commit is contained in:
Paul Chote
2020-10-08 22:37:12 +01:00
committed by abcdefg30
parent 8aeec24c9b
commit daa8c74c37
4 changed files with 54 additions and 25 deletions

View File

@@ -22,22 +22,52 @@ namespace OpenRA.Mods.Common.Traits
if (!world.Map.Contains(cell))
return false;
var building = world.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(cell);
if (building != null)
if (!bi.AllowInvalidPlacement)
{
if (ai == null)
return false;
// Replaceable actors are rare, so avoid initializing state unless we have to
var checkReplacements = ai != null && ai.HasTraitInfo<ReplacementInfo>();
HashSet<string> acceptedReplacements = null;
var replacementInfo = ai.TraitInfoOrDefault<ReplacementInfo>();
if (replacementInfo == null)
return false;
var foundActors = false;
foreach (var a in world.ActorMap.GetActorsAt(cell))
{
if (a == toIgnore)
continue;
if (!building.TraitsImplementing<Replaceable>().Any(p => !p.IsTraitDisabled &&
p.Info.Types.Overlaps(replacementInfo.ReplaceableTypes)))
return false;
// If this is potentially a replacement actor we must check *all* cell occupants
// before we know the placement is invalid
// Otherwise, we can bail immediately
if (!checkReplacements)
return false;
foundActors = true;
foreach (var r in a.TraitsImplementing<Replaceable>())
{
if (r.IsTraitDisabled)
continue;
if (acceptedReplacements == null)
acceptedReplacements = new HashSet<string>();
acceptedReplacements.UnionWith(r.Info.Types);
}
}
// Replacements are enabled and the cell contained at least one (not ignored) actor
if (foundActors)
{
// The cell contains at least one actor, and none were replaceable
if (acceptedReplacements == null)
return false;
// The cell contains at least one replaceable actor, but not of the types we accept
var foundReplacement = ai.TraitInfos<ReplacementInfo>()
.Any(r => r.ReplaceableTypes.Overlaps(acceptedReplacements));
if (!foundReplacement)
return false;
}
}
else if (!bi.AllowInvalidPlacement && world.ActorMap.GetActorsAt(cell).Any(a => a != toIgnore))
return false;
// Buildings can never be placed on ramps
return world.Map.Ramp[cell] == 0 && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type);

View File

@@ -163,16 +163,15 @@ namespace OpenRA.Mods.Common.Traits
|| !buildingInfo.IsCloseEnoughToBase(self.World, order.Player, actorInfo, targetLocation))
return;
var replacementInfo = actorInfo.TraitInfoOrDefault<ReplacementInfo>();
if (replacementInfo != null)
{
var buildingInfluence = self.World.WorldActor.Trait<BuildingInfluence>();
var replaceableTypes = actorInfo.TraitInfos<ReplacementInfo>()
.SelectMany(r => r.ReplaceableTypes)
.ToHashSet();
if (replaceableTypes.Any())
foreach (var t in buildingInfo.Tiles(targetLocation))
{
var host = buildingInfluence.GetBuildingAt(t);
host?.World.Remove(host);
}
}
foreach (var a in self.World.ActorMap.GetActorsAt(t))
if (a.TraitsImplementing<Replaceable>().Any(r => !r.IsTraitDisabled && r.Info.Types.Overlaps(replaceableTypes)))
self.World.Remove(a);
var building = w.CreateActor(actorInfo.Name, new TypeDictionary
{

View File

@@ -16,15 +16,15 @@ namespace OpenRA.Mods.Common.Traits
public class ReplaceableInfo : ConditionalTraitInfo
{
[FieldLoader.Require]
[Desc("Replacement types this Relpaceable actor accepts.")]
[Desc("Replacement types this Replaceable actor accepts.")]
public readonly HashSet<string> Types = new HashSet<string>();
public override object Create(ActorInitializer init) { return new Replaceable(init, this); }
public override object Create(ActorInitializer init) { return new Replaceable(this); }
}
public class Replaceable : ConditionalTrait<ReplaceableInfo>
{
public Replaceable(ActorInitializer init, ReplaceableInfo info)
public Replaceable(ReplaceableInfo info)
: base(info) { }
}
}

View File

@@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits
public class ReplacementInfo : TraitInfo<Replacement>
{
[FieldLoader.Require]
[Desc("Replacement type (matched against Conditions in Replaceable).")]
[Desc("Replacement type (matched against Types in Replaceable).")]
public readonly HashSet<string> ReplaceableTypes = new HashSet<string>();
}