using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using DM;
using Landfall.TABS;
using Landfall.TABS.Budget;
using Landfall.TABS.GameMode;
using Landfall.TABS.UnitPlacement;
using Landfall.TABS.WinConditions;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("RandomEnemyGenerator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RandomEnemyGenerator")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3737bb01-abc0-4c64-9e0c-2cf744d6c985")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RandomEnemyGenerator;
public static class ArmyGenerator
{
private sealed class LayoutEntry
{
public UnitBlueprint Blueprint;
public int Cost;
public float Width;
public float Depth;
public bool IsRanged;
public bool IsLarge;
}
private sealed class PlacementArea
{
public Vector3 Center;
public Vector3 Forward;
public Vector3 Side;
public float FrontF;
public float BackF;
public float MinS;
public float MaxS;
public float Y;
}
public static int Budget = 5000;
public static string BudgetText = "5000";
public static float UnitPadding = 0.8f;
public static float PositionJitter = 0.4f;
public static float FacingVariation = 8f;
public static float SpawnHeightLift = 1.1f;
public static bool ClearBeforeGenerate = true;
public static bool IgnoreOfficialBudgetCap = true;
public static bool RaiseOfficialBlueBudget = true;
public static bool IncludeModdedCustomUnits = false;
public static bool IncludeWipUnits = false;
private static readonly Random Rng = new Random();
public static int LastPlacedCount { get; private set; }
public static int LastSpent { get; private set; }
public static string LastMessage { get; private set; } = "Ready.";
public static void SetStatus(string message)
{
LastMessage = message;
}
public static bool TryParseBudgetField()
{
if (!int.TryParse(BudgetText, out var result))
{
return false;
}
Budget = result;
return true;
}
public static void AdjustBudget(int delta)
{
Budget = Mathf.Clamp(Budget + delta, 10, 50000);
BudgetText = Budget.ToString();
}
public static int GetBlueBudgetUsed()
{
BattleBudget battleBudget = GetBattleBudget();
if (battleBudget == null)
{
return 0;
}
try
{
return battleBudget.GetBudget((Team)1);
}
catch
{
return 0;
}
}
public static int GetBlueBudgetMax()
{
BattleBudget battleBudget = GetBattleBudget();
if (battleBudget == null)
{
return 0;
}
try
{
return battleBudget.GetMaxBudget((Team)1);
}
catch
{
return 0;
}
}
public static int GetBlueBudgetRemaining()
{
BattleBudget battleBudget = GetBattleBudget();
if (battleBudget == null)
{
return 0;
}
try
{
if (battleBudget.IsInfinite((Team)1))
{
return int.MaxValue;
}
int budget = battleBudget.GetBudget((Team)1);
int maxBudget = battleBudget.GetMaxBudget((Team)1);
return Mathf.Max(0, maxBudget - budget);
}
catch
{
return 0;
}
}
public static void SyncBlueUI()
{
if ((Object)(object)PlacementUI.Instance == (Object)null)
{
return;
}
try
{
PlacementUI.Instance.SetBudgetText((Team)1, GetBlueBudgetUsed());
PlacementUI.Instance.OnUnitCountChanged((Team)1);
}
catch
{
}
}
public static void Generate()
{
LastPlacedCount = 0;
LastSpent = 0;
LastMessage = "Generating...";
if (!TryParseBudgetField())
{
LastMessage = "Budget field is invalid.";
return;
}
Budget = Mathf.Clamp(Budget, 10, 50000);
BudgetText = Budget.ToString();
GameModeService service = ServiceLocator.GetService<GameModeService>();
if ((Object)(object)service == (Object)null || service.CurrentGameMode == null)
{
LastMessage = "No active game mode.";
return;
}
BaseGameMode currentGameMode = service.CurrentGameMode;
if (currentGameMode.Brush == null || currentGameMode.BattleBudget == null)
{
LastMessage = "Placement brush or battle budget is missing.";
return;
}
UnitDatabase.EnsureInitialized(IncludeModdedCustomUnits, IncludeWipUnits);
if (UnitDatabase.Count == 0)
{
LastMessage = "No usable units found.";
return;
}
if (ClearBeforeGenerate)
{
PlacementSpawner.ClearBlueArmy();
SyncBlueUI();
}
BattleBudget battleBudget = currentGameMode.BattleBudget;
int blueBudgetUsed = GetBlueBudgetUsed();
int blueBudgetMax = GetBlueBudgetMax();
bool flag = battleBudget.IsInfinite((Team)1);
int num = (flag ? int.MaxValue : Mathf.Max(0, blueBudgetMax - blueBudgetUsed));
if (!flag && Budget > num && (IgnoreOfficialBudgetCap || RaiseOfficialBlueBudget))
{
int num2 = blueBudgetUsed + Budget;
battleBudget.SetBudget((Team)1, num2);
num = GetBlueBudgetRemaining();
}
int num3 = (flag ? Budget : Mathf.Min(Budget, num));
if (num3 <= 0)
{
LastMessage = "Blue side has no remaining budget to spend.";
return;
}
int cheapestCost = UnitDatabase.GetCheapestCost();
if (cheapestCost <= 0 || num3 < cheapestCost)
{
LastMessage = "Budget is below the cheapest usable unit.";
return;
}
if (!TryBuildPlacementArea(currentGameMode.Brush, out var area))
{
LastMessage = "Could not find the real blue placement area on this map.";
return;
}
List<UnitBlueprint> list = BuildArmy(num3);
if (list.Count == 0)
{
LastMessage = "Could not assemble an army from the current budget.";
return;
}
PlaceArmy(currentGameMode.Brush, area, list);
SyncBlueUI();
if (LastPlacedCount <= 0)
{
LastMessage = "Nothing was placed. The formation probably ran out of valid blue-side space.";
return;
}
LastMessage = "Placed " + LastPlacedCount + " real blue placement units, spent " + LastSpent + ", blue used now " + GetBlueBudgetUsed() + ", remaining " + GetBlueBudgetRemaining() + ".";
}
private static BattleBudget GetBattleBudget()
{
GameModeService service = ServiceLocator.GetService<GameModeService>();
if ((Object)(object)service == (Object)null || service.CurrentGameMode == null)
{
return null;
}
return service.CurrentGameMode.BattleBudget;
}
private static List<UnitBlueprint> BuildArmy(int workingBudget)
{
List<UnitBlueprint> list = new List<UnitBlueprint>();
int remaining = workingBudget;
int cheapestCost = UnitDatabase.GetCheapestCost();
int num = 0;
while (remaining >= cheapestCost && num < 5000)
{
List<UnitBlueprint> list2 = UnitDatabase.Units.Where((UnitBlueprint u) => (int)u.GetUnitCost(true) <= remaining).ToList();
if (list2.Count == 0)
{
break;
}
UnitBlueprint val = PickWeightedAffordable(list2, remaining);
if ((Object)(object)val == (Object)null)
{
break;
}
list.Add(val);
remaining -= (int)val.GetUnitCost(true);
num++;
}
return list;
}
private static UnitBlueprint PickWeightedAffordable(List<UnitBlueprint> affordable, int remaining)
{
float num = Mathf.Clamp((float)remaining * 0.08f, 70f, 1400f);
float num2 = 0f;
float[] array = new float[affordable.Count];
for (int i = 0; i < affordable.Count; i++)
{
UnitBlueprint val = affordable[i];
int unitCost = (int)val.GetUnitCost(true);
float num3 = Mathf.Abs((float)unitCost - num) / Mathf.Max(1f, num);
float num4 = 0.25f + 1f / (1f + num3) * 1.75f;
num2 += (array[i] = ((!IsRanged(val)) ? (num4 * 1.1f) : (num4 * 0.9f)));
}
float num5 = (float)Rng.NextDouble() * num2;
for (int j = 0; j < affordable.Count; j++)
{
num5 -= array[j];
if (num5 <= 0f)
{
return affordable[j];
}
}
return affordable[affordable.Count - 1];
}
private static void PlaceArmy(UnitPlacementBrush brush, PlacementArea area, List<UnitBlueprint> army)
{
//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
//IL_0304: Unknown result type (might be due to invalid IL or missing references)
//IL_0309: Unknown result type (might be due to invalid IL or missing references)
//IL_030f: Unknown result type (might be due to invalid IL or missing references)
//IL_0319: Unknown result type (might be due to invalid IL or missing references)
//IL_031e: Unknown result type (might be due to invalid IL or missing references)
//IL_0323: Unknown result type (might be due to invalid IL or missing references)
//IL_0365: Unknown result type (might be due to invalid IL or missing references)
//IL_036a: Unknown result type (might be due to invalid IL or missing references)
//IL_036f: Unknown result type (might be due to invalid IL or missing references)
//IL_0380: Unknown result type (might be due to invalid IL or missing references)
//IL_0385: Unknown result type (might be due to invalid IL or missing references)
//IL_038a: Unknown result type (might be due to invalid IL or missing references)
//IL_0393: Unknown result type (might be due to invalid IL or missing references)
//IL_0395: Unknown result type (might be due to invalid IL or missing references)
List<LayoutEntry> list = new List<LayoutEntry>();
for (int i = 0; i < army.Count; i++)
{
UnitBlueprint val = army[i];
EstimateFootprint(val, out var width, out var depth);
list.Add(new LayoutEntry
{
Blueprint = val,
Cost = (int)val.GetUnitCost(true),
Width = width,
Depth = depth,
IsRanged = IsRanged(val),
IsLarge = (depth >= 3.2f || width >= 3.2f || (Object)(object)val.Mount != (Object)null)
});
}
List<LayoutEntry> entries = (from e in list
where !e.IsRanged
orderby e.IsLarge descending, e.Cost descending
select e).ToList();
List<LayoutEntry> entries2 = (from e in list
where e.IsRanged
orderby e.Cost descending
select e).ToList();
float maxWidth = Mathf.Max(8f, area.MaxS - area.MinS - 2.5f);
List<List<LayoutEntry>> list2 = new List<List<LayoutEntry>>();
list2.AddRange(PackRows(entries, maxWidth));
list2.AddRange(PackRows(entries2, maxWidth));
float num = 2.2f;
float num2 = 1.8f;
float num3 = 1.2f;
float num4 = area.FrontF - num;
for (int j = 0; j < list2.Count; j++)
{
List<LayoutEntry> list3 = CenterOutOrder(list2[j]);
float num5 = 0f;
float num6 = 0f;
for (int k = 0; k < list3.Count; k++)
{
num5 = Mathf.Max(num5, list3[k].Depth);
num6 += list3[k].Width;
}
float num7 = num4 - num5 * 0.5f;
if (num7 - num5 * 0.5f < area.BackF + num2)
{
break;
}
float num8 = (0f - num6) * 0.5f;
num8 = Mathf.Clamp(num8, area.MinS + num3, area.MaxS - num3 - num6);
float num9 = num8;
for (int l = 0; l < list3.Count; l++)
{
LayoutEntry layoutEntry = list3[l];
float num10 = num9 + layoutEntry.Width * 0.5f;
num9 += layoutEntry.Width;
float num11 = Random.Range(0f - PositionJitter, PositionJitter) * 0.35f;
float num12 = Random.Range(0f - PositionJitter, PositionJitter);
Vector3 position = area.Center + area.Forward * (num7 + num11) + area.Side * (num10 + num12);
if (SnapIntoBlueArea(brush, area, ref position))
{
position.y = area.Y + GetSpawnLift(layoutEntry);
float num13 = Random.Range(0f - FacingVariation, FacingVariation);
Quaternion rotation = Quaternion.LookRotation(area.Forward, Vector3.up) * Quaternion.Euler(0f, num13, 0f);
if (PlacementSpawner.PlaceUnit(layoutEntry.Blueprint, position, rotation))
{
LastPlacedCount++;
LastSpent += layoutEntry.Cost;
}
}
}
num4 = num7 - num5 * 0.5f - UnitPadding;
}
}
private static float GetSpawnLift(LayoutEntry entry)
{
float num = Mathf.Max(0.7f, entry.Depth * 0.45f);
if (entry.IsLarge)
{
num += 0.35f;
}
num += SpawnHeightLift;
return Mathf.Clamp(num, 0.8f, 3.5f);
}
private static List<List<LayoutEntry>> PackRows(List<LayoutEntry> entries, float maxWidth)
{
List<List<LayoutEntry>> list = new List<List<LayoutEntry>>();
if (entries.Count == 0)
{
return list;
}
List<LayoutEntry> list2 = new List<LayoutEntry>();
float num = 0f;
for (int i = 0; i < entries.Count; i++)
{
LayoutEntry layoutEntry = entries[i];
if (list2.Count > 0 && num + layoutEntry.Width > maxWidth)
{
list.Add(list2);
list2 = new List<LayoutEntry>();
num = 0f;
}
list2.Add(layoutEntry);
num += layoutEntry.Width;
}
if (list2.Count > 0)
{
list.Add(list2);
}
return list;
}
private static List<LayoutEntry> CenterOutOrder(List<LayoutEntry> source)
{
List<LayoutEntry> list = (from e in source
orderby e.IsLarge descending, e.Cost descending, e.Depth descending
select e).ToList();
List<LayoutEntry> list2 = new List<LayoutEntry>();
bool flag = true;
for (int i = 0; i < list.Count; i++)
{
if (flag)
{
list2.Insert(0, list[i]);
}
else
{
list2.Add(list[i]);
}
flag = !flag;
}
return list2;
}
private static bool TryBuildPlacementArea(UnitPlacementBrush brush, out PlacementArea area)
{
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Invalid comparison between Unknown and I4
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Invalid comparison between Unknown and I4
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
//IL_015f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
//IL_01be: Unknown result type (might be due to invalid IL or missing references)
//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
//IL_0234: Unknown result type (might be due to invalid IL or missing references)
//IL_0235: Unknown result type (might be due to invalid IL or missing references)
//IL_023b: Unknown result type (might be due to invalid IL or missing references)
//IL_023d: Unknown result type (might be due to invalid IL or missing references)
//IL_0243: Unknown result type (might be due to invalid IL or missing references)
//IL_0245: Unknown result type (might be due to invalid IL or missing references)
//IL_026b: Unknown result type (might be due to invalid IL or missing references)
area = null;
MethodInfo teamAreaMethod = GetTeamAreaMethod(brush);
if (teamAreaMethod == null)
{
Plugin.LogWarning("Could not reflect GetTeamAreaAtPosition from the brush behaviour.");
return false;
}
List<Vector3> list = new List<Vector3>();
List<Vector3> list2 = new List<Vector3>();
Vector3 val = default(Vector3);
for (float num = -70f; num <= 70f; num += 2.5f)
{
for (float num2 = -70f; num2 <= 70f; num2 += 2.5f)
{
((Vector3)(ref val))..ctor(num, 0.2f, num2);
if (TryGetTeamAtPoint(teamAreaMethod, brush.BrushBehaviour, val, out var team, out var _))
{
if ((int)team == 1)
{
list.Add(val);
}
else if ((int)team == 0)
{
list2.Add(val);
}
}
}
}
if (list.Count < 8)
{
return false;
}
Vector3 val2 = Average(list);
Vector3 val3 = ((list2.Count > 0) ? Average(list2) : (val2 + Vector3.left * 10f));
Vector3 val4 = val3 - val2;
val4.y = 0f;
if (((Vector3)(ref val4)).sqrMagnitude < 0.001f)
{
val4 = Vector3.left;
}
((Vector3)(ref val4)).Normalize();
Vector3 val5 = new Vector3(0f - val4.z, 0f, val4.x);
Vector3 normalized = ((Vector3)(ref val5)).normalized;
float num3 = float.MinValue;
float num4 = float.MaxValue;
float num5 = float.MaxValue;
float num6 = float.MinValue;
for (int i = 0; i < list.Count; i++)
{
Vector3 val6 = list[i] - val2;
float num7 = Vector3.Dot(val6, val4);
float num8 = Vector3.Dot(val6, normalized);
if (num7 > num3)
{
num3 = num7;
}
if (num7 < num4)
{
num4 = num7;
}
if (num8 < num5)
{
num5 = num8;
}
if (num8 > num6)
{
num6 = num8;
}
}
area = new PlacementArea
{
Center = val2,
Forward = val4,
Side = normalized,
FrontF = num3,
BackF = num4,
MinS = num5,
MaxS = num6,
Y = val2.y
};
return true;
}
private static bool SnapIntoBlueArea(UnitPlacementBrush brush, PlacementArea area, ref Vector3 position)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Invalid comparison between Unknown and I4
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Invalid comparison between Unknown and I4
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
MethodInfo teamAreaMethod = GetTeamAreaMethod(brush);
if (teamAreaMethod == null)
{
return true;
}
if (TryGetTeamAtPoint(teamAreaMethod, brush.BrushBehaviour, position, out var team, out var rotation) && (int)team == 1)
{
return true;
}
for (int i = 1; i <= 18; i++)
{
float num = 0.55f * (float)i;
Vector3 val = position - area.Forward * num;
val += area.Side * Random.Range(-0.35f, 0.35f);
if (TryGetTeamAtPoint(teamAreaMethod, brush.BrushBehaviour, val, out team, out rotation) && (int)team == 1)
{
position = val;
return true;
}
}
return false;
}
private static MethodInfo GetTeamAreaMethod(UnitPlacementBrush brush)
{
if (brush == null || brush.BrushBehaviour == null)
{
return null;
}
return ((object)brush.BrushBehaviour).GetType().GetMethod("GetTeamAreaAtPosition", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
private static bool TryGetTeamAtPoint(MethodInfo method, object brushBehaviour, Vector3 point, out Team team, out Quaternion rotation)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Expected I4, but got Unknown
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
team = (Team)0;
rotation = Quaternion.identity;
try
{
object[] array = new object[2]
{
point,
Quaternion.identity
};
object obj = method.Invoke(brushBehaviour, array);
if (obj is Team)
{
team = (Team)(int)(Team)obj;
rotation = (Quaternion)array[1];
return true;
}
}
catch
{
}
return false;
}
private static Vector3 Average(List<Vector3> points)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = Vector3.zero;
for (int i = 0; i < points.Count; i++)
{
val += points[i];
}
return val / (float)Mathf.Max(1, points.Count);
}
private static void EstimateFootprint(UnitBlueprint blueprint, out float width, out float depth)
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
width = 2.2f;
depth = 2.2f;
try
{
if ((Object)(object)blueprint != (Object)null && (Object)(object)blueprint.UnitBase != (Object)null)
{
Unit component = blueprint.UnitBase.GetComponent<Unit>();
if ((Object)(object)component != (Object)null)
{
PlacementSquare placementSquare = component.PlacementSquare;
Vector3 size = ((PlacementSquare)(ref placementSquare)).Size;
width = Mathf.Max(1.4f, Mathf.Abs(size.x)) + UnitPadding;
depth = Mathf.Max(1.4f, Mathf.Abs(size.z)) + UnitPadding;
return;
}
}
}
catch
{
}
int num = (int)(((Object)(object)blueprint != (Object)null) ? blueprint.GetUnitCost(true) : 100);
float num2 = Mathf.Clamp((float)num / 350f, 1.8f, 4.8f);
width = num2 + UnitPadding * 0.5f;
depth = num2 + UnitPadding * 0.5f;
}
private static bool IsRanged(UnitBlueprint blueprint)
{
if ((Object)(object)blueprint == (Object)null)
{
return false;
}
try
{
if ((Object)(object)blueprint.RightWeapon != (Object)null && (Object)(object)blueprint.RightWeapon.GetComponent<RangeWeapon>() != (Object)null)
{
return true;
}
if ((Object)(object)blueprint.LeftWeapon != (Object)null && (Object)(object)blueprint.LeftWeapon.GetComponent<RangeWeapon>() != (Object)null)
{
return true;
}
}
catch
{
}
return false;
}
}
public static class PlacementSpawner
{
public static bool PlaceUnit(UnitBlueprint blueprint, Vector3 position, Quaternion rotation)
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)blueprint == (Object)null)
{
return false;
}
GameModeService service = ServiceLocator.GetService<GameModeService>();
if ((Object)(object)service == (Object)null || service.CurrentGameMode == null)
{
return false;
}
BaseGameMode currentGameMode = service.CurrentGameMode;
UnitPlacementBrush brush = currentGameMode.Brush;
if (brush == null)
{
return false;
}
int bluePlacedCount = GetBluePlacedCount();
try
{
brush.PlaceUnitInternal(blueprint, (Team)1, position, rotation, true, (RuntimeReference)null, true, false, true, 0);
}
catch (Exception ex)
{
Plugin.LogWarning("Placement failed for " + blueprint.Name + ": " + ex.Message);
return false;
}
int bluePlacedCount2 = GetBluePlacedCount();
return bluePlacedCount2 > bluePlacedCount;
}
public static void ClearBlueArmy()
{
if ((Object)(object)PlacementUI.Instance != (Object)null)
{
PlacementUI.Instance.ClearBlue();
return;
}
GameModeService service = ServiceLocator.GetService<GameModeService>();
if ((Object)(object)service != (Object)null && service.CurrentGameMode != null)
{
service.CurrentGameMode.OnClearedTeam((Team)1);
}
}
public static int GetBluePlacedCount()
{
GameModeService service = ServiceLocator.GetService<GameModeService>();
if ((Object)(object)service == (Object)null || service.CurrentGameMode == null)
{
return 0;
}
BaseGameMode currentGameMode = service.CurrentGameMode;
if (currentGameMode.TeamLayouts == null)
{
return 0;
}
return currentGameMode.TeamLayouts.GetTeamLayout((Team)1).Count;
}
public static void StartBattle()
{
if ((Object)(object)PlacementUI.Instance != (Object)null)
{
PlacementUI.Instance.StartBattle();
}
}
public static void BackToPlacement()
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Invalid comparison between Unknown and I4
GameModeService service = ServiceLocator.GetService<GameModeService>();
if (!((Object)(object)service == (Object)null) && service.CurrentGameMode != null)
{
BaseGameMode currentGameMode = service.CurrentGameMode;
if (currentGameMode.GameStateManager != null && (int)currentGameMode.GameStateManager.GameState == 1)
{
currentGameMode.GameStateManager.EnterPlacementState();
}
}
}
}
[BepInPlugin("com.randomenemygenerator.fullmod", "Random Enemy Generator", "3.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
internal static Plugin Instance;
private GameObject uiRoot;
private void Awake()
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected O, but got Unknown
Instance = this;
uiRoot = new GameObject("RandomEnemyGenerator_UI");
((Object)uiRoot).hideFlags = (HideFlags)1;
Object.DontDestroyOnLoad((Object)(object)uiRoot);
uiRoot.AddComponent<UIManager.UIBehaviour>();
UnitDatabase.MarkDirty();
LogInfo("Random Enemy Generator loaded.");
}
private void OnDestroy()
{
if ((Object)(object)uiRoot != (Object)null)
{
Object.Destroy((Object)(object)uiRoot);
uiRoot = null;
}
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
internal static void LogInfo(string message)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
}
}
internal static void LogWarning(string message)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
}
}
internal static void LogError(string message)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)message);
}
}
}
public static class UIManager
{
public sealed class UIBehaviour : MonoBehaviour
{
private Rect windowRect = new Rect(18f, 18f, 390f, 560f);
private Vector2 scroll;
private bool visible = true;
private bool blockingGameInput;
private void Update()
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
if (Input.GetKeyDown((KeyCode)287))
{
visible = !visible;
}
bool flag = visible && ((Rect)(ref windowRect)).Contains(GetMouseGuiPosition());
bool flag2 = blockingGameInput && Input.GetMouseButton(0);
SetGameInputBlocked(flag || flag2);
}
private void OnDisable()
{
SetGameInputBlocked(blocked: false);
}
private void OnDestroy()
{
SetGameInputBlocked(blocked: false);
}
private void OnGUI()
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Expected O, but got Unknown
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Invalid comparison between Unknown and I4
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Invalid comparison between Unknown and I4
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Invalid comparison between Unknown and I4
if (!visible)
{
SetGameInputBlocked(blocked: false);
return;
}
GUI.depth = -1000;
windowRect = GUI.Window(684213, windowRect, new WindowFunction(DrawWindow), "Random Enemy Generator");
Event current = Event.current;
if (current != null && ((Rect)(ref windowRect)).Contains(GetMouseGuiPosition()) && ((int)current.type == 0 || (int)current.type == 1 || (int)current.type == 3 || (int)current.type == 6))
{
current.Use();
}
}
private void DrawWindow(int id)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_058c: Unknown result type (might be due to invalid IL or missing references)
scroll = GUILayout.BeginScrollView(scroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[2]
{
GUILayout.Width(380f),
GUILayout.Height(525f)
});
GUILayout.Label("F6 = show / hide", Array.Empty<GUILayoutOption>());
GUILayout.Space(6f);
GUILayout.Label("Requested spend", Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
ArmyGenerator.BudgetText = GUILayout.TextField(ArmyGenerator.BudgetText, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) });
if (GUILayout.Button("-500", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
{
ArmyGenerator.AdjustBudget(-500);
}
if (GUILayout.Button("+500", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }))
{
ArmyGenerator.AdjustBudget(500);
}
if (GUILayout.Button("+5000", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
{
ArmyGenerator.AdjustBudget(5000);
}
GUILayout.EndHorizontal();
ArmyGenerator.Budget = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)ArmyGenerator.Budget, 10f, 50000f, Array.Empty<GUILayoutOption>()));
if (ArmyGenerator.TryParseBudgetField())
{
ArmyGenerator.Budget = Mathf.Clamp(ArmyGenerator.Budget, 10, 50000);
ArmyGenerator.BudgetText = ArmyGenerator.Budget.ToString();
}
else
{
ArmyGenerator.BudgetText = ArmyGenerator.Budget.ToString();
}
GUILayout.Label("Requested spend: " + ArmyGenerator.Budget, Array.Empty<GUILayoutOption>());
GUILayout.Label("Blue spent now: " + ArmyGenerator.GetBlueBudgetUsed(), Array.Empty<GUILayoutOption>());
GUILayout.Label("Blue max budget: " + ArmyGenerator.GetBlueBudgetMax(), Array.Empty<GUILayoutOption>());
GUILayout.Label("Blue remaining cap: " + ArmyGenerator.GetBlueBudgetRemaining(), Array.Empty<GUILayoutOption>());
GUILayout.Space(10f);
ArmyGenerator.UnitPadding = SliderLine("Unit padding", ArmyGenerator.UnitPadding, 0.2f, 2.5f);
ArmyGenerator.PositionJitter = SliderLine("Position jitter", ArmyGenerator.PositionJitter, 0f, 1.5f);
ArmyGenerator.FacingVariation = SliderLine("Facing variation", ArmyGenerator.FacingVariation, 0f, 20f);
ArmyGenerator.SpawnHeightLift = SliderLine("Spawn height lift", ArmyGenerator.SpawnHeightLift, 0.3f, 3f);
GUILayout.Space(8f);
GUILayout.Label("Options", Array.Empty<GUILayoutOption>());
ArmyGenerator.ClearBeforeGenerate = GUILayout.Toggle(ArmyGenerator.ClearBeforeGenerate, "Clear current blue army first", Array.Empty<GUILayoutOption>());
ArmyGenerator.IgnoreOfficialBudgetCap = GUILayout.Toggle(ArmyGenerator.IgnoreOfficialBudgetCap, "Ignore official TABS blue budget cap", Array.Empty<GUILayoutOption>());
ArmyGenerator.RaiseOfficialBlueBudget = GUILayout.Toggle(ArmyGenerator.RaiseOfficialBlueBudget, "Also try to raise official blue budget", Array.Empty<GUILayoutOption>());
ArmyGenerator.IncludeModdedCustomUnits = GUILayout.Toggle(ArmyGenerator.IncludeModdedCustomUnits, "Include modded/custom units", Array.Empty<GUILayoutOption>());
ArmyGenerator.IncludeWipUnits = GUILayout.Toggle(ArmyGenerator.IncludeWipUnits, "Include WIP units (unstable)", Array.Empty<GUILayoutOption>());
GUILayout.Space(10f);
GUILayout.Label("Actions", Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Generate Blue Army", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
{
ArmyGenerator.Generate();
}
if (GUILayout.Button("Clear Blue Army", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }))
{
PlacementSpawner.ClearBlueArmy();
ArmyGenerator.SyncBlueUI();
ArmyGenerator.SetStatus("Cleared blue army.");
}
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Reload Unit Database", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
{
UnitDatabase.MarkDirty();
UnitDatabase.EnsureInitialized(ArmyGenerator.IncludeModdedCustomUnits, ArmyGenerator.IncludeWipUnits);
ArmyGenerator.SetStatus("Reloaded unit database.");
}
if (GUILayout.Button("Sync Blue UI", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
{
ArmyGenerator.SyncBlueUI();
ArmyGenerator.SetStatus("Blue UI synced.");
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (GUILayout.Button("Start Battle", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
{
PlacementSpawner.StartBattle();
}
if (GUILayout.Button("Back To Placement", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
{
PlacementSpawner.BackToPlacement();
}
GUILayout.EndHorizontal();
GUILayout.Space(10f);
GUILayout.Label("Status", Array.Empty<GUILayoutOption>());
GUILayout.Space(4f);
GUILayout.Label("Usable units loaded: " + UnitDatabase.Count, Array.Empty<GUILayoutOption>());
GUILayout.Label("Current blue placed units: " + PlacementSpawner.GetBluePlacedCount(), Array.Empty<GUILayoutOption>());
GUILayout.Label("Last generated units: " + ArmyGenerator.LastPlacedCount, Array.Empty<GUILayoutOption>());
GUILayout.Label("Last spent: " + ArmyGenerator.LastSpent, Array.Empty<GUILayoutOption>());
GUILayout.TextArea(ArmyGenerator.LastMessage, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) });
GUILayout.EndScrollView();
GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f));
}
private float SliderLine(string label, float value, float min, float max)
{
GUILayout.Label(label + ": " + value.ToString("0.0"), Array.Empty<GUILayoutOption>());
return GUILayout.HorizontalSlider(value, min, max, Array.Empty<GUILayoutOption>());
}
private static Vector2 GetMouseGuiPosition()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
Vector3 mousePosition = Input.mousePosition;
return new Vector2(mousePosition.x, (float)Screen.height - mousePosition.y);
}
private void SetGameInputBlocked(bool blocked)
{
if (blockingGameInput != blocked)
{
blockingGameInput = blocked;
UIScreenInputBlocker.DoBlockInput(blocked);
}
}
}
}
public static class UnitDatabase
{
private static bool initialized;
private static bool includeModdedCustomUnits;
private static bool includeWipUnits;
public static List<UnitBlueprint> Units { get; private set; } = new List<UnitBlueprint>();
public static int Count => (Units != null) ? Units.Count : 0;
public static void MarkDirty()
{
initialized = false;
Units.Clear();
}
public static void EnsureInitialized(bool includeModded, bool includeWip)
{
if (!initialized || includeModdedCustomUnits != includeModded || includeWipUnits != includeWip || Units.Count <= 0)
{
includeModdedCustomUnits = includeModded;
includeWipUnits = includeWip;
Initialize();
}
}
public static void Initialize()
{
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
initialized = true;
Units = new List<UnitBlueprint>();
ContentDatabase val;
try
{
val = ContentDatabase.Instance();
}
catch (Exception ex)
{
Plugin.LogWarning("ContentDatabase not ready: " + ex.Message);
initialized = false;
return;
}
if (val == null)
{
initialized = false;
return;
}
HashSet<string> hashSet = new HashSet<string>();
IEnumerable<Faction> allFactions = val.GetAllFactions();
if (allFactions == null)
{
initialized = false;
return;
}
foreach (Faction item2 in allFactions)
{
if ((Object)(object)item2 == (Object)null || item2.Units == null)
{
continue;
}
UnitBlueprint[] units = item2.Units;
foreach (UnitBlueprint val2 in units)
{
if (IsUsable(val2))
{
DatabaseID gUID = val2.Entity.GUID;
string item = ((object)(DatabaseID)(ref gUID)).ToString();
if (hashSet.Add(item))
{
Units.Add(val2);
}
}
}
}
Units = (from u in Units
orderby (int)u.GetUnitCost(true), u.Name
select u).ToList();
Plugin.LogInfo("Loaded usable units: " + Units.Count);
}
public static int GetCheapestCost()
{
if (Units == null || Units.Count == 0)
{
return 0;
}
return (int)Units[0].GetUnitCost(true);
}
private static bool IsUsable(UnitBlueprint unit)
{
if ((Object)(object)unit == (Object)null)
{
return false;
}
if (unit.Entity == null)
{
return false;
}
if ((Object)(object)unit.UnitBase == (Object)null)
{
return false;
}
if ((Object)(object)unit.UnitBase.GetComponent<Unit>() == (Object)null)
{
return false;
}
if (unit.IsSecret)
{
return false;
}
if (unit.ExcludeFromOnlineMultiplayer)
{
return false;
}
if (unit.HasMissingComponents)
{
return false;
}
if (!includeModdedCustomUnits && (unit.IsModUnit || unit.IsCustomUnit))
{
return false;
}
string text = ((unit.Entity.Name ?? string.Empty) + " " + (unit.Name ?? string.Empty) + " " + (unit.UnitDescription ?? string.Empty)).ToUpperInvariant();
if (!includeWipUnits && text.Contains("WIP"))
{
return false;
}
if (text.Contains("DEBUG"))
{
return false;
}
if (text.Contains("PLACEHOLDER"))
{
return false;
}
if (text.Contains("TEST"))
{
return false;
}
int unitCost;
try
{
unitCost = (int)unit.GetUnitCost(true);
}
catch
{
return false;
}
if (unitCost <= 0)
{
return false;
}
return true;
}
}