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
var type = BuildingType.Building;
CPos? location = null;
var actorVariant = 0;
string orderString = "PlaceBuilding";
// Check if Building is a plug for other Building
var actorInfo = world.Map.Rules.Actors[currentBuilding.Item];
var plugInfo = actorInfo.TraitInfoOrDefault<PlugInfo>();
if (plugInfo != null)
{
var possibleBuilding = world.ActorsWithTrait<Pluggable>().FirstOrDefault(a =>
@@ -159,7 +161,9 @@ namespace OpenRA.Mods.Common.Traits
else if (baseBuilder.Info.RefineryTypes.Contains(actorInfo.Name))
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)
@@ -184,6 +188,9 @@ namespace OpenRA.Mods.Common.Traits
// Building to place
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
ExtraData = queue.Actor.ActorID,
SuppressVisualFeedback = true
@@ -325,8 +332,14 @@ namespace OpenRA.Mods.Common.Traits
if (!buildableThings.Any(b => b.Name == name))
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?
var count = playerBuildings.Count(a => a.Info.Name == name);
if (count * 100 > frac.Value * playerBuildings.Length)
continue;
@@ -368,36 +381,86 @@ namespace OpenRA.Mods.Common.Traits
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 bi = actorInfo.TraitInfoOrDefault<BuildingInfo>();
if (bi == null)
return null;
return (null, 0);
// 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);
// Sort by distance to target if we have one
if (center != target)
{
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
{
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)
{
if (!world.CanPlaceBuilding(cell, actorInfo, bi, null))
if (!world.CanPlaceBuilding(cell, variantActorInfo, vbi, null))
continue;
if (distanceToBaseIsImportant && !bi.IsCloseEnoughToBase(world, player, actorInfo, cell))
if (distanceToBaseIsImportant && !vbi.IsCloseEnoughToBase(world, player, variantActorInfo, cell))
continue;
return cell;
return (cell, actorVariant);
}
return null;
return (null, 0);
};
var baseCenter = baseBuilder.GetRandomBaseCenter();
@@ -411,6 +474,7 @@ namespace OpenRA.Mods.Common.Traits
.ClosestTo(world.Map.CenterOfCell(baseBuilder.DefenseCenter));
var targetCell = closestEnemy != null ? closestEnemy.Location : baseCenter;
return findPos(baseBuilder.DefenseCenter, targetCell, baseBuilder.Info.MinimumDefenseRadius, baseBuilder.Info.MaximumDefenseRadius);
case BuildingType.Refinery:
@@ -425,7 +489,7 @@ namespace OpenRA.Mods.Common.Traits
foreach (var r in nearbyResources)
{
var found = findPos(baseCenter, r, baseBuilder.Info.MinBaseRadius, baseBuilder.Info.MaxBaseRadius);
if (found != null)
if (found.Location != null)
return found;
}
}
@@ -439,7 +503,7 @@ namespace OpenRA.Mods.Common.Traits
}
// Can't find a build location
return null;
return (null, 0);
}
}
}