Add place variant building for BaseBuilderBotModule.

1. If it follow the refinery placing logic, then we can use Facings in PlaceBuildingVariants to help BaseBuilderBotModule "rotates" it to minefield.

2. If it is a normal building, BaseBuilderBotModule will place a random variant actor.
This commit is contained in:
dnqbob
2022-03-02 17:39:34 +08:00
committed by Matthias Mailänder
parent f74d1c3cf8
commit 4f43b157a8
2 changed files with 78 additions and 11 deletions

View File

@@ -135,11 +135,13 @@ namespace OpenRA.Mods.Common.Traits
// TODO: Derive this from BuildingCommonNames instead // TODO: Derive this from BuildingCommonNames instead
var type = BuildingType.Building; var type = BuildingType.Building;
CPos? location = null; CPos? location = null;
var actorVariant = 0;
string orderString = "PlaceBuilding"; string orderString = "PlaceBuilding";
// Check if Building is a plug for other Building // Check if Building is a plug for other Building
var actorInfo = world.Map.Rules.Actors[currentBuilding.Item]; var actorInfo = world.Map.Rules.Actors[currentBuilding.Item];
var plugInfo = actorInfo.TraitInfoOrDefault<PlugInfo>(); var plugInfo = actorInfo.TraitInfoOrDefault<PlugInfo>();
if (plugInfo != null) if (plugInfo != null)
{ {
var possibleBuilding = world.ActorsWithTrait<Pluggable>().FirstOrDefault(a => var possibleBuilding = world.ActorsWithTrait<Pluggable>().FirstOrDefault(a =>
@@ -159,7 +161,9 @@ namespace OpenRA.Mods.Common.Traits
else if (baseBuilder.Info.RefineryTypes.Contains(actorInfo.Name)) else if (baseBuilder.Info.RefineryTypes.Contains(actorInfo.Name))
type = BuildingType.Refinery; type = BuildingType.Refinery;
location = ChooseBuildLocation(currentBuilding.Item, true, type); var pack = ChooseBuildLocation(currentBuilding.Item, true, type);
location = pack.Location;
actorVariant = pack.Variant;
} }
if (location == null) if (location == null)
@@ -184,6 +188,9 @@ namespace OpenRA.Mods.Common.Traits
// Building to place // Building to place
TargetString = currentBuilding.Item, TargetString = currentBuilding.Item,
// Actor variant will always be small enough to safely pack in a CPos
ExtraLocation = new CPos(actorVariant, 0),
// Actor ID to associate the placement with // Actor ID to associate the placement with
ExtraData = queue.Actor.ActorID, ExtraData = queue.Actor.ActorID,
SuppressVisualFeedback = true SuppressVisualFeedback = true
@@ -325,8 +332,14 @@ namespace OpenRA.Mods.Common.Traits
if (!buildableThings.Any(b => b.Name == name)) if (!buildableThings.Any(b => b.Name == name))
continue; continue;
// Check the number of this structure and its variants
var actorInfo = world.Map.Rules.Actors[name];
var buildingVariantInfo = actorInfo.TraitInfoOrDefault<PlaceBuildingVariantsInfo>();
var variants = buildingVariantInfo?.Actors ?? Array.Empty<string>();
var count = playerBuildings.Count(a => a.Info.Name == name || variants.Contains(a.Info.Name));
// Do we want to build this structure? // Do we want to build this structure?
var count = playerBuildings.Count(a => a.Info.Name == name);
if (count * 100 > frac.Value * playerBuildings.Length) if (count * 100 > frac.Value * playerBuildings.Length)
continue; continue;
@@ -368,36 +381,86 @@ namespace OpenRA.Mods.Common.Traits
return null; return null;
} }
CPos? ChooseBuildLocation(string actorType, bool distanceToBaseIsImportant, BuildingType type) (CPos? Location, int Variant) ChooseBuildLocation(string actorType, bool distanceToBaseIsImportant, BuildingType type)
{ {
var actorInfo = world.Map.Rules.Actors[actorType]; var actorInfo = world.Map.Rules.Actors[actorType];
var bi = actorInfo.TraitInfoOrDefault<BuildingInfo>(); var bi = actorInfo.TraitInfoOrDefault<BuildingInfo>();
if (bi == null) if (bi == null)
return null; return (null, 0);
// Find the buildable cell that is closest to pos and centered around center // Find the buildable cell that is closest to pos and centered around center
Func<CPos, CPos, int, int, CPos?> findPos = (center, target, minRange, maxRange) => Func<CPos, CPos, int, int, (CPos? Location, int Variant)> findPos = (center, target, minRange, maxRange) =>
{ {
var actorVariant = 0;
var buildingVariantInfo = actorInfo.TraitInfoOrDefault<PlaceBuildingVariantsInfo>();
var variantActorInfo = actorInfo;
var vbi = bi;
var cells = world.Map.FindTilesInAnnulus(center, minRange, maxRange); var cells = world.Map.FindTilesInAnnulus(center, minRange, maxRange);
// Sort by distance to target if we have one // Sort by distance to target if we have one
if (center != target) if (center != target)
{
cells = cells.OrderBy(c => (c - target).LengthSquared); cells = cells.OrderBy(c => (c - target).LengthSquared);
// Rotate building if we have a Facings in buildingVariantInfo.
// If we don't have Facings in buildingVariantInfo, use a random variant
if (buildingVariantInfo?.Actors != null)
{
if (buildingVariantInfo.Facings != null)
{
var vector = world.Map.CenterOfCell(target) - world.Map.CenterOfCell(center);
// The rotation Y point to upside vertically, so -Y = Y(rotation)
var desireFacing = new WAngle(WAngle.ArcSin((int)((long)Math.Abs(vector.X) * 1024 / vector.Length)).Angle);
if (vector.X > 0 && vector.Y >= 0)
desireFacing = new WAngle(512) - desireFacing;
else if (vector.X < 0 && vector.Y >= 0)
desireFacing = new WAngle(512) + desireFacing;
else if (vector.X < 0 && vector.Y < 0)
desireFacing = -desireFacing;
for (int i = 0, e = 1024; i < buildingVariantInfo.Facings.Length; i++)
{
var minDelta = Math.Min((desireFacing - buildingVariantInfo.Facings[i]).Angle, (buildingVariantInfo.Facings[i] - desireFacing).Angle);
if (e > minDelta)
{
e = minDelta;
actorVariant = i;
}
}
}
else
actorVariant = world.LocalRandom.Next(buildingVariantInfo.Actors.Length + 1);
}
}
else else
{
cells = cells.Shuffle(world.LocalRandom); cells = cells.Shuffle(world.LocalRandom);
if (buildingVariantInfo?.Actors != null)
actorVariant = world.LocalRandom.Next(buildingVariantInfo.Actors.Length + 1);
}
if (actorVariant != 0)
{
variantActorInfo = world.Map.Rules.Actors[buildingVariantInfo.Actors[actorVariant - 1]];
vbi = variantActorInfo.TraitInfoOrDefault<BuildingInfo>();
}
foreach (var cell in cells) foreach (var cell in cells)
{ {
if (!world.CanPlaceBuilding(cell, actorInfo, bi, null)) if (!world.CanPlaceBuilding(cell, variantActorInfo, vbi, null))
continue; continue;
if (distanceToBaseIsImportant && !bi.IsCloseEnoughToBase(world, player, actorInfo, cell)) if (distanceToBaseIsImportant && !vbi.IsCloseEnoughToBase(world, player, variantActorInfo, cell))
continue; continue;
return cell; return (cell, actorVariant);
} }
return null; return (null, 0);
}; };
var baseCenter = baseBuilder.GetRandomBaseCenter(); var baseCenter = baseBuilder.GetRandomBaseCenter();
@@ -411,6 +474,7 @@ namespace OpenRA.Mods.Common.Traits
.ClosestTo(world.Map.CenterOfCell(baseBuilder.DefenseCenter)); .ClosestTo(world.Map.CenterOfCell(baseBuilder.DefenseCenter));
var targetCell = closestEnemy != null ? closestEnemy.Location : baseCenter; var targetCell = closestEnemy != null ? closestEnemy.Location : baseCenter;
return findPos(baseBuilder.DefenseCenter, targetCell, baseBuilder.Info.MinimumDefenseRadius, baseBuilder.Info.MaximumDefenseRadius); return findPos(baseBuilder.DefenseCenter, targetCell, baseBuilder.Info.MinimumDefenseRadius, baseBuilder.Info.MaximumDefenseRadius);
case BuildingType.Refinery: case BuildingType.Refinery:
@@ -425,7 +489,7 @@ namespace OpenRA.Mods.Common.Traits
foreach (var r in nearbyResources) foreach (var r in nearbyResources)
{ {
var found = findPos(baseCenter, r, baseBuilder.Info.MinBaseRadius, baseBuilder.Info.MaxBaseRadius); var found = findPos(baseCenter, r, baseBuilder.Info.MinBaseRadius, baseBuilder.Info.MaxBaseRadius);
if (found != null) if (found.Location != null)
return found; return found;
} }
} }
@@ -439,7 +503,7 @@ namespace OpenRA.Mods.Common.Traits
} }
// Can't find a build location // Can't find a build location
return null; return (null, 0);
} }
} }
} }

View File

@@ -21,6 +21,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Variant actors that can be cycled between when placing a structure.")] [Desc("Variant actors that can be cycled between when placing a structure.")]
public readonly string[] Actors = null; public readonly string[] Actors = null;
[Desc("Facing of the non-variant actor, followed by facings for each variant actor. The length equals the length of Actors + 1.")]
public readonly WAngle[] Facings = null;
public override object Create(ActorInitializer init) { return new PlaceBuildingVariants(); } public override object Create(ActorInitializer init) { return new PlaceBuildingVariants(); }
} }