Skip to content

Commit

Permalink
Removed old bot cap settings, more log level changes, block broken si…
Browse files Browse the repository at this point in the history
…lo spawn
  • Loading branch information
dwesterwick committed Feb 12, 2025
1 parent 1ef7501 commit dc3a4fc
Show file tree
Hide file tree
Showing 26 changed files with 89 additions and 273 deletions.
22 changes: 4 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,17 +323,11 @@ Since normal AI Limit mods will disable bots that are questing (which will preve
* **bot_spawns.blacklisted_pmc_bot_brains**: An array of the bot "brain" types that SPT will not be able to use when generating initial PMC's. These "brain" types have behaviors that inhibit their ability to quest, and this causes them to get stuck in areas for a long time (including their spawn locations). **Do not change this unless you know what you're doing!**
* **bot_spawns.spawn_retry_time**: If any bots fail to spawn, no other attempts will be made to spawn more of them for this amount of time (in seconds). By default, this is **10** s.
* **bot_spawns.delay_game_start_until_bot_gen_finishes**: After the final loading screen shows "0:00.000" for a few seconds, the game will be further delayed from starting if not all bots have been generated. Without doing this, PMC's may not spawn immediately when the raid starts, and the remaining bots will take much longer to generate. This is **true** by default.
* **bot_spawns.spawn_initial_bosses_first**: If initial bosses must spawn before PMC's are allowed to spawn. This does not apply to Factory (Day or Night). If this is **false** and **bot_spawns.advanced_eft_bot_count_management.enabled=false**, initial PMC spawns may prevent some bosses (i.e. Rogues on Lighthouse) from spawning at the beginning of the raid. This is **false** by default and assumes **bot_spawns.advanced_eft_bot_count_management.enabled=true**.
* **bot_spawns.spawn_initial_bosses_first**: If initial bosses must spawn before PMC's are allowed to spawn. This does not apply to Factory (Day or Night). This is **false** by default.
* **bot_spawns.non_wave_retry_delay_after_blocked**: If Scavs are blocked by Questing Bots (per the F12 menu settings) from spawning via EFT's "new" spawning system, delay the next spawn check by this many seconds. This does not have an effect on Scav spawning behavior, but it will reduce server load by limiting how many extra bots it will generate between spawn checks. This is **180** s by default.
* **bot_spawns.advanced_eft_bot_count_management.enabled**: If **true**, this enables code that tricks EFT into thinking that bots generated by this mod are human players. This makes EFT ignore bot caps (both total and zone-specific) for PMC's and player Scavs generated by this mod. This is **true** by default.
* **bot_spawns.advanced_eft_bot_count_management.use_EFT_bot_caps**: If **bot_spawns.advanced_eft_bot_count_management.enabled=true**, SPT's bot caps will be changed to match EFT's bot caps. This is **true** by default.
* **bot_spawns.advanced_eft_bot_count_management.only_decrease_bot_caps**: If **bot_spawns.advanced_eft_bot_count_management.enabled=true** and **bot_spawns.advanced_eft_bot_count_management.use_EFT_bot_caps=true**, SPT's bot caps will be changed to match EFT's bot caps only if EFT's bot caps are lower. This is **true** by default.
* **bot_spawns.advanced_eft_bot_count_management.bot_cap_adjustments**: If **bot_spawns.advanced_eft_bot_count_management.enabled=true** and **bot_spawns.advanced_eft_bot_count_management.use_EFT_bot_caps=true**, these additional adjustments will be made to SPT's bot caps after changing them to EFT's. This is used to balance bot spawns and performance.
* **bot_spawns.bot_cap_adjustments.enabled**: If bot caps should be modified (**false** by default assuming **bot_spawns.advanced_eft_bot_count_management.enabled=true**).
* **bot_spawns.bot_cap_adjustments.min_other_bots_allowed_to_spawn**: PMC's and player Scavs will not be allowed to spawn unless there are fewer than this value below the bot count for the map. For example, if this value is 4 and the maximum bot cap is 20, PMC's and player Scavs will not be allowed to spawn if there are 17 or more alive bots in the map. This is to retain a "buffer" below the maximum bot cap so that Scavs are able to continue spawning throughout the raid. This is **2** by default.
* **bot_spawns.bot_cap_adjustments.add_max_players_to_bot_cap**: If this is **true** (which is the default setting), the bot cap for the map will be increased by the maximum number of players for the map. This is to better emulate live Tarkov where there can still be many Scavs around the map even with a full lobby.
* **bot_spawns.bot_cap_adjustments.max_additional_bots**: The bot cap for the map will not be allowed to be increased by more than this value. If this value is too high, performance may be impacted. This is **5** by default.
* **bot_spawns.bot_cap_adjustments.max_total_bots**: The highest allowed bot cap for any map. If this value is too high, performance may be impacted. This is **26** by default.
* **bot_spawns.bot_cap_adjustments.use_EFT_bot_caps**: SPT's bot caps will be changed to match EFT's bot caps. This is **true** by default.
* **bot_spawns.bot_cap_adjustments.only_decrease_bot_caps**: If **bot_spawns.bot_cap_adjustments.use_EFT_bot_caps=true**, SPT's bot caps will be changed to match EFT's bot caps only if EFT's bot caps are lower. This is **true** by default.
* **bot_spawns.bot_cap_adjustments.map_specific_adjustments**: If **bot_spawns.bot_cap_adjustments.use_EFT_bot_caps=true**, these additional adjustments will be made to SPT's bot caps after changing them to EFT's. This is used to balance bot spawns and performance.
* **bot_spawns.limit_initial_boss_spawns.enabled**: If initial boss spawns should be limited (**true** by default).
* **bot_spawns.limit_initial_boss_spawns.disable_rogue_delay**: If the 180-second delay SPT adds to Rogue spawns on Lighthouse should be removed. This is **true** by default.
* **bot_spawns.limit_initial_boss_spawns.max_initial_bosses**: The maximum number of bosses that are allowed to spawn at the beginning of the raid (including Raiders and Rogues). After this number is reached, all remaining initial boss spawns will be canceled. If this number is too high, few Scavs will be able to spawn after the initial PMC spawns. This is **14** by default.
Expand Down Expand Up @@ -378,14 +372,6 @@ Since normal AI Limit mods will disable bots that are questing (which will preve
* A *"Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead."* error will sometimes appear in the game console after a bot unlocks a door. This can be ignored.
* Player-level ranges for some quests are not reasonable, so bots may do late-game quests at low player levels and vice versa. This is because EFT has no minimum level defined for several quest lines.

**PMC and Player-Scav Spawning System:**
* If there is a lot of PMC action at the beginning of the raid, the rest of the raid will feel dead. However, this isn't so different from live Tarkov.
* If **advanced_eft_bot_count_management.enabled=false**, not all PMC's or player Scavs spawn into Streets because too many Scavs spawn into the map first
* In maps with a high number of max players, Scavs don't always spawn when the game generates them if your **max_alive_bots** setting is high and **advanced_eft_bot_count_management.enabled=false**
* In maps with a high number of max players, performance can be pretty bad if your **max_alive_bots** setting is high
* Noticeable stuttering for (possibly) several seconds when the initial PMC wave spawns if your **max_alive_bots** setting is high
* Performance may be worse if **advanced_eft_bot_count_management.enabled=true** because EFT may be allowed to spawn more Scavs than with previous versions of this mod.

**---------- Credits ----------**

* Thanks to [Props](https://hub.sp-tarkov.com/user/18746-props/) for sharing the code [DONUTS](https://hub.sp-tarkov.com/files/file/878-swag-donuts-dynamic-spawn-waves-and-custom-spawn-points/) uses to spawn bots. This was the inspiration to create this mod.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private void revertAlliances(Player player, Player otherPlayer = null)

if (!originalAllies[player].Contains(ally))
{
LoggingController.LogInfo(player.GetText() + "'s group is no longer allied with " + ally.GetText());
LoggingController.LogDebug(player.GetText() + "'s group is no longer allied with " + ally.GetText());

BotOwner allyOwner = ally.GetBotOwner();
if (allyOwner != null)
Expand All @@ -282,7 +282,7 @@ private void revertAlliances(Player player, Player otherPlayer = null)

if (!playerGroup.Enemies.ContainsKey(enemy))
{
LoggingController.LogInfo(player.GetText() + "'s group has restored their hostility with " + enemy.GetText());
LoggingController.LogDebug(player.GetText() + "'s group has restored their hostility with " + enemy.GetText());
playerGroup.AddEnemy(enemy, EBotEnemyCause.initCauseEnemy);
}
}
Expand Down
14 changes: 12 additions & 2 deletions bepinex_dev/SPTQuestingBots/Components/LocationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,9 @@ public SpawnPointParams[] GetAllValidSpawnPointParams()

if (CurrentLocation.Id.Contains("factory"))
{
// Bots cannot enter the building from outside where the transit extract is

SpawnPointParams[] validSpawnPointParams = CurrentLocation.SpawnPointParams
.Where(s => (s.Position.x < 8) || (s.Position.x > 33) || (s.Position.z < 45) || (s.Position.z > 65))
.Where(s => !isOutsideNearTransitOnFactory(s.Position) && !isInsideBrokenSiloOnFactory(s.Position))
.ToArray();

return validSpawnPointParams;
Expand All @@ -355,6 +355,16 @@ public SpawnPointParams[] GetAllValidSpawnPointParams()
return CurrentLocation.SpawnPointParams;
}

private static bool isOutsideNearTransitOnFactory(Vector3 position)
{
return (position.x > 8) && (position.x < 33) && (position.z > 45) && (position.z < 65);
}

private static bool isInsideBrokenSiloOnFactory(Vector3 position)
{
return (position.x > -9) && (position.x < 0) && (position.z > 12) && (position.z < 20);
}

// This isn't actually used anywhere in this mod, but I left it in here because it's a pretty nifty algorithm
public bool TryGetObjectNearPosition<T>(Vector3 position, float maxDistance, bool onlyVisible, out T obj) where T: Behaviour
{
Expand Down
27 changes: 2 additions & 25 deletions bepinex_dev/SPTQuestingBots/Components/Spawning/BotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ public abstract class BotGenerator : MonoBehaviour
public int GeneratedBotCount { get; private set; } = 0;

public bool WaitForInitialBossesToSpawn { get; protected set; } = true;
public bool RespectMaxBotCap { get; protected set; } = true;
public int MaxAliveBots { get; protected set; } = 10;
public int MinOtherBotsAllowedToSpawn { get; protected set; } = -99;
public float RetryTimeSeconds { get; protected set; } = 10;

protected readonly List<Models.BotSpawnInfo> BotGroups = new List<Models.BotSpawnInfo>();
Expand Down Expand Up @@ -299,12 +297,6 @@ public IReadOnlyCollection<BotOwner> GetSpawnGroupMembers(BotOwner bot)
return new ReadOnlyCollection<BotOwner>(botFriends.ToArray());
}

public int CountBeforeBotCapIsReached()
{
List<Player> allPlayers = Singleton<GameWorld>.Instance.AllAlivePlayersList;
return Singleton<GameWorld>.Instance.GetComponent<LocationData>().MaxTotalBots - allPlayers.Count;
}

public bool AllowedToSpawnBots()
{
if (!HasGeneratedBots || IsSpawningBots || !HasRemainingSpawns)
Expand Down Expand Up @@ -332,23 +324,6 @@ public bool AllowedToSpawnBots()
return true;
}

public bool CanSpawnAdditionalBots()
{
// Ensure the total number of bots isn't too close to the bot cap for the map
if (RespectMaxBotCap && (CountBeforeBotCapIsReached() < MinOtherBotsAllowedToSpawn))
{
return false;
}

// Don't allow too many alive bots to be on the map for performance and difficulty reasons
if (BotsAllowedToSpawnForGeneratorType() > 0)
{
return true;
}

return false;
}

public bool HaveInitialBossWavesSpawned()
{
if (!PlayerWantsBotsInRaid())
Expand Down Expand Up @@ -381,6 +356,8 @@ public IEnumerable<BotOwner> AliveBots()
return aliveBots;
}

public bool CanSpawnAdditionalBots() => BotsAllowedToSpawnForGeneratorType() > 0;

public int BotsAllowedToSpawnForGeneratorType()
{
return MaxAliveBots - AliveBots().Count();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ public PMCGenerator() : base("PMC") { }

protected override void Init()
{
if (ConfigController.Config.BotSpawns.BotCapAdjustments.Enabled)
{
MinOtherBotsAllowedToSpawn = ConfigController.Config.BotSpawns.BotCapAdjustments.MinOtherBotsAllowedToSpawn;
}

RetryTimeSeconds = ConfigController.Config.BotSpawns.SpawnRetryTime;
RespectMaxBotCap = !ConfigController.Config.BotSpawns.AdvancedEFTBotCountManagement.Enabled;

setMaxAliveBots();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@ public PScavGenerator() : base("PScav") { }

protected override void Init()
{
if (ConfigController.Config.BotSpawns.BotCapAdjustments.Enabled)
{
MinOtherBotsAllowedToSpawn = ConfigController.Config.BotSpawns.BotCapAdjustments.MinOtherBotsAllowedToSpawn;
}

RetryTimeSeconds = ConfigController.Config.BotSpawns.SpawnRetryTime;
RespectMaxBotCap = !ConfigController.Config.BotSpawns.AdvancedEFTBotCountManagement.Enabled;

setMaxAliveBots();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@ namespace SPTQuestingBots.Configuration
{
public class BotCapAdjustmentsConfig
{
[JsonProperty("enabled")]
public bool Enabled { get; set; } = true;
[JsonProperty("use_EFT_bot_caps")]
public bool UseEFTBotCaps { get; set; } = true;

[JsonProperty("min_other_bots_allowed_to_spawn")]
public int MinOtherBotsAllowedToSpawn { get; set; } = 4;
[JsonProperty("only_decrease_bot_caps")]
public bool OnlyDecreaseBotCaps { get; set; } = true;

[JsonProperty("add_max_players_to_bot_cap")]
public bool AddMaxPlayersToBotCap { get; set; } = false;

[JsonProperty("max_additional_bots")]
public int MaxAdditionalBots { get; set; } = 10;

[JsonProperty("max_total_bots")]
public int MaxTotalBots { get; set; } = 40;
[JsonProperty("map_specific_adjustments")]
public Dictionary<string, int> MapSpecificAdjustments { get; set; } = new Dictionary<string, int>();

public BotCapAdjustmentsConfig()
{
Expand Down
3 changes: 0 additions & 3 deletions bepinex_dev/SPTQuestingBots/Configuration/BotSpawnsConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ public class BotSpawnsConfig
[JsonProperty("non_wave_retry_delay_after_blocked")]
public float NonWaveRetryDelayAfterBlocked { get; set; } = 20;

[JsonProperty("advanced_eft_bot_count_management")]
public AdvancedEFTBotCountManagementConfig AdvancedEFTBotCountManagement { get; set; } = new AdvancedEFTBotCountManagementConfig();

[JsonProperty("bot_cap_adjustments")]
public BotCapAdjustmentsConfig BotCapAdjustments { get; set; } = new BotCapAdjustmentsConfig();

Expand Down
6 changes: 2 additions & 4 deletions bepinex_dev/SPTQuestingBots/Helpers/BotGroupHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using Comfort.Common;
using EFT;
using HarmonyLib;
using SPTQuestingBots.Controllers;
using SPTQuestingBots.Models;

namespace SPTQuestingBots.Helpers
{
Expand Down Expand Up @@ -130,14 +128,14 @@ public static void FormAlliance(this BotsGroup playerGroup, IPlayer playerToAlly

foreach (IPlayer remainingEnemy in enemyMatches)
{
LoggingController.LogInfo("Group containing " + groupMembersText + " has paused their hostility with " + remainingEnemy.GetText());
LoggingController.LogDebug("Group containing " + groupMembersText + " has paused their hostility with " + remainingEnemy.GetText());
playerGroup.RemoveEnemy(remainingEnemy);
}

Player otherPlayer = playerToAlly.GetPlayer();
if (!playerGroup.Allies.Contains(otherPlayer))
{
LoggingController.LogInfo("Group containing " + groupMembersText + " is temporarily allied with " + otherPlayer.GetText());
LoggingController.LogDebug("Group containing " + groupMembersText + " is temporarily allied with " + otherPlayer.GetText());
playerGroup.AddAlly(otherPlayer);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected static void PatchPostfix(BotOwner __instance)
{
registerBot(__instance);

if (ConfigController.Config.BotSpawns.AdvancedEFTBotCountManagement.Enabled && BotGenerator.GetAllGeneratedBotProfileIDs().Contains(__instance.Profile.Id))
if (BotGenerator.GetAllGeneratedBotProfileIDs().Contains(__instance.Profile.Id))
{
reduceBotCounts(__instance);
}
Expand Down Expand Up @@ -84,8 +84,6 @@ private static void registerBotAsHumanPlayer(BotOwner __instance)

botSpawnerClass.AddPlayer(__instance.GetPlayer());
__instance.GetPlayer().OnPlayerDead += deletePlayer;

Controllers.LoggingController.LogWarning("Registered " + __instance.GetText() + " as a human player in EFT");
}

private static void deletePlayer(Player player, IPlayer lastAgressor, DamageInfoStruct damage, EBodyPart part)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected override MethodBase GetTargetMethod()
protected static void PatchPostfix(PhysicsTriggerHandler ___physicsTriggerHandler_0)
{
Components.LightkeeperIslandMonitor.LightkeeperTraderZoneColliderHandler = ___physicsTriggerHandler_0;
Controllers.LoggingController.LogInfo("Found collider for the Lighthouse trader zone");
Controllers.LoggingController.LogDebug("Found collider for the Lighthouse trader zone");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,20 @@ public static void AddSpawnedScavs(int count)
spawnedScavTimes.Add(elapsedTime, count);
}

public static int GetSpawnedScavCount(float timeWindow)
public static int GetSpawnedScavCount(float timeWindow, bool excludeBotsBeforeThreshold)
{
float elapsedTime = SPT.SinglePlayer.Utils.InRaid.RaidTimeUtil.GetSecondsSinceSpawning();
float elapsedTimeThreshold = elapsedTime - timeWindow;

return spawnedScavTimes
IEnumerable<KeyValuePair<float, int>> scavsToCheck = spawnedScavTimes;

if (excludeBotsBeforeThreshold)
{
int initialScavs = 0;
scavsToCheck = scavsToCheck.SkipWhile(x => (initialScavs += x.Value) <= QuestingBotsPluginConfig.ScavSpawnLimitThreshold.Value);
}

return scavsToCheck
.Where(x => x.Key >= elapsedTimeThreshold)
.Sum(x => x.Value);
}
Expand Down
Loading

0 comments on commit dc3a4fc

Please sign in to comment.