This commit is contained in:
Alex
2025-01-15 11:50:55 +00:00
127 changed files with 13130 additions and 9952 deletions
+546 -37
View File
@@ -1,4 +1,5 @@
using Core.Annotations;
using Core.Helpers;
using Core.Models.Common;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
@@ -6,7 +7,9 @@ using Core.Models.Enums;
using Core.Models.Enums.RaidSettings;
using Core.Models.Spt.Bots;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using BodyPart = Core.Models.Eft.Common.Tables.BodyPart;
using ILogger = Core.Models.Utils.ILogger;
@@ -17,20 +20,65 @@ namespace Core.Generators;
public class BotGenerator
{
private readonly ILogger _logger;
private readonly HashUtil _hashUtil;
private readonly RandomUtil _randomUtil;
private readonly TimeUtil _timeUtil;
private readonly ProfileHelper _profileHelper;
private readonly DatabaseService _databaseService;
private readonly BotInventoryGenerator _botInventoryGenerator;
private readonly BotLevelGenerator _botLevelGenerator;
private readonly BotEquipmentFilterService _botEquipmentFilterService;
private readonly WeightedRandomHelper _weightedRandomHelper;
private readonly BotHelper _botHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly SeasonalEventService _seasonalEventService;
private readonly ItemFilterService _itemFilterService;
private readonly BotNameService _botNameService;
private readonly ConfigServer _configServer;
private readonly ICloner _cloner;
private BotConfig _botConfig;
private PmcConfig _pmcConfig;
public BotGenerator(
ILogger logger,
HashUtil hashUtil,
RandomUtil randomUtil,
TimeUtil timeUtil,
ProfileHelper profileHelper,
DatabaseService databaseService,
BotInventoryGenerator botInventoryGenerator,
BotLevelGenerator botLevelGenerator,
BotEquipmentFilterService botEquipmentFilterService,
WeightedRandomHelper weightedRandomHelper,
BotHelper botHelper,
BotGeneratorHelper botGeneratorHelper,
SeasonalEventService seasonalEventService,
ItemFilterService itemFilterService,
BotNameService botNameService,
ConfigServer configServer,
ICloner cloner
)
)
{
_logger = logger;
_hashUtil = hashUtil;
_randomUtil = randomUtil;
_timeUtil = timeUtil;
_profileHelper = profileHelper;
_databaseService = databaseService;
_botInventoryGenerator = botInventoryGenerator;
_botLevelGenerator = botLevelGenerator;
_botEquipmentFilterService = botEquipmentFilterService;
_weightedRandomHelper = weightedRandomHelper;
_botHelper = botHelper;
_botGeneratorHelper = botGeneratorHelper;
_seasonalEventService = seasonalEventService;
_itemFilterService = itemFilterService;
_botNameService = botNameService;
_configServer = configServer;
_cloner = cloner;
_botConfig = _configServer.GetConfig<BotConfig>(ConfigTypes.BOT);
_pmcConfig = _configServer.GetConfig<PmcConfig>(ConfigTypes.PMC);
}
/// <summary>
@@ -49,7 +97,8 @@ public class BotGenerator
bot.Info.Settings.Role = role;
bot.Info.Side = "Savage";
var botGenDetails = new BotGenerationDetails{
var botGenDetails = new BotGenerationDetails
{
IsPmc = false,
Side = "Savage",
Role = role,
@@ -108,7 +157,23 @@ public class BotGenerator
/// <returns>constructed bot</returns>
public BotBase PrepareAndGenerateBot(string sessionId, BotGenerationDetails botGenerationDetails)
{
throw new NotImplementedException();
var preparedBotBase = GetPreparedBotBase(
botGenerationDetails.EventRole ?? botGenerationDetails.Role, // Use eventRole if provided,
botGenerationDetails.Side,
botGenerationDetails.BotDifficulty
);
// Get raw json data for bot (Cloned)
var botRole = botGenerationDetails.IsPmc ?? false
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
: botGenerationDetails.Role;
var botJsonTemplateClone = _cloner.Clone(_botHelper.GetBotTemplate(botRole));
if (botJsonTemplateClone is not null)
{
_logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
}
return GenerateBot(sessionId, preparedBotBase, botJsonTemplateClone, botGenerationDetails);
}
/// <summary>
@@ -120,7 +185,12 @@ public class BotGenerator
/// <returns>Cloned bot base</returns>
public BotBase GetPreparedBotBase(string botRole, string botSide, string difficulty)
{
throw new NotImplementedException();
var botBaseClone = GetCloneOfBotBase();
botBaseClone.Info.Settings.Role = botRole;
botBaseClone.Info.Side = botSide;
botBaseClone.Info.Settings.BotDifficulty = difficulty;
return botBaseClone;
}
/// <summary>
@@ -140,35 +210,154 @@ public class BotGenerator
/// <param name="botJsonTemplate">Bot template from db/bots/x.json</param>
/// <param name="botGenerationDetails">details on how to generate the bot</param>
/// <returns>BotBase object</returns>
public BotBase GenerateBot(string sessionId, BotBase bot, BotType botJsonTemplate, BotGenerationDetails botGenerationDetails)
public BotBase GenerateBot(
string sessionId,
BotBase bot,
BotType botJsonTemplate,
BotGenerationDetails botGenerationDetails)
{
_logger.Error("NOT IMPLEMENTED BotGenerator.GenerateBot");
bot.Inventory.Items = [];
var botRoleLowercase = botGenerationDetails.Role.ToLower();
var botLevel = _botLevelGenerator.GenerateBotLevel(
botJsonTemplate.BotExperience.Level,
botGenerationDetails,
bot);
// Only filter bot equipment, never players
if (!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false))
{
_botEquipmentFilterService.FilterBotEquipment(
sessionId,
botJsonTemplate,
botLevel.Level.Value,
botGenerationDetails);
}
bot.Info.Nickname = _botNameService.GenerateUniqueBotNickname(
botJsonTemplate,
botGenerationDetails,
botRoleLowercase,
_botConfig.BotRolesThatMustHaveUniqueName);
bot.Info.LowerNickname = bot.Info.Nickname.ToLower();
// Only run when generating a 'fake' playerscav, not actual player scav
if (!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false) && ShouldSimulatePlayerScav(botRoleLowercase))
{
_botNameService.AddRandomPmcNameToBotMainProfileNicknameProperty(bot);
SetRandomisedGameVersionAndCategory(bot.Info);
}
if (!_seasonalEventService.ChristmasEventEnabled())
{
// Process all bots EXCEPT gifter, he needs christmas items
if (botGenerationDetails.Role != "gifter")
{
_seasonalEventService.RemoveChristmasItemsFromBotInventory(
botJsonTemplate.BotInventory,
botGenerationDetails.Role);
}
}
RemoveBlacklistedLootFromBotTemplate(botJsonTemplate.BotInventory);
// Remove hideout data if bot is not a PMC or pscav - match what live sends
if (!(botGenerationDetails.IsPmc.GetValueOrDefault(false) || botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)))
{
bot.Hideout = null;
}
bot.Info.Experience = botLevel.Exp;
bot.Info.Level = botLevel.Level;
bot.Info.Settings.Experience = GetExperienceRewardForKillByDifficulty(
botJsonTemplate.BotExperience.Reward,
botGenerationDetails.BotDifficulty,
botGenerationDetails.Role);
bot.Info.Settings.StandingForKill = GetStandingChangeForKillByDifficulty(
botJsonTemplate.BotExperience.StandingForKill,
botGenerationDetails.BotDifficulty,
botGenerationDetails.Role);
bot.Info.Settings.AggressorBonus = GetAgressorBonusByDifficulty(
botJsonTemplate.BotExperience.StandingForKill,
botGenerationDetails.BotDifficulty,
botGenerationDetails.Role);
bot.Info.Settings.UseSimpleAnimator = botJsonTemplate.BotExperience.UseSimpleAnimator ?? false;
bot.Info.Voice = _weightedRandomHelper.GetWeightedValue(botJsonTemplate.BotAppearance.Voice);
bot.Health = GenerateHealth(botJsonTemplate.BotHealth, botGenerationDetails.IsPlayerScav.GetValueOrDefault(false));
bot.Skills = GenerateSkills(botJsonTemplate.BotSkills); // TODO: fix bad type, bot jsons store skills in dict, output needs to be array
if (botGenerationDetails.IsPmc.GetValueOrDefault(false))
{
bot.Info.IsStreamerModeAvailable = true; // Set to true so client patches can pick it up later - client sometimes alters botrole to assaultGroup
SetRandomisedGameVersionAndCategory(bot.Info);
if (bot.Info.GameVersion == GameEditions.UNHEARD)
{
AddAdditionalPocketLootWeightsForUnheardBot(botJsonTemplate);
}
}
// Add drip
SetBotAppearance(bot, botJsonTemplate.BotAppearance, botGenerationDetails);
// Filter out blacklisted gear from the base template
FilterBlacklistedGear(botJsonTemplate, botGenerationDetails);
bot.Inventory = _botInventoryGenerator.GenerateInventory(
sessionId,
botJsonTemplate,
botRoleLowercase,
botGenerationDetails.IsPmc.GetValueOrDefault(false),
botLevel.Level.Value,
bot.Info.GameVersion);
if (_botConfig.BotRolesWithDogTags.Contains(botRoleLowercase))
{
AddDogtagToBot(bot);
}
// Generate new bot ID
AddIdsToBot(bot);
// Generate new inventory ID
GenerateInventoryId(bot);
// Set role back to originally requested now its been generated
if (botGenerationDetails.EventRole is not null)
{
bot.Info.Settings.Role = botGenerationDetails.EventRole;
}
return bot;
}
/// <summary>
/// Should this bot have a name like "name (Pmc Name)" and be alterd by client patch to be hostile to player
/// Should this bot have a name like "name (Pmc Name)" and be altered by client patch to be hostile to player
/// </summary>
/// <param name="botRole">Role bot has</param>
/// <returns>True if name should be simulated pscav</returns>
public bool ShouldSimulatePlayerScav(string botRole)
{
throw new NotImplementedException();
return botRole == "assault" && _randomUtil.GetChance100(_botConfig.ChanceAssaultScavHasPlayerScavName);
}
/// <summary>
/// Get exp for kill by bot difficulty
/// </summary>
/// <param name="experience">Dict of difficulties and experience</param>
/// <param name="experiences">Dict of difficulties and experience</param>
/// <param name="botDifficulty">the killed bots difficulty</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Experience for kill</returns>
public int GetExperienceRewardForKillByDifficulty(Dictionary<string, MinMax> experience, string botDifficulty, string role)
public double GetExperienceRewardForKillByDifficulty(Dictionary<string, MinMax> experiences, string botDifficulty, string role)
{
throw new NotImplementedException();
var result = experiences[botDifficulty.ToLower()];
if (result is null)
{
_logger.Debug("Unable to find experience for kill value for: ${ role} ${ botDifficulty}, falling back to `normal`");
return _randomUtil.GetDouble(experiences["normal"].Min.Value, experiences["normal"].Max.Value);
}
return _randomUtil.GetDouble(result.Min.Value, result.Max.Value);
}
/// <summary>
@@ -178,9 +367,16 @@ public class BotGenerator
/// <param name="botDifficulty">Difficulty of bot to look up</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Standing change value</returns>
public int GetStandingChangeForKillByDifficulty(Dictionary<string, int> standingForKill, string botDifficulty, string role)
public double GetStandingChangeForKillByDifficulty(Dictionary<string, double> standingsForKill, string botDifficulty, string role)
{
throw new NotImplementedException();
if (!standingsForKill.TryGetValue(botDifficulty.ToLower(), out var result))
{
_logger.Warning($"Unable to find standing for kill value for: {role} {botDifficulty}, falling back to `normal`");
return standingsForKill["normal"];
}
return result;
}
/// <summary>
@@ -190,9 +386,16 @@ public class BotGenerator
/// <param name="botDifficulty">Difficulty of bot to look up</param>
/// <param name="role">Role of bot (optional, used for error logging)</param>
/// <returns>Standing change value</returns>
public int GetAgressorBonusByDifficulty(Dictionary<string, int> aggressorBonus, string botDifficulty, string role)
public double GetAgressorBonusByDifficulty(Dictionary<string, double> aggressorBonuses, string botDifficulty, string role)
{
throw new NotImplementedException();
if (!aggressorBonuses.TryGetValue(botDifficulty.ToLower(), out var result))
{
_logger.Warning($"Unable to find aggressor bonus for kill value for: {role} {botDifficulty}, falling back to `normal`");
return aggressorBonuses["normal"];
}
return result;
}
/// <summary>
@@ -202,7 +405,26 @@ public class BotGenerator
/// <param name="botGenerationDetails">Generation details of bot</param>
public void FilterBlacklistedGear(BotType botJsonTemplate, BotGenerationDetails botGenerationDetails)
{
throw new NotImplementedException();
var blacklist = _botEquipmentFilterService.GetBotEquipmentBlacklist(
_botGeneratorHelper.GetBotEquipmentRole(botGenerationDetails.Role),
botGenerationDetails.PlayerLevel.GetValueOrDefault(1));
if (blacklist?.Gear is null)
{
// Nothing to filter by
return;
}
foreach (var equipmentKvP in blacklist.Gear)
{
var equipmentDict = botJsonTemplate.BotInventory.Equipment[equipmentKvP.Key];
foreach (var blacklistedTpl in equipmentKvP.Value)
{
// Set weighting to 0, will never be picked
equipmentDict[blacklistedTpl] = 0;
}
}
}
/// <summary>
@@ -211,7 +433,10 @@ public class BotGenerator
/// <param name="botJsonTemplate">Bot data to adjust</param>
public void AddAdditionalPocketLootWeightsForUnheardBot(BotType botJsonTemplate)
{
throw new NotImplementedException();
// Adjust pocket loot weights to allow for 5 or 6 items
var pocketWeights = botJsonTemplate.BotGeneration.Items["pocketLoot"].Weights;
pocketWeights["5"] = 1;
pocketWeights["6"] = 1;
}
/// <summary>
@@ -220,7 +445,37 @@ public class BotGenerator
/// <param name="botInventory">Bot to filter</param>
public void RemoveBlacklistedLootFromBotTemplate(BotTypeInventory botInventory)
{
throw new NotImplementedException();
List<string> lootContainersToFilter = ["Backpack", "Pockets", "TacticalVest"];
var props = botInventory.Items.GetType().GetProperties();
// Remove blacklisted loot from loot containers
foreach (var lootContainerKey in lootContainersToFilter)
{
var prop = props.FirstOrDefault(x => x.Name.ToLower() == lootContainerKey.ToLower());
var propValue = (Dictionary<string, double>)prop.GetValue(botInventory.Items);
// No container, skip
if (propValue?.Count == 0)
{
continue;
}
List<string> tplsToRemove = [];
foreach (var item in propValue)
{
if (_itemFilterService.IsLootableItemBlacklisted(item.Key))
{
tplsToRemove.Add(item.Key);
}
}
foreach (var blacklistedTplToRemove in tplsToRemove)
{
propValue.Remove(blacklistedTplToRemove);
}
prop.SetValue(botInventory.Items, propValue);
}
}
/// <summary>
@@ -231,7 +486,19 @@ public class BotGenerator
/// <param name="botGenerationDetails">Generation details</param>
public void SetBotAppearance(BotBase bot, Appearance appearance, BotGenerationDetails botGenerationDetails)
{
throw new NotImplementedException();
// Choose random values by weight
bot.Customization.Head = _weightedRandomHelper.GetWeightedValue<string>(appearance.Head);
bot.Customization.Feet = _weightedRandomHelper.GetWeightedValue<string>(appearance.Feet);
bot.Customization.Body = _weightedRandomHelper.GetWeightedValue<string>(appearance.Body);
var bodyGlobalDictDb = _databaseService.GetGlobals().Configuration.Customization.Body;
var chosenBodyTemplate = _databaseService.GetCustomization()[bot.Customization.Body];
// Some bodies have matching hands, look up body to see if this is the case
var chosenBody = bodyGlobalDictDb[chosenBodyTemplate?.Name.Trim()];
bot.Customization.Hands = chosenBody?.IsNotRandom ?? false
? chosenBody.Hands // Has fixed hands for chosen body, update to match
: _weightedRandomHelper.GetWeightedValue<string>(appearance.Hands); // Hands can be random, choose any from weighted dict
}
/// <summary>
@@ -240,7 +507,8 @@ public class BotGenerator
/// <param name="output">Generated bot array, ready to send to client</param>
public void LogPmcGeneratedCount(List<BotBase> output)
{
throw new NotImplementedException();
var pmcCount = output.Aggregate(0, (acc, cur) => { return cur.Info.Side == "Bear" || cur.Info.Side == "Usec" ? acc + 1 : acc; });
_logger.Debug($"Generated {output.Count} total bots. Replaced ${pmcCount} with PMCs");
}
/// <summary>
@@ -249,9 +517,107 @@ public class BotGenerator
/// <param name="healthObj">health object from bot json</param>
/// <param name="playerScav">Is a pscav bot being generated</param>
/// <returns>Health object</returns>
public Health GenerateHealth(Health healthObj, bool playerScav = false)
public BotBaseHealth GenerateHealth(BotTypeHealth healthObj, bool playerScav = false)
{
throw new NotImplementedException();
var bodyParts = playerScav
? GetLowestHpBody(healthObj.BodyParts)
: _randomUtil.GetArrayValue(healthObj.BodyParts);
BotBaseHealth health = new()
{
Hydration = new()
{
Current = _randomUtil.GetInt((int)healthObj.Hydration.Min, (int)healthObj.Hydration.Max),
Maximum = healthObj.Hydration.Max
},
Energy = new()
{
Current = _randomUtil.GetInt((int)healthObj.Energy.Min, (int)healthObj.Energy.Max),
Maximum = healthObj.Energy.Max
},
Temperature = new()
{
Current = _randomUtil.GetInt((int)healthObj.Temperature.Min, (int)healthObj.Temperature.Max),
Maximum = healthObj.Temperature.Max
},
BodyParts = new Dictionary<string, BodyPartHealth>()
{
{
"Head", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.Head.Min, (int)bodyParts.Head.Max),
Maximum = Math.Round(bodyParts.Head.Max ?? 0)
}
}
},
{
"Chest", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.Chest.Min, (int)bodyParts.Chest.Max),
Maximum = Math.Round(bodyParts.Chest.Max ?? 0)
}
}
},
{
"Stomach", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.Stomach.Min, (int)bodyParts.Stomach.Max),
Maximum = Math.Round(bodyParts.Stomach.Max ?? 0)
}
}
},
{
"LeftArm", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.LeftArm.Min, (int)bodyParts.LeftArm.Max),
Maximum = Math.Round(bodyParts.LeftArm.Max ?? 0)
}
}
},
{
"RightArm", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.RightArm.Min, (int)bodyParts.RightArm.Max),
Maximum = Math.Round(bodyParts.RightArm.Max ?? 0)
}
}
},
{
"LeftLeg", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.LeftLeg.Min, (int)bodyParts.LeftLeg.Max),
Maximum = Math.Round(bodyParts.LeftLeg.Max ?? 0)
}
}
},
{
"RightLeg", new BodyPartHealth
{
Health = new()
{
Current = _randomUtil.GetInt((int)bodyParts.RightLeg.Min, (int)bodyParts.RightLeg.Max),
Maximum = Math.Round(bodyParts.RightLeg.Max ?? 0)
}
}
}
},
UpdateTime = _timeUtil.GetTimeStamp(),
Immortal = false
};
return health;
}
/// <summary>
@@ -259,9 +625,33 @@ public class BotGenerator
/// </summary>
/// <param name="bodies">Body parts to sum up</param>
/// <returns>Lowest hp collection</returns>
public BodyPart? GetLowestHpBody(List<BodyPart> bodies) // TODO: there are two types of body parts
public BodyPart? GetLowestHpBody(List<BodyPart> bodies)
{
throw new NotImplementedException();
if (bodies.Count == 0)
return null;
BodyPart result = new();
var props = result.GetType().GetProperties();
double? currentHighest = double.MaxValue;
foreach (var bodyPart in bodies)
{
double? hpTotal = 0;
foreach (var prop in props)
{
var value = (MinMax)prop.GetValue(bodyPart);
hpTotal += value.Max;
}
if (hpTotal < currentHighest)
{
// Found collection with lower value that previous, use it
currentHighest = hpTotal;
result = bodyPart;
}
}
return result;
}
/// <summary>
@@ -269,9 +659,16 @@ public class BotGenerator
/// </summary>
/// <param name="botSkills">Skills that should have their progress value randomised</param>
/// <returns>Skills</returns>
public Skills GenerateSkills(BaseJsonSkills botSkills)
public Skills GenerateSkills(BotDbSkills botSkills)
{
throw new NotImplementedException();
var skillsToReturn = new Skills
{
Common = GetSkillsWithRandomisedProgressValue(botSkills.Common, true),
Mastering = GetSkillsWithRandomisedProgressValue(botSkills.Mastering, false),
Points = 0
};
return skillsToReturn;
}
/// <summary>
@@ -280,19 +677,49 @@ public class BotGenerator
/// <param name="skills">Skills to randomise</param>
/// <param name="isCommonSkills">Are the skills 'common' skills</param>
/// <returns>Skills with randomised progress values as an array</returns>
public List<BaseSkill> GetSkillsWithRandomisedProgressValue(Dictionary<string, BaseSkill> skills, bool isCommonSkills)
public List<BaseSkill> GetSkillsWithRandomisedProgressValue(Dictionary<string, MinMax>? skills, bool isCommonSkills)
{
throw new NotImplementedException();
if (skills is null)
return [];
return skills.Select(kvp =>
{
// Get skill from dict, skip if not found
var skill = kvp.Value;
if (skill == null)
{
return null;
}
// All skills have id and progress props
var skillToAdd = new BaseSkill
{
Id = kvp.Key,
Progress = _randomUtil.GetInt((int)skill.Min, (int)skill.Max)
};
// Common skills have additional props
if (isCommonSkills)
{
((Common)skillToAdd).PointsEarnedDuringSession = 0;
((Common)skillToAdd).LastAccess = 0;
}
return skillToAdd;
}).Where(baseSkill => baseSkill != null).ToList();
}
/// <summary>
/// Generate an id+aid for a bot and apply
/// </summary>
/// <param name="bot">bot to update</param>
/// <returns>updated IBotBase object</returns> // TODO: Node server claims this in summary but is void
/// <returns></returns>
public void AddIdsToBot(BotBase bot)
{
throw new NotImplementedException();
var botId = _hashUtil.Generate();
bot.Id = botId;
bot.Aid = _hashUtil.GenerateAccountId();
}
/// <summary>
@@ -302,7 +729,30 @@ public class BotGenerator
/// <param name="profile">Profile to update</param>
public void GenerateInventoryId(BotBase profile)
{
throw new NotImplementedException();
var newInventoryItemId = _hashUtil.Generate();
foreach (var item in profile.Inventory.Items) {
// Root item found, update its _id value to newly generated id
if (item.Template == ItemTpl.INVENTORY_DEFAULT) {
item.Id = newInventoryItemId;
continue;
}
// Optimisation - skip items without a parentId
// They are never linked to root inventory item + we already handled root item above
if (item.ParentId is null) {
continue;
}
// Item is a child of root inventory item, update its parentId value to newly generated id
if (item.ParentId == profile.Inventory.Equipment) {
item.ParentId = newInventoryItemId;
}
}
// Update inventory equipment id to new one we generated
profile.Inventory.Equipment = newInventoryItemId;
}
/// <summary>
@@ -312,19 +762,57 @@ public class BotGenerator
/// </summary>
/// <param name="botInfo">bot info object to update</param>
/// <returns>Chosen game version</returns>
public string SetRandomisedGameVersionAndCategory(Info botInfo) // TODO: there are two types of Info
public string SetRandomisedGameVersionAndCategory(Info botInfo)
{
throw new NotImplementedException();
// Special case
if (botInfo.Nickname?.ToLower() == "nikita") {
botInfo.GameVersion = GameEditions.UNHEARD;
botInfo.MemberCategory = MemberCategory.DEVELOPER;
return botInfo.GameVersion;
}
// Choose random weighted game version for bot
botInfo.GameVersion = _weightedRandomHelper.GetWeightedValue(_pmcConfig.GameVersionWeight);
// Choose appropriate member category value
switch (botInfo.GameVersion) {
case GameEditions.EDGE_OF_DARKNESS:
botInfo.MemberCategory = MemberCategory.UNIQUE_ID;
break;
case GameEditions.UNHEARD:
botInfo.MemberCategory = MemberCategory.UNHEARD;
break;
default:
// Everyone else gets a weighted randomised category
botInfo.MemberCategory = _weightedRandomHelper.GetWeightedValue<MemberCategory>(_pmcConfig.AccountTypeWeight);
break;
}
// Ensure selected category matches
botInfo.SelectedMemberCategory = botInfo.MemberCategory;
return botInfo.GameVersion;
}
/// <summary>
/// Add a side-specific (usec/bear) dogtag item to a bots inventory
/// </summary>
/// <param name="bot">bot to add dogtag to</param>
/// <returns>Bot with dogtag added</returns> // TODO: Node server claims this in summary but is void
/// <returns></returns>
public void AddDogtagToBot(BotBase bot)
{
throw new NotImplementedException();
Item inventoryItem = new () {
Id = _hashUtil.Generate(),
Template = GetDogtagTplByGameVersionAndSide(bot.Info.Side, bot.Info.GameVersion),
ParentId = bot.Inventory.Equipment,
SlotId = "Dogtag",
Upd = new () {
SpawnedInSession = true,
},
};
bot.Inventory.Items.Add(inventoryItem);
}
/// <summary>
@@ -335,7 +823,25 @@ public class BotGenerator
/// <returns>item tpl</returns>
public string GetDogtagTplByGameVersionAndSide(string side, string gameVersion)
{
throw new NotImplementedException();
if (side.ToLower() == "usec") {
switch (gameVersion) {
case GameEditions.EDGE_OF_DARKNESS:
return ItemTpl.BARTER_DOGTAG_USEC_EOD;
case GameEditions.UNHEARD:
return ItemTpl.BARTER_DOGTAG_USEC_TUE;
default:
return ItemTpl.BARTER_DOGTAG_USEC;
}
}
switch (gameVersion) {
case GameEditions.EDGE_OF_DARKNESS:
return ItemTpl.BARTER_DOGTAG_BEAR_EOD;
case GameEditions.UNHEARD:
return ItemTpl.BARTER_DOGTAG_BEAR_TUE;
default:
return ItemTpl.BARTER_DOGTAG_BEAR;
}
}
/// <summary>
@@ -344,6 +850,9 @@ public class BotGenerator
/// <param name="bot">Pmc object to adjust</param>
public void SetPmcPocketsByGameVersion(BotBase bot)
{
throw new NotImplementedException();
if (bot.Info.GameVersion == GameEditions.UNHEARD) {
var pockets = bot.Inventory.Items.FirstOrDefault((item) => item.SlotId == "Pockets");
pockets.Template = ItemTpl.POCKETS_1X4_TUE;
}
}
}
+270 -10
View File
@@ -1,19 +1,55 @@
using Core.Annotations;
using Core.Annotations;
using Core.Context;
using Core.Helpers;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Match;
using Core.Models.Enums;
using Core.Models.Spt.Bots;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Utils;
using Equipment = Core.Models.Eft.Common.Tables.Equipment;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Generators;
[Injectable]
public class BotInventoryGenerator
{
private readonly ILogger _logger;
private readonly HashUtil _hashUtil;
private readonly BotLootGenerator _botLootGenerator;
private readonly BotHelper _botHelper;
private readonly BotGeneratorHelper _botGeneratorHelper;
private readonly WeatherHelper _weatherHelper;
private readonly ProfileHelper _profileHelper;
private readonly ConfigServer _configServer;
private readonly ApplicationContext _applicationContext;
private BotConfig _botConfig;
public BotInventoryGenerator()
public BotInventoryGenerator(
ILogger logger,
HashUtil hashUtil,
BotLootGenerator botLootGenerator,
BotHelper botHelper,
BotGeneratorHelper botGeneratorHelper,
WeatherHelper weatherHelper,
ProfileHelper profileHelper,
ConfigServer configServer,
ApplicationContext applicationContext
)
{
_logger = logger;
_hashUtil = hashUtil;
_botLootGenerator = botLootGenerator;
_botHelper = botHelper;
_botGeneratorHelper = botGeneratorHelper;
_weatherHelper = weatherHelper;
_profileHelper = profileHelper;
_configServer = configServer;
_applicationContext = applicationContext;
_botConfig = _configServer.GetConfig<BotConfig>(ConfigTypes.BOT);
}
/// <summary>
@@ -26,9 +62,45 @@ public class BotInventoryGenerator
/// <param name="botLevel">Level of bot being generated</param>
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
/// <returns>PmcInventory object with equipment/weapons/loot</returns>
public BotBaseInventory generateInventory(string sessionId, BotType botJsonTemplate, string botRole, bool isPmc, int botLevel, string chosenGameVersion)
public BotBaseInventory GenerateInventory(string sessionId, BotType botJsonTemplate, string botRole, bool isPmc, int botLevel, string chosenGameVersion)
{
throw new NotImplementedException();
var templateInventory = botJsonTemplate.BotInventory;
var wornItemChances = botJsonTemplate.BotChances;
var itemGenerationLimitsMinMax = botJsonTemplate.BotGeneration;
// Generate base inventory with no items
var botInventory = GenerateInventoryBase();
// Get generated raid details bot will be spawned in
var raidConfig = _applicationContext
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.GetValue<GetRaidConfigurationRequestData>();
GenerateAndAddEquipmentToBot(
sessionId,
templateInventory,
wornItemChances,
botRole,
botInventory,
botLevel,
chosenGameVersion,
raidConfig);
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
GenerateAndAddWeaponsToBot(
templateInventory,
wornItemChances,
sessionId,
botInventory,
botRole,
isPmc,
itemGenerationLimitsMinMax,
botLevel);
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
_botLootGenerator.GenerateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
/// <summary>
@@ -37,7 +109,31 @@ public class BotInventoryGenerator
/// <returns>PmcInventory object</returns>
public BotBaseInventory GenerateInventoryBase()
{
throw new NotImplementedException();
var equipmentId = _hashUtil.Generate();
var stashId = _hashUtil.Generate();
var questRaidItemsId = _hashUtil.Generate();
var questStashItemsId = _hashUtil.Generate();
var sortingTableId = _hashUtil.Generate();
return new BotBaseInventory{
Items =
[
new() { Id = equipmentId, Template = ItemTpl.INVENTORY_DEFAULT },
new() { Id = stashId, Template = ItemTpl.STASH_STANDARD_STASH_10X30 },
new() { Id = questRaidItemsId, Template = ItemTpl.STASH_QUESTRAID },
new() { Id = questStashItemsId, Template = ItemTpl.STASH_QUESTOFFLINE },
new() { Id = sortingTableId, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE }
],
Equipment = equipmentId,
Stash = stashId,
QuestRaidItems = questRaidItemsId,
QuestStashItems = questStashItemsId,
SortingTable = sortingTableId,
HideoutAreaStashes = { },
FastPanel = { },
FavoriteItems = [],
HideoutCustomizationStashId = "",
};
}
/// <summary>
@@ -51,10 +147,168 @@ public class BotInventoryGenerator
/// <param name="botLevel">Level of bot</param>
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
/// <param name="raidConfig">RadiConfig</param>
public void GenerateAndAddEquipmentToBot(string sessionId, BotBaseInventory templateInventory, Chances wornItemChances, string botRole,
public void GenerateAndAddEquipmentToBot(string sessionId, BotTypeInventory templateInventory, Chances wornItemChances, string botRole,
BotBaseInventory botInventory, int botLevel, string chosenGameVersion, GetRaidConfigurationRequestData raidConfig)
{
throw new NotImplementedException();
// These will be handled later
var excludedSlots = new List<EquipmentSlots>(){
EquipmentSlots.Pockets,
EquipmentSlots.FirstPrimaryWeapon,
EquipmentSlots.SecondPrimaryWeapon,
EquipmentSlots.Holster,
EquipmentSlots.ArmorVest,
EquipmentSlots.TacticalVest,
EquipmentSlots.FaceCover,
EquipmentSlots.Headwear,
EquipmentSlots.Earpiece
};
_botConfig.Equipment.TryGetValue(_botGeneratorHelper.GetBotEquipmentRole(botRole), out var botEquipConfig);
var randomistionDetails = _botHelper.GetBotRandomizationDetails(botLevel, botEquipConfig);
// Apply nighttime changes if its nighttime + there's changes to make
if (
randomistionDetails?.NighttimeChanges is not null &&
raidConfig is not null &&
_weatherHelper.IsNightTime(raidConfig.TimeVariant)
)
{
foreach (var equipmentSlotKvP in (randomistionDetails.NighttimeChanges.EquipmentModsModifiers)) {
// Never let mod chance go outside of 0 - 100
randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] = Math.Min(
Math.Max( randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] + equipmentSlotKvP.Value, 0), 100);
}
}
// Get profile of player generating bots, we use their level later on
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
var botEquipmentRole = _botGeneratorHelper.GetBotEquipmentRole(botRole);
// Iterate over all equipment slots of bot, do it in specifc order to reduce conflicts
// e.g. ArmorVest should be generated after TactivalVest
// or FACE_COVER before HEADWEAR
foreach (var equipmentSlotKvP in templateInventory.Equipment) {
// Skip some slots as they need to be done in a specific order + with specific parameter values
// e.g. Weapons
if (excludedSlots.Contains(equipmentSlotKvP.Key)) {
continue;
}
GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = equipmentSlotKvP.Key,
RootEquipmentPool = equipmentSlotKvP.Value,
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
}
// Generate below in specific order
GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Pockets,
// Unheard profiles have unique sized pockets, TODO - handle this somewhere else in a better way
RootEquipmentPool =
chosenGameVersion == GameEditions.UNHEARD
? new Dictionary<string, double>{ [ItemTpl.POCKETS_1X4_TUE] = 1 }
: templateInventory.Equipment[EquipmentSlots.Pockets],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData{ Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE],
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.FaceCover,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.FaceCover],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Headwear,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Headwear],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Earpiece,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
var hasArmorVest = GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.ArmorVest,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.ArmorVest],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
// Bot has no armor vest and flagged to be forced to wear armored rig in this event
if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest) {
// Filter rigs down to only those with armor
FilterRigsToThoseWithProtection(templateInventory.Equipment, botRole);
}
// Optimisation - Remove armored rigs from pool
if (hasArmorVest) {
// Filter rigs down to only those with armor
FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botRole);
}
// Bot is flagged as always needing a vest
if (botEquipConfig.ForceRigWhenNoVest.GetValueOrDefault(false) && !hasArmorVest) {
wornItemChances.EquipmentChances["TacticalVest"] = 100;
}
GenerateEquipment( new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Earpiece,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
}
/// <summary>
@@ -62,7 +316,12 @@ public class BotInventoryGenerator
/// </summary>
/// <param name="templateEquipment">Equpiment to filter TacticalVest of</param>
/// <param name="botRole">Role of bot vests are being filtered for</param>
public void FilterRigsToThoseWithProtection(Equipment templateEquipment, string botRole)
public void FilterRigsToThoseWithProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole)
{
throw new NotImplementedException();
}
public void FilterRigsToThoseWithoutProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole, bool allowEmptyResult = true)
{
throw new NotImplementedException();
}
@@ -85,7 +344,8 @@ public class BotInventoryGenerator
/// <returns>true when item added</returns>
public bool GenerateEquipment(GenerateEquipmentProperties settings)
{
throw new NotImplementedException();
_logger.Error("NOT IMPLEMENTED - GenerateEquipment");
return true;
}
/// <summary>
@@ -110,7 +370,7 @@ public class BotInventoryGenerator
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="itemGenerationLimitsMinMax">Limits for items the bot can have</param>
/// <param name="botLevel">level of bot having weapon generated</param>
public void GenerateAndAddWeaponsToBot(BotBaseInventory templateInventory, Chances equipmentChances, string sessionId, BotBaseInventory botInventory,
public void GenerateAndAddWeaponsToBot(BotTypeInventory templateInventory, Chances equipmentChances, string sessionId, BotBaseInventory botInventory,
string botRole,
bool isPmc, Generation itemGenerationLimitsMinMax, int botLevel)
{
+69 -6
View File
@@ -1,16 +1,32 @@
using Core.Annotations;
using Core.Annotations;
using Core.Models.Common;
using Core.Models.Eft.Bot;
using Core.Models.Eft.Common.Tables;
using Core.Models.Spt.Bots;
using Core.Services;
using Core.Utils;
using ILogger = Core.Models.Utils.ILogger;
namespace Core.Generators;
[Injectable]
public class BotLevelGenerator
{
public BotLevelGenerator()
private readonly ILogger _logger;
private readonly RandomUtil _randomUtil;
private readonly MathUtil _mathUtil;
private readonly DatabaseService _databaseService;
public BotLevelGenerator(
ILogger logger,
RandomUtil randomUtil,
MathUtil mathUtil,
DatabaseService databaseService)
{
_logger = logger;
_randomUtil = randomUtil;
_mathUtil = mathUtil;
_databaseService = databaseService;
}
/// <summary>
@@ -22,12 +38,30 @@ public class BotLevelGenerator
/// <returns>IRandomisedBotLevelResult object</returns>
public RandomisedBotLevelResult GenerateBotLevel(MinMax levelDetails, BotGenerationDetails botGenerationDetails, BotBase bot)
{
throw new NotImplementedException();
var expTable = _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable;
var botLevelRange = GetRelativeBotLevelRange(botGenerationDetails, levelDetails, expTable.Length);
// Get random level based on the exp table.
int exp = 0;
var level = int.Parse(ChooseBotLevel(botLevelRange.Min.Value, botLevelRange.Max.Value, 1, 1.15)
.ToString()); // TODO - nasty double to string to int conversion
for (var i = 0; i < level; i++)
{
exp += expTable[i].Experience.Value;
}
// Sprinkle in some random exp within the level, unless we are at max level.
if (level < expTable.Length - 1)
{
exp += _randomUtil.GetInt(0, expTable[level].Experience.Value - 1);
}
return new RandomisedBotLevelResult { Level = level, Exp = exp };
}
public int ChooseBotLevel(int min, int max, int shift, int number)
public double ChooseBotLevel(double min, double max, int shift, double number)
{
throw new NotImplementedException();
return _randomUtil.GetBiasedRandomNumber(min, max, shift, number);
}
/// <summary>
@@ -39,6 +73,35 @@ public class BotLevelGenerator
/// <returns>A MinMax of the lowest and highest level to generate the bots</returns>
public MinMax GetRelativeBotLevelRange(BotGenerationDetails botGenerationDetails, MinMax levelDetails, int maxAvailableLevel)
{
throw new NotImplementedException();
var isPmc = botGenerationDetails.IsPmc.GetValueOrDefault(false);
var pmcOverride = botGenerationDetails.LocationSpecificPmcLevelOverride;
var minPossibleLevel = isPmc && pmcOverride is not null
? Math.Min(
Math.Max(levelDetails.Min.Value, pmcOverride.Min.Value), // Biggest between json min and the botgen min
maxAvailableLevel // Fallback if value above is crazy (default is 79)
)
: Math.Min(levelDetails.Min.Value, maxAvailableLevel); // Not pmc with override or non-pmc
var maxPossibleLevel = isPmc && pmcOverride is not null
? Math.Min(pmcOverride.Max.Value, maxAvailableLevel) // Was a PMC and they have a level override
: Math.Min(levelDetails.Max.Value, maxAvailableLevel); // Not pmc with override or non-pmc
var minLevel = botGenerationDetails.PlayerLevel.HasValue
? botGenerationDetails.PlayerLevel.Value
: 0 - botGenerationDetails.BotRelativeLevelDeltaMin.Value;
var maxLevel = botGenerationDetails.PlayerLevel.HasValue
? botGenerationDetails.PlayerLevel.Value
: 0 + botGenerationDetails.BotRelativeLevelDeltaMin.Value;
// Bound the level to the min/max possible
maxLevel = Math.Min(Math.Max(maxLevel, minPossibleLevel), maxPossibleLevel);
minLevel = Math.Min(Math.Max(minLevel, minPossibleLevel), maxPossibleLevel);
return new MinMax
{
Min = minLevel,
Max = maxLevel,
};
}
}
+64 -15
View File
@@ -16,19 +16,25 @@ public class WeatherGenerator
private readonly SeasonalEventService _seasonalEventService;
private readonly WeatherHelper _weatherHelper;
private readonly ConfigServer _configServer;
private readonly WeightedRandomHelper _weightedRandomHelper;
private readonly RandomUtil _randomUtil;
private readonly WeatherConfig _weatherConfig;
public WeatherGenerator(
TimeUtil timeUtil,
SeasonalEventService seasonalEventService,
WeatherHelper weatherHelper,
ConfigServer configServer)
ConfigServer configServer,
WeightedRandomHelper weightedRandomHelper,
RandomUtil randomUtil
)
{
_timeUtil = timeUtil;
_seasonalEventService = seasonalEventService;
_weatherHelper = weatherHelper;
_configServer = configServer;
_weightedRandomHelper = weightedRandomHelper;
_randomUtil = randomUtil;
_weatherConfig = _configServer.GetConfig<WeatherConfig>();
}
@@ -81,7 +87,36 @@ public class WeatherGenerator
*/
public Weather GenerateWeather(Season currentSeason, long? timestamp = null)
{
throw new NotImplementedException();
var weatherValues = GetWeatherValuesBySeason(currentSeason);
var clouds = GetWeightedClouds(weatherValues);
// Force rain to off if no clouds
var rain = clouds <= 0.6 ? 0 : GetWeightedRain(weatherValues);
// TODO: Ensure Weather settings match Ts Server GetRandomDouble produces a decimal value way higher than ts server
var result = new Weather
{
Pressure = GetRandomDouble(weatherValues.Pressure.Min ?? 0, weatherValues.Pressure.Max ?? 0),
Temperature = 0,
Fog = GetWeightedFog(weatherValues),
RainIntensity =
rain > 1 ? GetRandomDouble(weatherValues.RainIntensity.Min ?? 0, weatherValues.RainIntensity.Max ?? 0) : 0,
Rain = rain,
WindGustiness = GetRandomDouble(weatherValues.WindGustiness.Min ?? 0, weatherValues.WindGustiness.Max ?? 0, 2),
WindDirection = GetWeightedWindDirection(weatherValues),
WindSpeed = GetWeightedWindSpeed(weatherValues),
Cloud = clouds,
Time = "",
Date = "",
Timestamp = 0,
SptInRaidTimestamp = 0
};
SetCurrentDateTime(result, timestamp);
result.Temperature = GetRaidTemperature(weatherValues, result.SptInRaidTimestamp ?? 0);
return result;
}
protected SeasonalValues GetWeatherValuesBySeason(Season currentSeason)
@@ -92,7 +127,7 @@ public class WeatherGenerator
return this._weatherConfig.Weather.SeasonValues["default"];
}
return value;
return value!;
}
/**
@@ -101,9 +136,15 @@ public class WeatherGenerator
* @param inRaidTimestamp What time is the raid running at
* @returns Timestamp
*/
protected double GetRaidTemperature(SeasonalValues weather, int inRaidTimestamp)
protected double GetRaidTemperature(SeasonalValues weather, long inRaidTimestamp)
{
throw new NotImplementedException();
// Convert timestamp to date so we can get current hour and check if its day or night
var currentRaidTime = new DateTime(inRaidTimestamp);
var minMax = _weatherHelper.IsHourAtNightTime(currentRaidTime.Hour)
? weather.Temp.Night
: weather.Temp.Day;
return Math.Round(_randomUtil.GetDouble(minMax.Min ?? 0, minMax.Max ?? 0), 2);
}
/**
@@ -111,38 +152,46 @@ public class WeatherGenerator
* @param weather Object to update
* @param timestamp OPTIONAL, define timestamp used
*/
protected void SetCurrentDateTime(Weather weather, int? timestamp = null)
protected void SetCurrentDateTime(Weather weather, long? timestamp = null)
{
throw new NotImplementedException();
var inRaidTime = _weatherHelper.GetInRaidTime(timestamp);
var normalTime = GetBsgFormattedTime(inRaidTime);
var formattedDate = _timeUtil.FormatDate(timestamp.HasValue ? _timeUtil.GetDateTimeFromTimeStamp(timestamp.Value) : DateTime.UtcNow);
var datetimeBsgFormat = $"{formattedDate} {normalTime}";
weather.Timestamp = timestamp ?? _timeUtil.GetTimeStampFromEpoch(inRaidTime); // matches weather.date We use to divide by 1000
weather.Date = formattedDate; // matches weather.timestamp
weather.Time = datetimeBsgFormat; // matches weather.timestamp
weather.SptInRaidTimestamp = _timeUtil.GetTimeStampFromEpoch(inRaidTime);
}
protected WindDirection GetWeightedWindDirection(SeasonalValues weather)
{
throw new NotImplementedException();
return _weightedRandomHelper.WeightedRandom(weather.WindDirection.Values, weather.WindDirection.Weights).Item;
}
protected double GetWeightedClouds(SeasonalValues weather)
{
throw new NotImplementedException();
return _weightedRandomHelper.WeightedRandom(weather.Clouds.Values, weather.Clouds.Weights).Item;
}
protected double GetWeightedWindSpeed(SeasonalValues weather)
{
throw new NotImplementedException();
return _weightedRandomHelper.WeightedRandom(weather.WindSpeed.Values, weather.WindSpeed.Weights).Item;
}
protected double GetWeightedFog(SeasonalValues weather)
{
throw new NotImplementedException();
return _weightedRandomHelper.WeightedRandom(weather.Fog.Values, weather.Fog.Weights).Item;
}
protected double GetWeightedRain(SeasonalValues weather)
{
throw new NotImplementedException();
return _weightedRandomHelper.WeightedRandom(weather.Rain.Values, weather.Rain.Weights).Item;
}
protected double GetRandomFloat(double min, double max, int precision = 3)
protected double GetRandomDouble(double min, double max, int precision = 3)
{
throw new NotImplementedException();
return Math.Round(_randomUtil.GetDouble(min, max), precision);
}
}