Implement playerscavgenerator

This commit is contained in:
CWX
2025-01-13 14:08:34 +00:00
parent de93e93d4d
commit 542ee67bc7
+285 -13
View File
@@ -1,17 +1,77 @@
using Core.Annotations;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Generators;
[Injectable]
public class PlayerScavGenerator
{
private readonly ILogger _logger;
private readonly RandomUtil _randomUtil;
private readonly DatabaseService _databaseService;
private readonly HashUtil _hashUtil;
private readonly ItemHelper _itemHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly SaveServer _saveServer;
private readonly ProfileHelper _profileHelper;
private readonly BotHelper _botHelper;
private readonly FenceService _fenceService;
private readonly BotLootCacheService _botLootCacheService;
private readonly LocalisationService _localisationService;
private readonly BotGenerator _botGenerator;
private readonly ConfigServer _configServer;
private readonly ICloner _cloner;
private readonly TimeUtil _timeUtil;
private PlayerScavConfig _playerScavConfig;
public PlayerScavGenerator()
public PlayerScavGenerator
(
ILogger logger,
RandomUtil randomUtil,
DatabaseService databaseService,
HashUtil hashUtil,
ItemHelper itemHelper,
BotGeneratorHelper botGeneratorHelper,
SaveServer saveServer,
ProfileHelper profileHelper,
BotHelper botHelper,
FenceService fenceService,
BotLootCacheService botLootCacheService,
LocalisationService localisationService,
BotGenerator botGenerator,
ConfigServer configServer,
ICloner cloner,
TimeUtil timeUtil
)
{
_logger = logger;
_randomUtil = randomUtil;
_databaseService = databaseService;
_hashUtil = hashUtil;
_itemHelper = itemHelper;
_botGeneratorHelper = botGeneratorHelper;
_saveServer = saveServer;
_profileHelper = profileHelper;
_botHelper = botHelper;
_fenceService = fenceService;
_botLootCacheService = botLootCacheService;
_localisationService = localisationService;
_botGenerator = botGenerator;
_configServer = configServer;
_cloner = cloner;
_timeUtil = timeUtil;
_playerScavConfig = configServer.GetConfig<PlayerScavConfig>(ConfigTypes.PLAYERSCAV);
}
/// <summary>
@@ -21,7 +81,78 @@ public class PlayerScavGenerator
/// <returns>profile object</returns>
public PmcData Generate(string sessionID)
{
throw new NotImplementedException();
// get karma level from profile
var profile = _saveServer.GetProfile(sessionID);
var profileCharactersClone = _cloner.Clone(profile.CharacterData);
var pmcDataClone = _cloner.Clone(profileCharactersClone.PmcData);
var existingScavDataClone = _cloner.Clone(profileCharactersClone.ScavData);
var scavKarmaLevel = GetScavKarmaLevel(pmcDataClone);
// use karma level to get correct karmaSettings
var playerScavKarmaSettings = _playerScavConfig.KarmaLevel[scavKarmaLevel.ToString()];
if (playerScavKarmaSettings == null)
_logger.Error(_localisationService.GetText("scav-missing_karma_settings", scavKarmaLevel));
_logger.Debug($"generated player scav loadout with karma level {scavKarmaLevel}");
// Edit baseBotNode values
var baseBotNode = ConstructBotBaseTemplate(playerScavKarmaSettings.BotTypeForLoot);
AdjustBotTemplateWithKarmaSpecificSettings(playerScavKarmaSettings, baseBotNode);
var scavData = _botGenerator.GeneratePlayerScav(
sessionID,
playerScavKarmaSettings.BotTypeForLoot.ToLower(),
"easy",
baseBotNode,
pmcDataClone);
// Remove cached bot data after scav was generated
_botLootCacheService.ClearCache();
// Add scav metadata
scavData.Savage = null;
scavData.Aid = pmcDataClone.Aid;
scavData.TradersInfo = pmcDataClone.TradersInfo;
scavData.Info.Settings = new();
scavData.Info.Bans = [];
scavData.Info.RegistrationDate = pmcDataClone.Info.RegistrationDate;
scavData.Info.GameVersion = pmcDataClone.Info.GameVersion;
scavData.Info.MemberCategory = MemberCategory.UNIQUE_ID;
scavData.Info.LockedMoveCommands = true;
scavData.RagfairInfo = pmcDataClone.RagfairInfo;
scavData.UnlockedInfo = pmcDataClone.UnlockedInfo;
// Persist previous scav data into new scav
scavData.Id = existingScavDataClone.Id ?? pmcDataClone.Savage;
scavData.SessionId = existingScavDataClone.SessionId ?? pmcDataClone.SessionId;
scavData.Skills = this.GetScavSkills(existingScavDataClone);
scavData.Stats = this.GetScavStats(existingScavDataClone);
scavData.Info.Level = this.GetScavLevel(existingScavDataClone);
scavData.Info.Experience = this.GetScavExperience(existingScavDataClone);
scavData.Quests = existingScavDataClone.Quests ?? [];
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? new();
scavData.Notes = existingScavDataClone.Notes ?? new() { DataNotes = new() };
scavData.WishList = existingScavDataClone.WishList ?? new();
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? new();
// Add additional items to player scav as loot
AddAdditionalLootToPlayerScavContainers(playerScavKarmaSettings.LootItemsToAddChancePercent, scavData, [
"TacticalVest",
"Pockets",
"Backpack"
]);
// Remove secure container
scavData = _profileHelper.RemoveSecureContainer(scavData);
// set cooldown timer
scavData = SetScavCooldownTimer(scavData, pmcDataClone);
// add scav to profile
_saveServer.GetProfile(sessionID).CharacterData.ScavData = scavData;
return scavData;
}
/// <summary>
@@ -32,7 +163,40 @@ public class PlayerScavGenerator
/// <param name="containersToAddTo">Possible slotIds to add loot to</param>
protected void AddAdditionalLootToPlayerScavContainers(Dictionary<string, double> possibleItemsToAdd, BotBase scavData, List<string> containersToAddTo)
{
throw new NotImplementedException();
foreach (var tpl in possibleItemsToAdd)
{
var shouldAdd = _randomUtil.GetChance100(tpl.Value);
if (!shouldAdd)
continue;
var itemResult = _itemHelper.GetItem(tpl.Key);
if (!itemResult.Key)
{
_logger.Warning(_localisationService.GetText("scav-unable_to_add_item_to_player_scav", tpl));
continue;
}
var itemTemplate = itemResult.Value;
var itemsToAdd = new List<Item>()
{
new Item()
{
Id = _hashUtil.Generate(),
Template = itemTemplate.Id,
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemTemplate)
}
};
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
containersToAddTo,
itemsToAdd[0].Id,
itemTemplate.Id,
itemsToAdd,
scavData.Inventory);
if (result != ItemAddedResult.SUCCESS)
_logger.Debug($"Unable to add keycard to bot. Reason: {result.ToString()}");
}
}
/// <summary>
@@ -43,7 +207,19 @@ public class PlayerScavGenerator
/// <returns>karma level</returns>
protected double GetScavKarmaLevel(PmcData pmcData)
{
throw new NotImplementedException();
var fenceInfo = pmcData.TradersInfo[Traders.FENCE];
// can be empty during profile creation
if (fenceInfo == null)
{
_logger.Warning(_localisationService.GetText("scav-missing_karma_level_getting_default"));
return 0;
}
if (fenceInfo.Standing > 6)
return 6;
return Math.Floor(fenceInfo.Standing ?? 0);
}
/// <summary>
@@ -54,7 +230,19 @@ public class PlayerScavGenerator
/// <returns>IBotType object</returns>
protected BotType ConstructBotBaseTemplate(string botTypeForLoot)
{
throw new NotImplementedException();
var baseScavType = "assault";
var asssaultBase = _cloner.Clone(_botHelper.GetBotTemplate(baseScavType));
// Loot bot is same as base bot, return base with no modification
if (botTypeForLoot == baseScavType)
return asssaultBase;
var lootBase = _cloner.Clone(_botHelper.GetBotTemplate(botTypeForLoot));
asssaultBase.BotInventory = lootBase.BotInventory;
asssaultBase.BotChances = lootBase.BotChances;
asssaultBase.BotGeneration = lootBase.BotGeneration;
return asssaultBase;
}
/// <summary>
@@ -64,32 +252,90 @@ public class PlayerScavGenerator
/// <param name="baseBotNode">bot template to modify according to karama level settings</param>
protected void AdjustBotTemplateWithKarmaSpecificSettings(KarmaLevel karmaSettings, BotType baseBotNode)
{
throw new NotImplementedException();
// Adjust equipment chance values
foreach (var equipment in karmaSettings.Modifiers.Equipment)
{
if (equipment.Value == 0)
continue;
var prop = typeof(EquipmentChances).GetProperties().FirstOrDefault(p => p.Name == equipment.Key);
var value = (int)prop.GetValue(baseBotNode.BotChances.EquipmentChances);
var newValue = (int)value + karmaSettings.Modifiers.Equipment[equipment.Key];
prop.SetValue(baseBotNode.BotChances.EquipmentChances, newValue);
}
// Adjust mod chance values
foreach (var mod in karmaSettings.Modifiers.Mod)
{
if (mod.Value == 0)
continue;
baseBotNode.BotChances.WeaponModsChances[mod.Key] += karmaSettings.Modifiers.Mod[mod.Key];
}
// Adjust item spawn quantity values
var props = karmaSettings.ItemLimits.GetType().GetProperties();
var botGenProps = baseBotNode.BotGeneration.Items.GetType().GetProperties();
foreach (var prop in props)
{
botGenProps.FirstOrDefault(p => p.Name == prop.Name).SetValue(baseBotNode.BotGeneration.Items, prop.GetValue(karmaSettings.ItemLimits));
}
// Blacklist equipment
props = baseBotNode.BotInventory.Equipment.GetType().GetProperties();
foreach (var equipment in karmaSettings.EquipmentBlacklist)
{
var blacklistedItemTpls = equipment.Value;
foreach (var itemToRemove in blacklistedItemTpls)
{
var dict = (Dictionary<string, double>?)props.FirstOrDefault(p => p.Name == equipment.Key).GetValue(baseBotNode.BotInventory.Equipment);
dict.Remove(itemToRemove);
}
}
}
protected Skills GetScavSkills(PmcData scavProfile)
{
throw new NotImplementedException();
if (scavProfile?.Skills != null)
return scavProfile.Skills;
return GetDefaultScavSkills();
}
protected Skills GetDefaultScavSkills()
{
throw new NotImplementedException();
return new()
{
Common = new(new(), new()),
Mastering = new(),
Points = 0
};
}
protected Stats GetScavStats(PmcData scavProfile)
{
throw new NotImplementedException();
if (scavProfile?.Stats != null)
return scavProfile.Stats;
return _profileHelper.GetDefaultCounters();
}
protected int GetScavLevel(PmcData scavProfile)
{
throw new NotImplementedException();
// Info can be null on initial account creation
if (scavProfile?.Info?.Level == null)
return 1;
return scavProfile?.Info?.Level ?? 1;
}
protected int GetScavExperience(PmcData scavProfile)
protected double GetScavExperience(PmcData scavProfile)
{
throw new NotImplementedException();
// Info can be null on initial account creation
if (scavProfile?.Info?.Experience == null)
return 0;
return scavProfile?.Info?.Experience ?? 0;
}
/// <summary>
@@ -101,6 +347,32 @@ public class PlayerScavGenerator
/// <returns></returns>
protected PmcData SetScavCooldownTimer(PmcData scavData, PmcData pmcData)
{
throw new NotImplementedException();
// Set cooldown time.
// Make sure to apply ScavCooldownTimer bonus from Hideout if the player has it.
var scavLockDuration = _databaseService.GetGlobals().Configuration.SavagePlayCooldown;
var modifier = 1D;
foreach (var bonus in pmcData.Bonuses)
{
if (bonus.Type == BonusType.ScavCooldownTimer)
{
// Value is negative, so add.
// Also note that for scav cooldown, multiple bonuses stack additively.
modifier += (bonus?.Value ?? 1) / 100;
}
}
var fenceInfo = _fenceService.GetFenceInfo(pmcData);
modifier *= fenceInfo.SavageCooldownModifier ?? 1;
scavLockDuration *= modifier;
var fullProfile = _profileHelper.GetFullProfile(pmcData?.SessionId);
if (fullProfile?.ProfileInfo?.Edition.ToLower().StartsWith(AccountTypes.SPT_DEVELOPER) ?? false)
scavLockDuration = 10;
if (scavData?.Info != null)
scavData.Info.SavageLockTime = _timeUtil.GetTimeStamp() / 1000 + (long)scavLockDuration;
return scavData;
}
}