Skip to content

Commit

Permalink
Add place variant building for BaseBuilderBotModule.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dnqbob committed Mar 12, 2022
1 parent 0203476 commit 218f91d
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 11 deletions.
Expand Up @@ -135,11 +135,13 @@ bool TickQueue(IBot bot, ProductionQueue queue)
// 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 =>
Expand All @@ -159,7 +161,9 @@ bool TickQueue(IBot bot, ProductionQueue queue)
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)
Expand All @@ -184,6 +188,9 @@ bool TickQueue(IBot bot, ProductionQueue queue)
// 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
Expand Down Expand Up @@ -325,8 +332,14 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
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;

Expand Down Expand Up @@ -368,36 +381,86 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
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();
Expand All @@ -411,6 +474,7 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
.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:
Expand All @@ -425,7 +489,7 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
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;
}
}
Expand All @@ -439,7 +503,7 @@ ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
}

// Can't find a build location
return null;
return (null, 0);
}
}
}
3 changes: 3 additions & 0 deletions OpenRA.Mods.Common/Traits/Buildings/PlaceBuildingVariants.cs
Expand Up @@ -21,6 +21,9 @@ public class PlaceBuildingVariantsInfo : TraitInfo<PlaceBuildingVariants>, Requi
[Desc("Variant actors that can be cycled between when placing a structure.")]
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(); }
}

Expand Down

0 comments on commit 218f91d

Please sign in to comment.