This is just Jetbrains formatting and code syntax styling
This commit is contained in:
@@ -3,74 +3,73 @@ using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Servers;
|
||||
|
||||
namespace ExampleMods.Mods
|
||||
namespace ExampleMods.Mods;
|
||||
|
||||
[Injectable]
|
||||
public class EditConfigs
|
||||
{
|
||||
[Injectable]
|
||||
public class EditConfigs
|
||||
private readonly AirdropConfig _airdropConfig;
|
||||
private readonly BotConfig _botConfig;
|
||||
private readonly ConfigServer _configServer;
|
||||
private readonly HideoutConfig _hideoutConfig;
|
||||
private readonly PmcChatResponse _pmcChatResponseConfig;
|
||||
private readonly PmcConfig _pmcConfig;
|
||||
private readonly QuestConfig _questConfig;
|
||||
private readonly WeatherConfig _weatherConfig;
|
||||
|
||||
// We access configs via ConfigServer
|
||||
public EditConfigs(
|
||||
ConfigServer configServer)
|
||||
{
|
||||
private readonly ConfigServer _configServer;
|
||||
private readonly BotConfig _botConfig;
|
||||
private readonly HideoutConfig _hideoutConfig;
|
||||
private readonly WeatherConfig _weatherConfig;
|
||||
private readonly AirdropConfig _airdropConfig;
|
||||
private readonly PmcChatResponse _pmcChatResponseConfig;
|
||||
private readonly QuestConfig _questConfig;
|
||||
private readonly PmcConfig _pmcConfig;
|
||||
_configServer = configServer;
|
||||
|
||||
// We access configs via ConfigServer
|
||||
public EditConfigs(
|
||||
ConfigServer configServer)
|
||||
{
|
||||
_configServer = configServer;
|
||||
// We get the bot config by calling GetConfig and passing the configs 'type' within the diamond brackets
|
||||
_botConfig = _configServer.GetConfig<BotConfig>();
|
||||
_hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
_weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
_airdropConfig = _configServer.GetConfig<AirdropConfig>();
|
||||
_pmcChatResponseConfig = _configServer.GetConfig<PmcChatResponse>();
|
||||
_questConfig = _configServer.GetConfig<QuestConfig>();
|
||||
_pmcConfig = _configServer.GetConfig<PmcConfig>();
|
||||
|
||||
// We get the bot config by calling GetConfig and passing the configs 'type' within the diamond brackets
|
||||
_botConfig = _configServer.GetConfig<BotConfig>();
|
||||
_hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
_weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
_airdropConfig = _configServer.GetConfig<AirdropConfig>();
|
||||
_pmcChatResponseConfig = _configServer.GetConfig<PmcChatResponse>();
|
||||
_questConfig = _configServer.GetConfig<QuestConfig>();
|
||||
_pmcConfig = _configServer.GetConfig<PmcConfig>();
|
||||
Run();
|
||||
}
|
||||
|
||||
Run();
|
||||
}
|
||||
public void Run()
|
||||
{
|
||||
// Let's edit the weather config to make the season winter
|
||||
_weatherConfig.OverrideSeason = Season.WINTER;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
// Let's edit the weather config to make the season winter
|
||||
_weatherConfig.OverrideSeason = Season.WINTER;
|
||||
// Let's edit the hideout config to Make all crafts take 60 seconds
|
||||
_hideoutConfig.OverrideCraftTimeSeconds = 60;
|
||||
|
||||
// Let's edit the hideout config to Make all crafts take 60 seconds
|
||||
_hideoutConfig.OverrideCraftTimeSeconds = 60;
|
||||
// Let's edit the hideout config to Make all upgrades take 60 seconds
|
||||
_hideoutConfig.OverrideBuildTimeSeconds = 60;
|
||||
|
||||
// Let's edit the hideout config to Make all upgrades take 60 seconds
|
||||
_hideoutConfig.OverrideBuildTimeSeconds = 60;
|
||||
// Let's edit the airdrop config to Make weapon/armor drops really common
|
||||
_airdropConfig.AirdropTypeWeightings[SptAirdropTypeEnum.weaponArmor] = 999;
|
||||
|
||||
// Let's edit the airdrop config to Make weapon/armor drops really common
|
||||
_airdropConfig.AirdropTypeWeightings[SptAirdropTypeEnum.weaponArmor] = 999;
|
||||
// Let's edit the airdrop config to Make weapon/armor drops always have 3 sealed weapon crates
|
||||
var weaponCrateMinMax = _airdropConfig.Loot["weaponArmor"].WeaponCrateCount;
|
||||
weaponCrateMinMax.Min = 3;
|
||||
weaponCrateMinMax.Max = 3;
|
||||
|
||||
// Let's edit the airdrop config to Make weapon/armor drops always have 3 sealed weapon crates
|
||||
var weaponCrateMinMax = _airdropConfig.Loot["weaponArmor"].WeaponCrateCount;
|
||||
weaponCrateMinMax.Min = 3;
|
||||
weaponCrateMinMax.Max = 3;
|
||||
// Let's make PMCs always mail you when they kill you
|
||||
_pmcChatResponseConfig.Killer.ResponseChancePercent = 100;
|
||||
|
||||
// Let's make PMCs always mail you when they kill you
|
||||
_pmcChatResponseConfig.Killer.ResponseChancePercent = 100;
|
||||
// Let's make quest rewards sent to you via mail last for over a week if you have an unheard profile
|
||||
_questConfig.MailRedeemTimeHours["unheard_edition"] = 168;
|
||||
|
||||
// Let's make quest rewards sent to you via mail last for over a week if you have an unheard profile
|
||||
_questConfig.MailRedeemTimeHours["unheard_edition"] = 168;
|
||||
// Let's make the interchange bot cap huge
|
||||
_botConfig.MaxBotCap["interchange"] = 50;
|
||||
|
||||
// Let's make the interchange bot cap huge
|
||||
_botConfig.MaxBotCap["interchange"] = 50;
|
||||
// Let's disable loot on scavs
|
||||
_botConfig.DisableLootOnBotTypes.Add("assault");
|
||||
|
||||
// Let's disable loot on scavs
|
||||
_botConfig.DisableLootOnBotTypes.Add("assault");
|
||||
|
||||
// Let's make the conversion rate of scavs to pmcs 100% on factory day
|
||||
var factory4DayConversionSettings = _pmcConfig.ConvertIntoPmcChance["factory4_day"];
|
||||
var assaultConversionSettings = factory4DayConversionSettings["assault"];
|
||||
assaultConversionSettings.Min = 100;
|
||||
assaultConversionSettings.Max = 100;
|
||||
}
|
||||
// Let's make the conversion rate of scavs to pmcs 100% on factory day
|
||||
var factory4DayConversionSettings = _pmcConfig.ConvertIntoPmcChance["factory4_day"];
|
||||
var assaultConversionSettings = factory4DayConversionSettings["assault"];
|
||||
assaultConversionSettings.Min = 100;
|
||||
assaultConversionSettings.Max = 100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,197 +4,186 @@ using Core.Models.Enums;
|
||||
using Core.Models.Utils;
|
||||
using Core.Services;
|
||||
|
||||
namespace ExampleMods.Mods
|
||||
namespace ExampleMods.Mods;
|
||||
|
||||
[Injectable]
|
||||
public class EditDatabaseValues
|
||||
{
|
||||
[Injectable]
|
||||
public class EditDatabaseValues
|
||||
private readonly DatabaseService _databaseService;
|
||||
|
||||
public EditDatabaseValues(
|
||||
DatabaseService databaseService)
|
||||
{
|
||||
private readonly DatabaseService _databaseService;
|
||||
_databaseService = databaseService;
|
||||
|
||||
public EditDatabaseValues(
|
||||
DatabaseService databaseService)
|
||||
Run();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
// When SPT starts, it stores all the data found in (SPT_Data\Server\database) in memory
|
||||
// We can use the '_databaseService' we injected to access this data, this includes files from EFT and SPT
|
||||
|
||||
// Lets edit some globals settings to make the game easier
|
||||
EditGlobals();
|
||||
|
||||
// Lets edit the BTR to have the christmas tarcola skin
|
||||
EditBtr();
|
||||
|
||||
// Let's edit the hideout so it's easier to upgrade the lavatory
|
||||
EditHideout();
|
||||
|
||||
// Lets edit the default scav to
|
||||
EditScavSettings();
|
||||
|
||||
// Lets edit Customs
|
||||
EditCustoms();
|
||||
}
|
||||
|
||||
private void EditGlobals()
|
||||
{
|
||||
// Let's edit settings in the GLOBALS file (database/globals.json)
|
||||
var globals = _databaseService.GetGlobals();
|
||||
|
||||
// Let's edit the scav cooldown to be 1 second
|
||||
globals.Configuration.SavagePlayCooldown = 1;
|
||||
|
||||
|
||||
// Now lets try editing the ragfair unlock level, lets get the ragfair settings first
|
||||
var ragfairSettings = globals.Configuration.RagFair;
|
||||
|
||||
// Lets set the level you need to be to access flea to be 1
|
||||
ragfairSettings.MinUserLevel = 1;
|
||||
|
||||
|
||||
// Now lets increase the number of offers you can have listed at one time
|
||||
// The max is stored in a list, different flea ratings give different offer amounts
|
||||
|
||||
// We loop over all the settings, setting all of them to be 20
|
||||
foreach (var offerCountSettings in ragfairSettings.MaxActiveOfferCount) offerCountSettings.Count = 20;
|
||||
}
|
||||
|
||||
private void EditBtr()
|
||||
{
|
||||
// BTR setting can be found in the GLOBALS file too
|
||||
var globals = _databaseService.GetGlobals();
|
||||
|
||||
// We get the BTR settings from globals first
|
||||
var btrSettings = globals.Configuration.BTRSettings;
|
||||
|
||||
// Let's get the settings for woods specifically, we use 'tryGetValue' for this, the settings will be stored in 'woodsBtrSettings'
|
||||
btrSettings.MapsConfigs.TryGetValue("Woods", out var woodsBtrSettings);
|
||||
|
||||
// Lets set the BTR to use the christmas skin
|
||||
woodsBtrSettings.BtrSkin = "Tarcola";
|
||||
}
|
||||
|
||||
private void EditHideout()
|
||||
{
|
||||
// Hideout data can be found in (SPT_Data\Server\database\hideout)
|
||||
var hideout = _databaseService.GetHideout();
|
||||
|
||||
// We want the areas, they're stored in a list
|
||||
var hideoutAreas = hideout.Areas;
|
||||
|
||||
// We find the toilet, we use 'firstOrDefault', if we cant find the lavatory, 'lavatoryArea' will be null
|
||||
var lavatoryArea = hideoutAreas.FirstOrDefault(area => area.Type == HideoutAreas.LAVATORY);
|
||||
|
||||
|
||||
// Now we have the toilet, we can find the requirements to craft, all data is stored by stage
|
||||
var toiletStages = lavatoryArea.Stages;
|
||||
|
||||
// Stages are stored in a dictionary, a dictionary has a 'key' and a 'value'
|
||||
// In this case, the 'key' is the upgrade stage, e.g. "1", or "2"
|
||||
// We reference to each stage as a 'stageKvP' this means 'Key value Pair', every key has a value (key = stage number, value = data for that stage)
|
||||
foreach (var stageKvP in toiletStages)
|
||||
{
|
||||
_databaseService = databaseService;
|
||||
// while we're here, we can make the stages craft really fast (60 seconds)
|
||||
stageKvP.Value.ConstructionTime = 60;
|
||||
|
||||
Run();
|
||||
}
|
||||
// Let's get the stage requirements, they're a list
|
||||
var stageRequirements = stageKvP.Value.Requirements;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
// When SPT starts, it stores all the data found in (SPT_Data\Server\database) in memory
|
||||
// We can use the '_databaseService' we injected to access this data, this includes files from EFT and SPT
|
||||
|
||||
// Lets edit some globals settings to make the game easier
|
||||
EditGlobals();
|
||||
|
||||
// Lets edit the BTR to have the christmas tarcola skin
|
||||
EditBtr();
|
||||
|
||||
// Let's edit the hideout so it's easier to upgrade the lavatory
|
||||
EditHideout();
|
||||
|
||||
// Lets edit the default scav to
|
||||
EditScavSettings();
|
||||
|
||||
// Lets edit Customs
|
||||
EditCustoms();
|
||||
}
|
||||
|
||||
private void EditGlobals()
|
||||
{
|
||||
// Let's edit settings in the GLOBALS file (database/globals.json)
|
||||
var globals = _databaseService.GetGlobals();
|
||||
|
||||
// Let's edit the scav cooldown to be 1 second
|
||||
globals.Configuration.SavagePlayCooldown = 1;
|
||||
|
||||
|
||||
// Now lets try editing the ragfair unlock level, lets get the ragfair settings first
|
||||
var ragfairSettings = globals.Configuration.RagFair;
|
||||
|
||||
// Lets set the level you need to be to access flea to be 1
|
||||
ragfairSettings.MinUserLevel = 1;
|
||||
|
||||
|
||||
// Now lets increase the number of offers you can have listed at one time
|
||||
// The max is stored in a list, different flea ratings give different offer amounts
|
||||
|
||||
// We loop over all the settings, setting all of them to be 20
|
||||
foreach (var offerCountSettings in ragfairSettings.MaxActiveOfferCount)
|
||||
{
|
||||
offerCountSettings.Count = 20;
|
||||
}
|
||||
}
|
||||
|
||||
private void EditBtr()
|
||||
{
|
||||
// BTR setting can be found in the GLOBALS file too
|
||||
var globals = _databaseService.GetGlobals();
|
||||
|
||||
// We get the BTR settings from globals first
|
||||
var btrSettings = globals.Configuration.BTRSettings;
|
||||
|
||||
// Let's get the settings for woods specifically, we use 'tryGetValue' for this, the settings will be stored in 'woodsBtrSettings'
|
||||
btrSettings.MapsConfigs.TryGetValue("Woods", out var woodsBtrSettings);
|
||||
|
||||
// Lets set the BTR to use the christmas skin
|
||||
woodsBtrSettings.BtrSkin = "Tarcola";
|
||||
}
|
||||
|
||||
private void EditHideout()
|
||||
{
|
||||
// Hideout data can be found in (SPT_Data\Server\database\hideout)
|
||||
Core.Models.Spt.Hideout.Hideout hideout = _databaseService.GetHideout();
|
||||
|
||||
// We want the areas, they're stored in a list
|
||||
List<HideoutArea>? hideoutAreas = hideout.Areas;
|
||||
|
||||
// We find the toilet, we use 'firstOrDefault', if we cant find the lavatory, 'lavatoryArea' will be null
|
||||
HideoutArea? lavatoryArea = hideoutAreas.FirstOrDefault(area => area.Type == HideoutAreas.LAVATORY);
|
||||
|
||||
|
||||
|
||||
// Now we have the toilet, we can find the requirements to craft, all data is stored by stage
|
||||
var toiletStages = lavatoryArea.Stages;
|
||||
|
||||
// Stages are stored in a dictionary, a dictionary has a 'key' and a 'value'
|
||||
// In this case, the 'key' is the upgrade stage, e.g. "1", or "2"
|
||||
// We reference to each stage as a 'stageKvP' this means 'Key value Pair', every key has a value (key = stage number, value = data for that stage)
|
||||
foreach (var stageKvP in toiletStages)
|
||||
{
|
||||
// while we're here, we can make the stages craft really fast (60 seconds)
|
||||
stageKvP.Value.ConstructionTime = 60;
|
||||
|
||||
// Let's get the stage requirements, they're a list
|
||||
var stageRequirements = stageKvP.Value.Requirements;
|
||||
|
||||
// We empty the requirements out, now it can be built straight away
|
||||
stageRequirements.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void EditScavSettings()
|
||||
{
|
||||
var bots = _databaseService.GetBots();
|
||||
|
||||
// Same as the above example, we use 'TryGetValue' to get the 'assault' bot (assault is the internal name for scavs)
|
||||
bots.Types.TryGetValue("assault", out var assaultBot);
|
||||
|
||||
// Let's make the chance to get a good backpack really high
|
||||
assaultBot.BotInventory.Equipment.TryGetValue(EquipmentSlots.Backpack, out Dictionary<string, double> backPacks);
|
||||
|
||||
// We access the backpacks dictionary by key directly using square brackets, we use ItemTpl to get the items ID
|
||||
// Alternately, we could have typed backPacks["59e763f286f7742ee57895da"] and done the same thing, ItemTpl makes it easier to read
|
||||
backPacks[ItemTpl.BACKPACK_PILGRIM_TOURIST] = 999999;
|
||||
|
||||
|
||||
|
||||
|
||||
// Now lets make them always have an M4A1
|
||||
assaultBot.BotInventory.Equipment.TryGetValue(EquipmentSlots.FirstPrimaryWeapon, out Dictionary<string, double> primaryWeapons);
|
||||
|
||||
// We edit the weight value (pick chance) that is already there to be massive, making the item more likely to be picked
|
||||
primaryWeapons[ItemTpl.ASSAULTRIFLE_COLT_M4A1_556X45_ASSAULT_RIFLE] = 999999;
|
||||
|
||||
|
||||
|
||||
|
||||
// Now lets make them always have the first name of Gary
|
||||
// We start by removing all the existing names
|
||||
assaultBot.FirstNames.Clear();
|
||||
|
||||
// We add the new name Gary, very menacing
|
||||
assaultBot.FirstNames.Add("Gary");
|
||||
}
|
||||
|
||||
private void EditCustoms()
|
||||
{
|
||||
// Let's get all the maps (called locations)
|
||||
var locations = _databaseService.GetLocations();
|
||||
|
||||
// Customs is called 'bigmap' in eft
|
||||
var customs = locations.Bigmap;
|
||||
|
||||
// Lets get the exits and make them all 100% chance to appear
|
||||
var exits = customs.Base.Exits;
|
||||
|
||||
// They're stored as a list so we can loop over them
|
||||
foreach (var exit in exits)
|
||||
{
|
||||
// I can't remember which one is used, you'd assume ChancePVE is used in pve, but this is BSG we're dealing with
|
||||
// So we set both
|
||||
exit.Chance = 100;
|
||||
exit.ChancePVE = 100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Lets try editing the airdrops on customs to be better
|
||||
var airdropSettings = customs.Base.AirdropParameters;
|
||||
|
||||
// They're stored in an array but there's only one bunch of settings, it means we have to get the first item from the list,
|
||||
// An alternate way to access the first item is done by using square brackets with the 'index' of the item we want,
|
||||
// indexes start at 0 so we want to type "[0]" to access the first item in the list,
|
||||
var actualAirdropSettings = airdropSettings.First();
|
||||
|
||||
// Make it spawn 100%
|
||||
actualAirdropSettings.PlaneAirdropChance = 1; // Number between 0 and 1
|
||||
|
||||
// Make it spawn as early as start of raid
|
||||
actualAirdropSettings.PlaneAirdropStartMin = 1;
|
||||
|
||||
|
||||
|
||||
// Let's make bosses spawn 100% of the time
|
||||
|
||||
// We get all the bosses, they're stored in a list
|
||||
var bosses = customs.Base.BossLocationSpawn;
|
||||
|
||||
// Let's get Reshala, we use "FirstOrDefault" and look for the first boss with the name "bossBully"
|
||||
var reshala = bosses.FirstOrDefault(boss => boss.BossName == "bossBully");
|
||||
|
||||
// Set him to 100%
|
||||
reshala.BossChance = 100;
|
||||
// We empty the requirements out, now it can be built straight away
|
||||
stageRequirements.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void EditScavSettings()
|
||||
{
|
||||
var bots = _databaseService.GetBots();
|
||||
|
||||
// Same as the above example, we use 'TryGetValue' to get the 'assault' bot (assault is the internal name for scavs)
|
||||
bots.Types.TryGetValue("assault", out var assaultBot);
|
||||
|
||||
// Let's make the chance to get a good backpack really high
|
||||
assaultBot.BotInventory.Equipment.TryGetValue(EquipmentSlots.Backpack, out var backPacks);
|
||||
|
||||
// We access the backpacks dictionary by key directly using square brackets, we use ItemTpl to get the items ID
|
||||
// Alternately, we could have typed backPacks["59e763f286f7742ee57895da"] and done the same thing, ItemTpl makes it easier to read
|
||||
backPacks[ItemTpl.BACKPACK_PILGRIM_TOURIST] = 999999;
|
||||
|
||||
|
||||
// Now lets make them always have an M4A1
|
||||
assaultBot.BotInventory.Equipment.TryGetValue(EquipmentSlots.FirstPrimaryWeapon, out var primaryWeapons);
|
||||
|
||||
// We edit the weight value (pick chance) that is already there to be massive, making the item more likely to be picked
|
||||
primaryWeapons[ItemTpl.ASSAULTRIFLE_COLT_M4A1_556X45_ASSAULT_RIFLE] = 999999;
|
||||
|
||||
|
||||
// Now lets make them always have the first name of Gary
|
||||
// We start by removing all the existing names
|
||||
assaultBot.FirstNames.Clear();
|
||||
|
||||
// We add the new name Gary, very menacing
|
||||
assaultBot.FirstNames.Add("Gary");
|
||||
}
|
||||
|
||||
private void EditCustoms()
|
||||
{
|
||||
// Let's get all the maps (called locations)
|
||||
var locations = _databaseService.GetLocations();
|
||||
|
||||
// Customs is called 'bigmap' in eft
|
||||
var customs = locations.Bigmap;
|
||||
|
||||
// Lets get the exits and make them all 100% chance to appear
|
||||
var exits = customs.Base.Exits;
|
||||
|
||||
// They're stored as a list so we can loop over them
|
||||
foreach (var exit in exits)
|
||||
{
|
||||
// I can't remember which one is used, you'd assume ChancePVE is used in pve, but this is BSG we're dealing with
|
||||
// So we set both
|
||||
exit.Chance = 100;
|
||||
exit.ChancePVE = 100;
|
||||
}
|
||||
|
||||
|
||||
// Lets try editing the airdrops on customs to be better
|
||||
var airdropSettings = customs.Base.AirdropParameters;
|
||||
|
||||
// They're stored in an array but there's only one bunch of settings, it means we have to get the first item from the list,
|
||||
// An alternate way to access the first item is done by using square brackets with the 'index' of the item we want,
|
||||
// indexes start at 0 so we want to type "[0]" to access the first item in the list,
|
||||
var actualAirdropSettings = airdropSettings.First();
|
||||
|
||||
// Make it spawn 100%
|
||||
actualAirdropSettings.PlaneAirdropChance = 1; // Number between 0 and 1
|
||||
|
||||
// Make it spawn as early as start of raid
|
||||
actualAirdropSettings.PlaneAirdropStartMin = 1;
|
||||
|
||||
|
||||
// Let's make bosses spawn 100% of the time
|
||||
|
||||
// We get all the bosses, they're stored in a list
|
||||
var bosses = customs.Base.BossLocationSpawn;
|
||||
|
||||
// Let's get Reshala, we use "FirstOrDefault" and look for the first boss with the name "bossBully"
|
||||
var reshala = bosses.FirstOrDefault(boss => boss.BossName == "bossBully");
|
||||
|
||||
// Set him to 100%
|
||||
reshala.BossChance = 100;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-26
@@ -7,36 +7,35 @@ using SptCommon.Annotations;
|
||||
using Core.Models.Logging;
|
||||
using Core.Models.Utils;
|
||||
|
||||
namespace ExampleMods.Mods
|
||||
namespace ExampleMods.Mods;
|
||||
|
||||
[Injectable]
|
||||
public class Logging
|
||||
{
|
||||
[Injectable]
|
||||
public class Logging
|
||||
private readonly ISptLogger<Logging> _logger;
|
||||
|
||||
// Constructor - Inject a 'ISptLogger' with your mods Class in the diamond brackets
|
||||
public Logging(
|
||||
ISptLogger<Logging> logger)
|
||||
{
|
||||
private readonly ISptLogger<Logging> _logger;
|
||||
// Save the logger we're injecting into a private variable that is scoped to this class (only this class has access to it)
|
||||
_logger = logger;
|
||||
|
||||
// Constructor - Inject a 'ISptLogger' with your mods Class in the diamond brackets
|
||||
public Logging(
|
||||
ISptLogger<Logging> logger)
|
||||
{
|
||||
// Save the logger we're injecting into a private variable that is scoped to this class (only this class has access to it)
|
||||
_logger = logger;
|
||||
// Not 100% necessary but let's split our code out into a method to make it easier to read
|
||||
Run();
|
||||
}
|
||||
|
||||
// Not 100% necessary but let's split our code out into a method to make it easier to read
|
||||
Run();
|
||||
}
|
||||
public void Run()
|
||||
{
|
||||
// We can access the logger to assigned in the constructor here
|
||||
_logger.Success("This is a success message");
|
||||
_logger.Warning("This is a warning message");
|
||||
_logger.Error("This is an error message");
|
||||
_logger.Info("This is an info message");
|
||||
_logger.Critical("this is a critical message");
|
||||
|
||||
public void Run()
|
||||
{
|
||||
// We can access the logger to assigned in the constructor here
|
||||
_logger.Success("This is a success message");
|
||||
_logger.Warning("This is a warning message");
|
||||
_logger.Error("This is an error message");
|
||||
_logger.Info("This is an info message");
|
||||
_logger.Critical("this is a critical message");
|
||||
|
||||
// Logging with colors requires you to 'pass' the text color and background color
|
||||
_logger.LogWithColor("This is a message with custom colors", LogTextColor.Red, LogBackgroundColor.Black);
|
||||
_logger.Debug("This is a debug message that gets written to the log file, not the console");
|
||||
}
|
||||
// Logging with colors requires you to 'pass' the text color and background color
|
||||
_logger.LogWithColor("This is a message with custom colors", LogTextColor.Red, LogBackgroundColor.Black);
|
||||
_logger.Debug("This is a debug message that gets written to the log file, not the console");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,12 @@ public class WatermarkOverride(
|
||||
ConfigServer _configServer,
|
||||
LocalisationService _localisationService,
|
||||
WatermarkLocale _watermarkLocale
|
||||
) : Watermark(_logger, _configServer, _localisationService, _watermarkLocale) // was testing overriding with primary constructors, works fine from what i can see
|
||||
) : Watermark(
|
||||
_logger,
|
||||
_configServer,
|
||||
_localisationService,
|
||||
_watermarkLocale
|
||||
) // was testing overriding with primary constructors, works fine from what i can see
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -6,8 +6,7 @@ using Core.Utils;
|
||||
namespace Core.Callbacks;
|
||||
|
||||
[Injectable(InjectableTypeOverride = typeof(AchievementCallbacks))]
|
||||
public class AchievementCallbacks
|
||||
(
|
||||
public class AchievementCallbacks(
|
||||
AchievementController _achievementController,
|
||||
HttpResponseUtil _httpResponseUtil
|
||||
)
|
||||
|
||||
@@ -16,9 +16,8 @@ public class ClientLogCallbacks(
|
||||
ConfigServer _configServer,
|
||||
LocalisationService _localisationService
|
||||
// ModLoadOrder _modLoadOrder // TODO: needs implementing
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Handle /singleplayer/log
|
||||
/// </summary>
|
||||
@@ -39,11 +38,11 @@ public class ClientLogCallbacks(
|
||||
public string ReleaseNotes()
|
||||
{
|
||||
var data = _configServer.GetConfig<CoreConfig>().Release;
|
||||
|
||||
|
||||
data.BetaDisclaimerText = ProgramStatics.MODS()
|
||||
? _localisationService.GetText("release-beta-disclaimer-mods-enabled")
|
||||
? _localisationService.GetText("release-beta-disclaimer-mods-enabled")
|
||||
: _localisationService.GetText("release-beta-disclaimer");
|
||||
|
||||
|
||||
data.BetaDisclaimerAcceptText = _localisationService.GetText("release-beta-disclaimer-accept");
|
||||
data.ServerModsLoadedText = _localisationService.GetText("release-server-mods-loaded");
|
||||
data.ServerModsLoadedDebugText = _localisationService.GetText("release-server-mods-debug-message");
|
||||
@@ -55,7 +54,7 @@ public class ClientLogCallbacks(
|
||||
data.IsBeta = ProgramStatics.ENTRY_TYPE() == EntryType.BLEEDING_EDGE || ProgramStatics.ENTRY_TYPE() == EntryType.BLEEDING_EDGE_MODS;
|
||||
data.IsModdable = ProgramStatics.MODS();
|
||||
data.IsModded = false; // TODO
|
||||
|
||||
|
||||
return _httpResponseUtil.NoBody(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,13 +149,9 @@ public class DataCallbacks(
|
||||
{
|
||||
var localeId = url.Replace("/client/menu/locale/", "");
|
||||
var locales = _databaseService.GetLocales();
|
||||
var result = locales.Menu?[localeId]
|
||||
?? locales.Menu?.FirstOrDefault(m => m.Key == "en").Value;
|
||||
var result = locales.Menu?[localeId] ?? locales.Menu?.FirstOrDefault(m => m.Key == "en").Value;
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
throw new Exception($"Unable to determine locale for request with {localeId}");
|
||||
}
|
||||
if (result == null) throw new Exception($"Unable to determine locale for request with {localeId}");
|
||||
|
||||
return _httpResponseUtil.GetBody(result);
|
||||
}
|
||||
@@ -171,8 +167,7 @@ public class DataCallbacks(
|
||||
{
|
||||
var localeId = url.Replace("/client/locale/", "");
|
||||
var locales = _databaseService.GetLocales();
|
||||
var result = locales.Global?[localeId].Value
|
||||
?? locales.Global?.FirstOrDefault(m => m.Key == "en").Value.Value;
|
||||
var result = locales.Global?[localeId].Value ?? locales.Global?.FirstOrDefault(m => m.Key == "en").Value.Value;
|
||||
|
||||
return _httpResponseUtil.GetUnclearedBody(result);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,17 @@ public class DialogueCallbacks(
|
||||
)
|
||||
: OnUpdate
|
||||
{
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
_dialogueController.Update();
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-dialogue";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/friend/list
|
||||
/// </summary>
|
||||
@@ -41,7 +52,7 @@ public class DialogueCallbacks(
|
||||
{
|
||||
var chatServer = new List<ChatServer>
|
||||
{
|
||||
new ChatServer
|
||||
new()
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
RegistrationId = 20,
|
||||
@@ -51,7 +62,7 @@ public class DialogueCallbacks(
|
||||
VersionId = "bgkidft87ddd",
|
||||
Ip = "",
|
||||
Port = 0,
|
||||
Chats = [ new Chat { Id = "0", Members = 0 } ],
|
||||
Chats = [new Chat { Id = "0", Members = 0 }]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -315,15 +326,4 @@ public class DialogueCallbacks(
|
||||
{
|
||||
return "Not Implemented!"; // Not implemented in Node
|
||||
}
|
||||
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
_dialogueController.Update();
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-dialogue";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ public class HealthCallbacks(
|
||||
HttpResponseUtil _httpResponseUtil,
|
||||
ProfileHelper _profileHelper,
|
||||
HealthController _healthController
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Custom spt server request found in modules/QTEPatch.cs
|
||||
/// </summary>
|
||||
|
||||
@@ -14,153 +14,10 @@ namespace Core.Callbacks;
|
||||
public class HideoutCallbacks(
|
||||
HideoutController _hideoutController,
|
||||
ConfigServer _configServer
|
||||
) : OnUpdate
|
||||
) : OnUpdate
|
||||
{
|
||||
private readonly HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutUpgrade event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse Upgrade(PmcData pmcData, HideoutUpgradeRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.StartUpgrade(pmcData, request, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutUpgradeComplete event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse UpgradeComplete(PmcData pmcData, HideoutUpgradeCompleteRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.UpgradeComplete(pmcData, request, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutPutItemsInAreaSlots
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse PutItemsInAreaSlots(PmcData pmcData, HideoutPutItemInRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.PutItemsInAreaSlots(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutTakeItemsFromAreaSlots event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse TakeItemsFromAreaSlots(PmcData pmcData, HideoutTakeItemOutRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.TakeItemsFromAreaSlots(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutToggleArea event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ToggleArea(PmcData pmcData, HideoutToggleAreaRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ToggleArea(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutSingleProductionStart event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse SingleProductionStart(PmcData pmcData, HideoutSingleProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.SingleProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutScavCaseProductionStart event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ScavCaseProductionStart(PmcData pmcData, HideoutScavCaseStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ScavCaseProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutContinuousProductionStart
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ContinuousProductionStart(PmcData pmcData, HideoutContinuousProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ContinuousProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutTakeProduction event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse TakeProduction(PmcData pmcData, HideoutTakeProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.TakeProduction(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutQuickTimeEvent
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HandleQTEEvent(PmcData pmcData, HandleQTEEventRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.HandleQTEEventOutcome(sessionID, pmcData, request, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse RecordShootingRangePoints(PmcData pmcData, RecordShootingRangePoints request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.RecordShootingRangePoints(sessionID, pmcData, request);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ImproveArea(PmcData pmcData, HideoutImproveAreaRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ImproveArea(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCancelProductionCommand
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse CancelProduction(PmcData pmcData, HideoutCancelProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.CancelProduction(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCircleOfCultistProductionStart
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse CicleOfCultistProductionStart(PmcData pmcData, HideoutCircleOfCultistProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.CicleOfCultistProductionStart(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutDeleteProductionCommand
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HideoutDeleteProductionCommand(PmcData pmcData, HideoutDeleteProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.HideoutDeleteProductionCommand(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCustomizationApply
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HideoutCustomizationApplyCommand(PmcData pmcData, HideoutCustomizationApplyRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.HideoutCustomizationApply(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/game/profile/items/moving - hideoutCustomizationSetMannequinPose
|
||||
*/
|
||||
public ItemEventRouterResponse HideoutCustomizationSetMannequinPose(PmcData pmcData, HideoutCustomizationSetMannequinPoseRequest request, string sessionId) {
|
||||
return _hideoutController.HideoutCustomizationSetMannequinPose(sessionId, pmcData, request);
|
||||
}
|
||||
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
if (timeSinceLastRun > _hideoutConfig.RunIntervalSeconds)
|
||||
@@ -176,4 +33,149 @@ public class HideoutCallbacks(
|
||||
{
|
||||
return "spt-hideout";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutUpgrade event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse Upgrade(PmcData pmcData, HideoutUpgradeRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.StartUpgrade(pmcData, request, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutUpgradeComplete event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse UpgradeComplete(PmcData pmcData, HideoutUpgradeCompleteRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.UpgradeComplete(pmcData, request, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutPutItemsInAreaSlots
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse PutItemsInAreaSlots(PmcData pmcData, HideoutPutItemInRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.PutItemsInAreaSlots(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutTakeItemsFromAreaSlots event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse TakeItemsFromAreaSlots(PmcData pmcData, HideoutTakeItemOutRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.TakeItemsFromAreaSlots(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutToggleArea event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ToggleArea(PmcData pmcData, HideoutToggleAreaRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ToggleArea(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutSingleProductionStart event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse SingleProductionStart(PmcData pmcData, HideoutSingleProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.SingleProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutScavCaseProductionStart event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ScavCaseProductionStart(PmcData pmcData, HideoutScavCaseStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ScavCaseProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutContinuousProductionStart
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ContinuousProductionStart(PmcData pmcData, HideoutContinuousProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ContinuousProductionStart(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutTakeProduction event
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse TakeProduction(PmcData pmcData, HideoutTakeProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.TakeProduction(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle HideoutQuickTimeEvent
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HandleQTEEvent(PmcData pmcData, HandleQTEEventRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.HandleQTEEventOutcome(sessionID, pmcData, request, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse RecordShootingRangePoints(PmcData pmcData, RecordShootingRangePoints request, string sessionID,
|
||||
ItemEventRouterResponse output)
|
||||
{
|
||||
_hideoutController.RecordShootingRangePoints(sessionID, pmcData, request);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - RecordShootingRangePoints
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse ImproveArea(PmcData pmcData, HideoutImproveAreaRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.ImproveArea(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCancelProductionCommand
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse CancelProduction(PmcData pmcData, HideoutCancelProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.CancelProduction(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCircleOfCultistProductionStart
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse CicleOfCultistProductionStart(PmcData pmcData, HideoutCircleOfCultistProductionStartRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.CicleOfCultistProductionStart(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutDeleteProductionCommand
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HideoutDeleteProductionCommand(PmcData pmcData, HideoutDeleteProductionRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.HideoutDeleteProductionCommand(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/profile/items/moving - HideoutCustomizationApply
|
||||
/// </summary>
|
||||
public ItemEventRouterResponse HideoutCustomizationApplyCommand(PmcData pmcData, HideoutCustomizationApplyRequestData request, string sessionID)
|
||||
{
|
||||
return _hideoutController.HideoutCustomizationApply(sessionID, pmcData, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/game/profile/items/moving - hideoutCustomizationSetMannequinPose
|
||||
*/
|
||||
public ItemEventRouterResponse HideoutCustomizationSetMannequinPose(PmcData pmcData, HideoutCustomizationSetMannequinPoseRequest request, string sessionId)
|
||||
{
|
||||
return _hideoutController.HideoutCustomizationSetMannequinPose(sessionId, pmcData, request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ public class HttpCallbacks(HttpServer _httpServer, ApplicationContext _applicati
|
||||
{
|
||||
public Task OnLoad()
|
||||
{
|
||||
_httpServer.Load( _applicationContext.GetLatestValue(ContextVariableType.APP_BUILDER)?.GetValue<WebApplicationBuilder>());
|
||||
_httpServer.Load(_applicationContext.GetLatestValue(ContextVariableType.APP_BUILDER)?.GetValue<WebApplicationBuilder>());
|
||||
_applicationContext.ClearValues(ContextVariableType.APP_BUILDER);
|
||||
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Core.Callbacks;
|
||||
public class InraidCallbacks(
|
||||
InRaidController _inRaidController,
|
||||
HttpResponseUtil _httpResponseUtil
|
||||
)
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle client/location/getLocalloot
|
||||
|
||||
@@ -18,11 +18,26 @@ public class InsuranceCallbacks(
|
||||
InsuranceService _insuranceService,
|
||||
HttpResponseUtil _httpResponseUtil,
|
||||
ConfigServer _configServer
|
||||
)
|
||||
)
|
||||
: OnUpdate
|
||||
{
|
||||
private InsuranceConfig _insuranceConfig = _configServer.GetConfig<InsuranceConfig>();
|
||||
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
if (timeSinceLastRun > Math.Max(_insuranceConfig.RunIntervalSeconds, 1))
|
||||
// _insuranceController.ProcessReturn();
|
||||
// TODO: InsuranceController is not implemented rn
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-insurance";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/insurance/items/list/cost
|
||||
/// </summary>
|
||||
@@ -32,7 +47,7 @@ public class InsuranceCallbacks(
|
||||
/// <returns></returns>
|
||||
public string GetInsuranceCost(string url, GetInsuranceCostRequestData info, string sessionID)
|
||||
{
|
||||
return _httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID));
|
||||
return _httpResponseUtil.GetBody(_insuranceController.Cost(info, sessionID));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -46,21 +61,4 @@ public class InsuranceCallbacks(
|
||||
{
|
||||
return _insuranceController.Insure(pmcData, info, sessionID);
|
||||
}
|
||||
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
if (timeSinceLastRun > Math.Max(_insuranceConfig.RunIntervalSeconds, 1))
|
||||
{
|
||||
// _insuranceController.ProcessReturn();
|
||||
// TODO: InsuranceController is not implemented rn
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-insurance";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRou
|
||||
{
|
||||
public string HandleEvents(string url, ItemEventRouterRequest info, string sessionID)
|
||||
{
|
||||
var eventResponse = _itemEventRouter.HandleEvents(info, sessionID);
|
||||
var result = IsCriticalError(eventResponse.Warnings)
|
||||
? _httpResponseUtil.GetBody(eventResponse, GetErrorCode(eventResponse.Warnings), eventResponse.Warnings[0].ErrorMessage)
|
||||
: _httpResponseUtil.GetBody(eventResponse);
|
||||
var eventResponse = _itemEventRouter.HandleEvents(info, sessionID);
|
||||
var result = IsCriticalError(eventResponse.Warnings)
|
||||
? _httpResponseUtil.GetBody(eventResponse, GetErrorCode(eventResponse.Warnings), eventResponse.Warnings[0].ErrorMessage)
|
||||
: _httpResponseUtil.GetBody(eventResponse);
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -26,11 +26,8 @@ public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRou
|
||||
/// <returns></returns>
|
||||
public bool IsCriticalError(List<Warning>? warnings)
|
||||
{
|
||||
if (warnings is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (warnings is null) return false;
|
||||
|
||||
// List of non-critical error codes, we return true if any error NOT included is passed in
|
||||
var nonCriticalErrorCodes = new List<BackendErrorCodes> { BackendErrorCodes.NotEnoughSpace };
|
||||
|
||||
@@ -38,19 +35,22 @@ public class ItemEventCallbacks(HttpResponseUtil _httpResponseUtil, ItemEventRou
|
||||
{
|
||||
if (!Enum.TryParse(warning.Code, out BackendErrorCodes code))
|
||||
throw new Exception($"Unable to parse [{warning.Code}] to BackendErrorCode.");
|
||||
|
||||
|
||||
if (!nonCriticalErrorCodes.Contains(code))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetErrorCode(List<Warning> warnings)
|
||||
{
|
||||
// Cast int to string to get the error code of 220 for Unknown Error.
|
||||
return int.Parse((warnings[0].Code is null || warnings[0].Code == "None"
|
||||
? ((int) BackendErrorCodes.UnknownError).ToString()
|
||||
: warnings.FirstOrDefault()?.Code) ?? string.Empty);
|
||||
return int.Parse(
|
||||
(warnings[0].Code is null || warnings[0].Code == "None"
|
||||
? ((int)BackendErrorCodes.UnknownError).ToString()
|
||||
: warnings.FirstOrDefault()?.Code) ??
|
||||
string.Empty
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ public class LauncherV2Callbacks(
|
||||
{
|
||||
public string Ping()
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2PingResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2PingResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Ping()
|
||||
}
|
||||
@@ -24,15 +25,18 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string Types()
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2TypesResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Types()
|
||||
});
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2TypesResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Types()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public string Login(LoginRequestData info)
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2LoginResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2LoginResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Login(info)
|
||||
}
|
||||
@@ -41,7 +45,8 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string Register(RegisterData info)
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2RegisterResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2RegisterResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Register(info),
|
||||
Profiles = _profileController.GetMiniProfiles()
|
||||
@@ -51,17 +56,19 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string PasswordChange(ChangeRequestData info)
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2PasswordChangeResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2PasswordChangeResponse
|
||||
{
|
||||
Response = _launcherV2Controller.PasswordChange(info),
|
||||
Profiles = _profileController.GetMiniProfiles()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public string Remove(LoginRequestData info)
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2RemoveResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2RemoveResponse
|
||||
{
|
||||
Response = _launcherV2Controller.Remove(info),
|
||||
Profiles = _profileController.GetMiniProfiles()
|
||||
@@ -71,7 +78,8 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string CompatibleVersion()
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2VersionResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2VersionResponse
|
||||
{
|
||||
Response = new LauncherV2CompatibleVersion
|
||||
{
|
||||
@@ -84,7 +92,8 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string Mods()
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2ModsResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2ModsResponse
|
||||
{
|
||||
Response = _launcherV2Controller.LoadedMods()
|
||||
}
|
||||
@@ -93,7 +102,8 @@ public class LauncherV2Callbacks(
|
||||
|
||||
public string Profiles()
|
||||
{
|
||||
return _httpResponseUtil.NoBody(new LauncherV2ProfilesResponse
|
||||
return _httpResponseUtil.NoBody(
|
||||
new LauncherV2ProfilesResponse
|
||||
{
|
||||
Response = _profileController.GetMiniProfiles()
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class MatchCallbacks(
|
||||
/// <returns></returns>
|
||||
public string AcceptGroupInvite(string url, RequestIdRequest info, string sessionID)
|
||||
{
|
||||
return _httpResponseUtil.GetBody(new List<GroupCharacter>() { new GroupCharacter() });
|
||||
return _httpResponseUtil.GetBody(new List<GroupCharacter>() { new() });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Core.Callbacks;
|
||||
[Injectable]
|
||||
public class NoteCallbacks(NoteController _noteController)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Handle AddNote event
|
||||
/// </summary>
|
||||
|
||||
@@ -94,10 +94,7 @@ public class ProfileCallbacks(
|
||||
public string GetReservedNickname(string url, EmptyRequestData info, string sessionID)
|
||||
{
|
||||
var fullProfile = _profileHelper.GetFullProfile(sessionID);
|
||||
if (fullProfile?.ProfileInfo?.Username is not null)
|
||||
{
|
||||
return _httpResponse.GetBody(fullProfile?.ProfileInfo?.Username);
|
||||
}
|
||||
if (fullProfile?.ProfileInfo?.Username is not null) return _httpResponse.GetBody(fullProfile?.ProfileInfo?.Username);
|
||||
|
||||
return _httpResponse.GetBody("SPTarkov");
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class RagfairCallbacks(
|
||||
RagfairTaxService _ragfairTaxService,
|
||||
RagfairPriceService _ragfairPriceService,
|
||||
ConfigServer _configServer
|
||||
) : OnLoad, OnUpdate
|
||||
) : OnLoad, OnUpdate
|
||||
{
|
||||
private RagfairConfig _ragfairConfig = _configServer.GetConfig<RagfairConfig>();
|
||||
|
||||
@@ -39,19 +39,21 @@ public class RagfairCallbacks(
|
||||
|
||||
public bool OnUpdate(long timeSinceLastRun)
|
||||
{
|
||||
if (timeSinceLastRun > _ragfairConfig.RunIntervalSeconds) {
|
||||
if (timeSinceLastRun > _ragfairConfig.RunIntervalSeconds)
|
||||
{
|
||||
// There is a flag inside this class that only makes it run once.
|
||||
_ragfairServer.AddPlayerOffers();
|
||||
|
||||
// Check player offers and mail payment to player if sold
|
||||
_ragfairController.Update();
|
||||
|
||||
// Process all offers / expire offers
|
||||
_ragfairServer.Update();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Check player offers and mail payment to player if sold
|
||||
_ragfairController.Update();
|
||||
|
||||
// Process all offers / expire offers
|
||||
_ragfairServer.Update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ public class SaveCallbacks(
|
||||
SaveServer _saveServer,
|
||||
ConfigServer _configServer,
|
||||
BackupService _backupService
|
||||
)
|
||||
)
|
||||
: OnLoad, OnUpdate
|
||||
{
|
||||
private readonly CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
|
||||
@@ -23,6 +23,11 @@ public class SaveCallbacks(
|
||||
_saveServer.Load();
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-save";
|
||||
}
|
||||
|
||||
public bool OnUpdate(long secondsSinceLastRun)
|
||||
{
|
||||
if (secondsSinceLastRun > _coreConfig.ProfileSaveIntervalInSeconds)
|
||||
@@ -33,9 +38,4 @@ public class SaveCallbacks(
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-save";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,23 +18,23 @@ public class TraderCallbacks(
|
||||
) : OnLoad, OnUpdate
|
||||
{
|
||||
private readonly TraderConfig _traderConfig = _configServer.GetConfig<TraderConfig>();
|
||||
|
||||
|
||||
public Task OnLoad()
|
||||
{
|
||||
_traderController.Load();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool OnUpdate(long _)
|
||||
{
|
||||
return _traderController.Update();
|
||||
}
|
||||
|
||||
public string GetRoute()
|
||||
{
|
||||
return "spt-traders";
|
||||
}
|
||||
|
||||
public bool OnUpdate(long _)
|
||||
{
|
||||
return _traderController.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/trading/api/traderSettings
|
||||
/// </summary>
|
||||
|
||||
@@ -25,10 +25,7 @@ public class ApplicationContext
|
||||
lock (variablesLock)
|
||||
{
|
||||
var values = new List<ContextVariable>();
|
||||
if (variables.TryGetValue(type, out var savedValues))
|
||||
{
|
||||
values.AddRange(savedValues);
|
||||
}
|
||||
if (variables.TryGetValue(type, out var savedValues)) values.AddRange(savedValues);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ public class ContextVariable(object value, ContextVariableType contextVariableIn
|
||||
{
|
||||
private readonly DateTime _timestamp = DateTime.UtcNow;
|
||||
|
||||
public T GetValue<T>() {
|
||||
public T GetValue<T>()
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,10 @@ public class AchievementController(
|
||||
var stats = new Dictionary<string, int>();
|
||||
|
||||
foreach (var achievement in achievements)
|
||||
{
|
||||
if (achievement.Id != null) stats.Add(achievement.Id, 0);
|
||||
}
|
||||
if (achievement.Id != null)
|
||||
stats.Add(achievement.Id, 0);
|
||||
|
||||
return new()
|
||||
return new CompletedAchievementsResponse
|
||||
{
|
||||
Elements = stats
|
||||
};
|
||||
|
||||
@@ -52,10 +52,7 @@ public class BotController(
|
||||
.First(p => p.Name.ToLower() == (typeInLower == "assaultgroup" ? "assault" : typeInLower))
|
||||
.GetValue(_botConfig.PresetBatch);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
if (value != null) return value;
|
||||
|
||||
_logger.Warning(_localisationService.GetText("bot-bot_preset_count_value_missing", type));
|
||||
return 30;
|
||||
@@ -70,18 +67,12 @@ public class BotController(
|
||||
{
|
||||
var difficulty = diffLevel.ToLower();
|
||||
|
||||
if (!(raidConfig != null || ignoreRaidSettings))
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
|
||||
}
|
||||
if (!(raidConfig != null || ignoreRaidSettings)) _logger.Error(_localisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
|
||||
|
||||
// Check value chosen in pre-raid difficulty dropdown
|
||||
// If value is not 'asonline', change requested difficulty to be what was chosen in dropdown
|
||||
var botDifficultyDropDownValue = raidConfig?.WavesSettings?.BotDifficulty?.ToString().ToLower() ?? "asonline";
|
||||
if (botDifficultyDropDownValue != "asonline")
|
||||
{
|
||||
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
|
||||
}
|
||||
if (botDifficultyDropDownValue != "asonline") difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
|
||||
|
||||
var botDb = _databaseService.GetBots();
|
||||
return _botDifficultyHelper.GetBotDifficultySettings(type, difficulty, botDb);
|
||||
@@ -96,10 +87,7 @@ public class BotController(
|
||||
var botTypes = Enum.GetValues<WildSpawnType>().Select(item => item.ToString()).ToList();
|
||||
foreach (var botType in botTypes)
|
||||
{
|
||||
if (botTypesDb is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (botTypesDb is null) continue;
|
||||
|
||||
// If bot is usec/bear, swap to different name
|
||||
var botTypeLower = _botHelper.IsBotPmc(botType)
|
||||
@@ -113,10 +101,7 @@ public class BotController(
|
||||
{
|
||||
// No bot of this type found, copy details from assault
|
||||
result[botTypeLower] = result["assault"];
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to find bot: {botTypeLower} in db, copying 'assault'");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Unable to find bot: {botTypeLower} in db, copying 'assault'");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -131,10 +116,7 @@ public class BotController(
|
||||
foreach (var (difficultyName, _) in botDetails.BotDifficulty)
|
||||
{
|
||||
// Bot doesn't exist in result, add
|
||||
if (!result.ContainsKey(botNameKey))
|
||||
{
|
||||
result.TryAdd(botNameKey, new Dictionary<string, DifficultyCategories>());
|
||||
}
|
||||
if (!result.ContainsKey(botNameKey)) result.TryAdd(botNameKey, new Dictionary<string, DifficultyCategories>());
|
||||
|
||||
// Store all difficulty values in dict keyed by difficulty type e.g. easy/normal/impossible
|
||||
result[botNameKey].Add(difficultyName, GetBotDifficulty(botNameKey, difficultyName, null, true));
|
||||
@@ -147,7 +129,7 @@ public class BotController(
|
||||
public List<BotBase> Generate(string sessionId, GenerateBotsRequestData info)
|
||||
{
|
||||
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
|
||||
|
||||
|
||||
// Use this opportunity to create and cache bots for later retrieval
|
||||
var multipleBotTypesRequested = info.Conditions?.Count > 1;
|
||||
return multipleBotTypesRequested
|
||||
@@ -166,7 +148,6 @@ public class BotController(
|
||||
var tasks = new List<Task>();
|
||||
// Map conditions to promises for bot generation
|
||||
foreach (var condition in request.Conditions ?? [])
|
||||
{
|
||||
tasks.Add(
|
||||
Task.Factory.StartNew(
|
||||
() =>
|
||||
@@ -185,14 +166,10 @@ public class BotController(
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
stopwatch.Stop();
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache");
|
||||
|
||||
return [];
|
||||
}
|
||||
@@ -220,24 +197,17 @@ public class BotController(
|
||||
|
||||
if (botCacheCount >= botGenerationDetails.BotCountToGenerate)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Cache already has sufficient {cacheKey} bots: {botCacheCount}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Cache already has sufficient {cacheKey} bots: {botCacheCount}");
|
||||
return;
|
||||
}
|
||||
|
||||
// We're below desired count, add bots to cache
|
||||
var botsToGenerate = botGenerationDetails.BotCountToGenerate - botCacheCount;
|
||||
var progressWriter = new ProgressWriter(botGenerationDetails.BotCountToGenerate.GetValueOrDefault(30));
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generating {botsToGenerate} bots for cacheKey: {cacheKey}");
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Generating {botsToGenerate} bots for cacheKey: {cacheKey}");
|
||||
|
||||
for (var i = 0; i < botsToGenerate; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var detailsClone = _cloner.Clone(botGenerationDetails);
|
||||
@@ -248,15 +218,12 @@ public class BotController(
|
||||
{
|
||||
_logger.Error($"Failed to generate bot: {botGenerationDetails.Role} #{i + 1}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Generated {botGenerationDetails.BotCountToGenerate} {botGenerationDetails.Role}" +
|
||||
$"({botGenerationDetails.EventRole ?? botGenerationDetails.Role ?? ""}) {botGenerationDetails.BotDifficulty}bots"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private List<BotBase> ReturnSingleBotFromCache(string sessionId, GenerateBotsRequestData request)
|
||||
@@ -271,7 +238,7 @@ public class BotController(
|
||||
{
|
||||
Role = requestedBot?.Role,
|
||||
Limit = 5,
|
||||
Difficulty = requestedBot?.Difficulty,
|
||||
Difficulty = requestedBot?.Difficulty
|
||||
};
|
||||
var botGenerationDetails = GetBotGenerationDetailsForWave(
|
||||
condition,
|
||||
@@ -321,13 +288,9 @@ public class BotController(
|
||||
{
|
||||
var bossConvertPercent = bossConvertMinMax.GetByJsonProp<MinMax>(requestedBot?.Role?.ToLower() ?? string.Empty);
|
||||
if (bossConvertPercent is not null)
|
||||
{
|
||||
// Roll a percentage check if we should convert scav to boss
|
||||
if (_randomUtil.GetChance100(_randomUtil.GetDouble(bossConvertPercent.Min!.Value, bossConvertPercent.Max!.Value)))
|
||||
{
|
||||
UpdateBotGenerationDetailsToRandomBoss(botGenerationDetails, bossesToConvertToWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a compound key to store bots in cache against
|
||||
@@ -341,14 +304,12 @@ public class BotController(
|
||||
{
|
||||
// No bot in cache, generate new and store in cache
|
||||
GenerateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey);
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Generated {botGenerationDetails.BotCountToGenerate} " +
|
||||
$"{botGenerationDetails.Role} ({botGenerationDetails.EventRole ?? ""}) {botGenerationDetails.BotDifficulty} bots"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var desiredBot = _botGenerationCacheService.GetBot(cacheKey);
|
||||
@@ -400,10 +361,7 @@ public class BotController(
|
||||
.GetLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
||||
?.GetValue<GetRaidConfigurationRequestData>();
|
||||
|
||||
if (raidSettings is null)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
|
||||
}
|
||||
if (raidSettings is null) _logger.Warning(_localisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
|
||||
|
||||
return raidSettings;
|
||||
}
|
||||
@@ -432,9 +390,9 @@ public class BotController(
|
||||
BotRelativeLevelDeltaMin = _pmcConfig.BotRelativeLevelDeltaMin,
|
||||
BotCountToGenerate = botCountToGenerate,
|
||||
BotDifficulty = condition.Difficulty,
|
||||
LocationSpecificPmcLevelOverride = this.GetPmcLevelRangeForMap(raidSettings?.Location), // Min/max levels for PMCs to generate within
|
||||
LocationSpecificPmcLevelOverride = GetPmcLevelRangeForMap(raidSettings?.Location), // Min/max levels for PMCs to generate within
|
||||
IsPlayerScav = false,
|
||||
AllPmcsHaveSameNameAsPlayer = allPmcsHaveSameNameAsPlayer,
|
||||
AllPmcsHaveSameNameAsPlayer = allPmcsHaveSameNameAsPlayer
|
||||
};
|
||||
}
|
||||
|
||||
@@ -442,11 +400,9 @@ public class BotController(
|
||||
{
|
||||
var botCap = _botConfig.MaxBotCap.FirstOrDefault(x => x.Key.ToLower() == location.ToLower());
|
||||
if (location == "default")
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService.GetText("bot-no_bot_cap_found_for_location", location.ToLower())
|
||||
);
|
||||
}
|
||||
|
||||
return botCap.Value;
|
||||
}
|
||||
@@ -457,7 +413,7 @@ public class BotController(
|
||||
{
|
||||
PmcType = _pmcConfig.PmcType,
|
||||
Assault = _botConfig.AssaultBrainType,
|
||||
PlayerScav = _botConfig.PlayerScavBrainType,
|
||||
PlayerScav = _botConfig.PlayerScavBrainType
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -465,11 +421,11 @@ public class BotController(
|
||||
public record AiBotBrainTypes
|
||||
{
|
||||
[JsonPropertyName("pmc")]
|
||||
public Dictionary<string,Dictionary<string,Dictionary<string,double>>> PmcType { get; set; }
|
||||
|
||||
public Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType { get; set; }
|
||||
|
||||
[JsonPropertyName("assault")]
|
||||
public Dictionary<string,Dictionary<string,int>> Assault { get; set; }
|
||||
|
||||
public Dictionary<string, Dictionary<string, int>> Assault { get; set; }
|
||||
|
||||
[JsonPropertyName("playerScav")]
|
||||
public Dictionary<string,Dictionary<string,int>> PlayerScav { get; set; }
|
||||
public Dictionary<string, Dictionary<string, int>> PlayerScav { get; set; }
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ public class BuildController(
|
||||
const string secureContainerSlotId = "SecuredContainer";
|
||||
var profile = _profileHelper.GetFullProfile(sessionID);
|
||||
if (profile is not null && profile.UserBuildData is null)
|
||||
{
|
||||
profile.UserBuildData = new UserBuilds { EquipmentBuilds = [], WeaponBuilds = [], MagazineBuilds = [] };
|
||||
}
|
||||
|
||||
// Ensure the secure container in the default presets match what the player has equipped
|
||||
var defaultEquipmentPresetsClone = _cloner.Clone(
|
||||
@@ -52,18 +50,13 @@ public class BuildController(
|
||||
x => x.SlotId == secureContainerSlotId
|
||||
);
|
||||
if (playerSecureContainer is not null && playerSecureContainer.Template != firstDefaultItemsSecureContainer?.Template)
|
||||
{
|
||||
// Default equipment presets' secure container tpl doesn't match players secure container tpl
|
||||
foreach (var defaultPreset in defaultEquipmentPresetsClone ?? [])
|
||||
{
|
||||
// Find presets secure container
|
||||
var secureContainer = defaultPreset.Items?.FirstOrDefault(item => item.SlotId == secureContainerSlotId);
|
||||
if (secureContainer is not null)
|
||||
{
|
||||
secureContainer.Template = playerSecureContainer.Template;
|
||||
}
|
||||
if (secureContainer is not null) secureContainer.Template = playerSecureContainer.Template;
|
||||
}
|
||||
}
|
||||
|
||||
// Clone player build data from profile and append the above defaults onto end
|
||||
var userBuildsClone = _cloner.Clone(profile?.UserBuildData);
|
||||
@@ -87,7 +80,7 @@ public class BuildController(
|
||||
body.Root = body.Items.FirstOrDefault().Id;
|
||||
|
||||
// Create new object ready to save into profile userbuilds.weaponBuilds
|
||||
WeaponBuild newBuild = new WeaponBuild { Id = body.Id, Name = body.Name, Root = body.Root, Items = body.Items };
|
||||
var newBuild = new WeaponBuild { Id = body.Id, Name = body.Name, Root = body.Root, Items = body.Items };
|
||||
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
|
||||
@@ -124,13 +117,13 @@ public class BuildController(
|
||||
// Root ID and the base item ID need to match.
|
||||
request.Items = _itemHelper.ReplaceIDs(request.Items, pmcData);
|
||||
|
||||
EquipmentBuild newBuild = new EquipmentBuild
|
||||
var newBuild = new EquipmentBuild
|
||||
{
|
||||
Id = request.Id,
|
||||
Name = request.Name,
|
||||
BuildType = EquipmentBuildType.Custom,
|
||||
Root = request.Items[0].Id,
|
||||
Items = request.Items,
|
||||
Items = request.Items
|
||||
};
|
||||
|
||||
var existingBuild = existingSavedEquipmentBuilds.FirstOrDefault(
|
||||
@@ -167,14 +160,14 @@ public class BuildController(
|
||||
/// <param name="request"></param>
|
||||
public void CreateMagazineTemplate(string sessionId, SetMagazineRequest request)
|
||||
{
|
||||
MagazineBuild result = new MagazineBuild
|
||||
var result = new MagazineBuild
|
||||
{
|
||||
Id = request.Id,
|
||||
Name = request.Name,
|
||||
Caliber = request.Caliber,
|
||||
TopCount = request.TopCount,
|
||||
BottomCount = request.BottomCount,
|
||||
Items = request.Items,
|
||||
Items = request.Items
|
||||
};
|
||||
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
|
||||
@@ -8,9 +8,8 @@ namespace Core.Controllers;
|
||||
[Injectable]
|
||||
public class ClientLogController(
|
||||
ISptLogger<ClientLogController> _logger
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Handle /singleplayer/log
|
||||
/// </summary>
|
||||
|
||||
@@ -55,10 +55,7 @@ public class CustomizationController(
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if (matchingSuits == null)
|
||||
{
|
||||
throw new Exception(_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
|
||||
}
|
||||
if (matchingSuits == null) throw new Exception(_localisationService.GetText("customisation-unable_to_get_trader_suits", traderId));
|
||||
|
||||
return matchingSuits;
|
||||
}
|
||||
@@ -104,7 +101,7 @@ public class CustomizationController(
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Charge player for buying item
|
||||
PayForClothingItems(sessionId, pmcData, buyClothingRequest.Items, output);
|
||||
|
||||
@@ -127,20 +124,14 @@ public class CustomizationController(
|
||||
{
|
||||
var suits = _saveServer.GetProfile(sessionId).Suits;
|
||||
|
||||
if (suits is null || suits.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (suits is null || suits.Count == 0) return false;
|
||||
return suits.Contains(suitId);
|
||||
}
|
||||
|
||||
private Suit? GetTraderClothingOffer(string sessionId, string? offerId)
|
||||
{
|
||||
var foundSuit = GetAllTraderSuits(sessionId).FirstOrDefault(s => s.Id == offerId);
|
||||
if (foundSuit is null)
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("customisation-unable_to_find_suit_with_id", offerId));
|
||||
}
|
||||
if (foundSuit is null) _logger.Error(_localisationService.GetText("customisation-unable_to_find_suit_with_id", offerId));
|
||||
|
||||
return foundSuit;
|
||||
}
|
||||
@@ -156,11 +147,8 @@ public class CustomizationController(
|
||||
List<PaymentItemForClothing>? itemsToPayForClothingWith,
|
||||
ItemEventRouterResponse output)
|
||||
{
|
||||
if (itemsToPayForClothingWith is null || itemsToPayForClothingWith.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemsToPayForClothingWith is null || itemsToPayForClothingWith.Count == 0) return;
|
||||
|
||||
foreach (var inventoryItemToProcess in itemsToPayForClothingWith)
|
||||
{
|
||||
var options = new ProcessBuyTradeRequestData
|
||||
@@ -189,9 +177,7 @@ public class CustomizationController(
|
||||
|
||||
foreach (var trader in traders)
|
||||
if (trader.Value.Base?.CustomizationSeller is not null && trader.Value.Base.CustomizationSeller.Value)
|
||||
{
|
||||
result.AddRange(GetTraderSuits(trader.Key, sessionId));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -220,10 +206,7 @@ public class CustomizationController(
|
||||
var customisationResultsClone = _cloner.Clone(_databaseService.GetTemplates().CustomisationStorage);
|
||||
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
if (profile is null)
|
||||
{
|
||||
return customisationResultsClone!;
|
||||
}
|
||||
if (profile is null) return customisationResultsClone!;
|
||||
|
||||
customisationResultsClone!.AddRange(profile.CustomisationUnlocks ?? []);
|
||||
|
||||
@@ -240,7 +223,6 @@ public class CustomizationController(
|
||||
public ItemEventRouterResponse SetCustomisation(string sessionId, CustomizationSetRequest request, PmcData pmcData)
|
||||
{
|
||||
foreach (var customisation in request.Customizations)
|
||||
{
|
||||
switch (customisation.Type)
|
||||
{
|
||||
case "dogTag":
|
||||
@@ -253,7 +235,6 @@ public class CustomizationController(
|
||||
_logger.Error($"Unhandled customisation type: {customisation.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
@@ -285,9 +266,6 @@ public class CustomizationController(
|
||||
}
|
||||
|
||||
// Feet
|
||||
if (dbSuit.Parent == _lowerParentClothingId)
|
||||
{
|
||||
pmcData.Customization.Feet = dbSuit.Properties.Feet;
|
||||
}
|
||||
if (dbSuit.Parent == _lowerParentClothingId) pmcData.Customization.Feet = dbSuit.Properties.Feet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,7 @@ public class DialogueController(
|
||||
public void RegisterChatBot(IDialogueChatBot chatBot) // TODO: this is in with the helper types
|
||||
{
|
||||
if (_dialogueChatBots.Any(cb => cb.GetChatBot().Id == chatBot.GetChatBot().Id))
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("dialog-chatbot_id_already_exists", chatBot.GetChatBot().Id));
|
||||
}
|
||||
|
||||
_dialogueChatBots.Add(chatBot);
|
||||
}
|
||||
@@ -51,10 +49,7 @@ public class DialogueController(
|
||||
public void Update()
|
||||
{
|
||||
var profiles = _saveServer.GetProfiles();
|
||||
foreach (var kvp in profiles)
|
||||
{
|
||||
RemoveExpiredItemsFromMessages(kvp.Key);
|
||||
}
|
||||
foreach (var kvp in profiles) RemoveExpiredItemsFromMessages(kvp.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -70,23 +65,19 @@ public class DialogueController(
|
||||
// Add any friends the user has after the chatbots
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
if (profile?.FriendProfileIds is not null)
|
||||
{
|
||||
foreach (var friendId in profile.FriendProfileIds)
|
||||
{
|
||||
var friendProfile = _profileHelper.GetChatRoomMemberFromSessionId(friendId);
|
||||
if (friendProfile is not null)
|
||||
{
|
||||
friends.Add(
|
||||
new UserDialogInfo
|
||||
{
|
||||
Id = friendProfile.Id,
|
||||
Aid = friendProfile.Aid,
|
||||
Info = friendProfile.Info,
|
||||
Info = friendProfile.Info
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new GetFriendListDataResponse
|
||||
{
|
||||
@@ -105,10 +96,7 @@ public class DialogueController(
|
||||
foreach (var bot in _dialogueChatBots)
|
||||
{
|
||||
var botData = bot.GetChatBot();
|
||||
if (chatBotConfig.EnabledBots.ContainsKey(botData.Id!))
|
||||
{
|
||||
activeBots.Add(botData);
|
||||
}
|
||||
if (chatBotConfig.EnabledBots.ContainsKey(botData.Id!)) activeBots.Add(botData);
|
||||
}
|
||||
|
||||
return activeBots;
|
||||
@@ -124,10 +112,7 @@ public class DialogueController(
|
||||
public List<DialogueInfo> GenerateDialogueList(string sessionId)
|
||||
{
|
||||
var data = new List<DialogueInfo>();
|
||||
foreach (var dialogueId in _dialogueHelper.GetDialogsForProfile(sessionId))
|
||||
{
|
||||
data.Add(GetDialogueInfo(dialogueId.Key, sessionId));
|
||||
}
|
||||
foreach (var dialogueId in _dialogueHelper.GetDialogsForProfile(sessionId)) data.Add(GetDialogueInfo(dialogueId.Key, sessionId));
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -153,7 +138,7 @@ public class DialogueController(
|
||||
New = dialogue?.New,
|
||||
AttachmentsNew = dialogue?.AttachmentsNew,
|
||||
Pinned = dialogue?.Pinned,
|
||||
Users = GetDialogueUsers(dialogue, dialogue?.Type, sessionId),
|
||||
Users = GetDialogueUsers(dialogue, dialogue?.Type, sessionId)
|
||||
};
|
||||
|
||||
return result;
|
||||
@@ -177,7 +162,6 @@ public class DialogueController(
|
||||
if (messageType == MessageType.USER_MESSAGE &&
|
||||
dialog?.Users is not null &&
|
||||
dialog.Users.All(userDialog => userDialog.Id != profile.CharacterData?.PmcData?.SessionId))
|
||||
{
|
||||
dialog.Users.Add(
|
||||
new UserDialogInfo
|
||||
{
|
||||
@@ -189,11 +173,10 @@ public class DialogueController(
|
||||
Nickname = profile.CharacterData?.PmcData?.Info?.Nickname,
|
||||
Side = profile.CharacterData?.PmcData?.Info?.Side,
|
||||
MemberCategory = profile.CharacterData?.PmcData?.Info?.MemberCategory,
|
||||
SelectedMemberCategory = profile.CharacterData?.PmcData?.Info?.SelectedMemberCategory,
|
||||
},
|
||||
SelectedMemberCategory = profile.CharacterData?.PmcData?.Info?.SelectedMemberCategory
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return dialog?.Users!;
|
||||
}
|
||||
@@ -225,7 +208,7 @@ public class DialogueController(
|
||||
{
|
||||
Messages = dialogue.Messages,
|
||||
Profiles = GetProfilesForMail(fullProfile, dialogue.Users),
|
||||
HasMessagesWithRewards = MessagesHaveUncollectedRewards(dialogue.Messages!),
|
||||
HasMessagesWithRewards = MessagesHaveUncollectedRewards(dialogue.Messages!)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,22 +232,16 @@ public class DialogueController(
|
||||
Pinned = false,
|
||||
Messages = [],
|
||||
New = 0,
|
||||
Type = request.Type,
|
||||
Type = request.Type
|
||||
};
|
||||
|
||||
if (request.Type != MessageType.USER_MESSAGE)
|
||||
{
|
||||
return profile.DialogueRecords[request.DialogId!];
|
||||
}
|
||||
if (request.Type != MessageType.USER_MESSAGE) return profile.DialogueRecords[request.DialogId!];
|
||||
|
||||
var dialogue = profile.DialogueRecords[request.DialogId!];
|
||||
dialogue.Users = [];
|
||||
var chatBot = _dialogueChatBots.FirstOrDefault(cb => cb.GetChatBot().Id == request.DialogId);
|
||||
|
||||
if (chatBot is null)
|
||||
{
|
||||
return profile.DialogueRecords[request.DialogId!];
|
||||
}
|
||||
if (chatBot is null) return profile.DialogueRecords[request.DialogId!];
|
||||
|
||||
dialogue.Users ??= [];
|
||||
dialogue.Users.Add(chatBot.GetChatBot());
|
||||
@@ -282,17 +259,12 @@ public class DialogueController(
|
||||
{
|
||||
List<UserDialogInfo> result = [];
|
||||
if (userDialogs is null)
|
||||
{
|
||||
// Nothing to add
|
||||
return result;
|
||||
}
|
||||
|
||||
result.AddRange(userDialogs);
|
||||
|
||||
if (result.Any(userDialog => userDialog.Id == fullProfile.ProfileInfo?.ProfileId))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (result.Any(userDialog => userDialog.Id == fullProfile.ProfileInfo?.ProfileId)) return result;
|
||||
|
||||
// Player doesn't exist, add them in before returning
|
||||
var pmcProfile = fullProfile.CharacterData?.PmcData;
|
||||
@@ -307,7 +279,7 @@ public class DialogueController(
|
||||
Side = pmcProfile?.Info?.Side,
|
||||
Level = pmcProfile?.Info?.Level,
|
||||
MemberCategory = pmcProfile?.Info?.MemberCategory,
|
||||
SelectedMemberCategory = pmcProfile?.Info?.SelectedMemberCategory,
|
||||
SelectedMemberCategory = pmcProfile?.Info?.SelectedMemberCategory
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -328,12 +300,8 @@ public class DialogueController(
|
||||
var newAttachmentCount = 0;
|
||||
var activeMessages = GetActiveMessagesFromDialog(sessionId, dialogueId);
|
||||
foreach (var message in activeMessages)
|
||||
{
|
||||
if (message.HasRewards.GetValueOrDefault(false) && !message.RewardCollected.GetValueOrDefault(false))
|
||||
{
|
||||
newAttachmentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return newAttachmentCount;
|
||||
}
|
||||
@@ -382,7 +350,7 @@ public class DialogueController(
|
||||
new
|
||||
{
|
||||
sessionId,
|
||||
dialogueId,
|
||||
dialogueId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -410,7 +378,7 @@ public class DialogueController(
|
||||
new
|
||||
{
|
||||
sessionId,
|
||||
dialogueId,
|
||||
dialogueId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -437,7 +405,7 @@ public class DialogueController(
|
||||
"dialogue-unable_to_find_dialogs_in_profile",
|
||||
new
|
||||
{
|
||||
sessionId,
|
||||
sessionId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -498,11 +466,13 @@ public class DialogueController(
|
||||
{
|
||||
_mailSendService.SendPlayerMessageToNpc(sessionId, request.DialogId!, request.Text!);
|
||||
|
||||
return (_dialogueChatBots.FirstOrDefault(cb =>
|
||||
cb.GetChatBot().Id == request.DialogId)
|
||||
?.HandleMessage(sessionId, request)
|
||||
?? request.DialogId)
|
||||
?? string.Empty;
|
||||
return (_dialogueChatBots.FirstOrDefault(
|
||||
cb =>
|
||||
cb.GetChatBot().Id == request.DialogId
|
||||
)
|
||||
?.HandleMessage(sessionId, request) ??
|
||||
request.DialogId) ??
|
||||
string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -521,10 +491,7 @@ public class DialogueController(
|
||||
/// <param name="sessionId">Session id</param>
|
||||
private void RemoveExpiredItemsFromMessages(string sessionId)
|
||||
{
|
||||
foreach (var dialogueId in _dialogueHelper.GetDialogsForProfile(sessionId))
|
||||
{
|
||||
RemoveExpiredItemsFromMessage(sessionId, dialogueId.Key);
|
||||
}
|
||||
foreach (var dialogueId in _dialogueHelper.GetDialogsForProfile(sessionId)) RemoveExpiredItemsFromMessage(sessionId, dialogueId.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -535,18 +502,11 @@ public class DialogueController(
|
||||
private void RemoveExpiredItemsFromMessage(string sessionId, string dialogueId)
|
||||
{
|
||||
var dialogs = _dialogueHelper.GetDialogsForProfile(sessionId);
|
||||
if (!dialogs.TryGetValue(dialogueId, out var dialog))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!dialogs.TryGetValue(dialogueId, out var dialog)) return;
|
||||
|
||||
foreach (var message in dialog.Messages ?? [])
|
||||
{
|
||||
if (MessageHasExpired(message))
|
||||
{
|
||||
message.Items = new MessageItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,30 +524,25 @@ public class DialogueController(
|
||||
// To avoid needing to jump between profiles, auto-accept all friend requests
|
||||
var friendProfile = _profileHelper.GetFullProfile(request.To);
|
||||
if (friendProfile?.CharacterData?.PmcData is null)
|
||||
{
|
||||
return new FriendRequestSendResponse
|
||||
{
|
||||
Status = BackendErrorCodes.PlayerProfileNotFound,
|
||||
RequestId = "", // Unused in an error state
|
||||
RetryAfter = 600,
|
||||
RetryAfter = 600
|
||||
};
|
||||
}
|
||||
|
||||
// Only add the profile to the friends list if it doesn't already exist
|
||||
var profile = _saveServer.GetProfile(sessionID);
|
||||
if (!profile.FriendProfileIds.Contains(request.To))
|
||||
{
|
||||
profile.FriendProfileIds.Add(request.To);
|
||||
}
|
||||
if (!profile.FriendProfileIds.Contains(request.To)) profile.FriendProfileIds.Add(request.To);
|
||||
|
||||
// We need to delay this so that the friend request gets properly added to the clientside list before we accept it
|
||||
_ = new Timer(
|
||||
_ =>
|
||||
{
|
||||
WsFriendsListAccept notification = new WsFriendsListAccept
|
||||
var notification = new WsFriendsListAccept
|
||||
{
|
||||
EventType = NotificationEventType.friendListRequestAccept,
|
||||
Profile = _profileHelper.GetChatRoomMemberFromPmcProfile(friendProfile.CharacterData.PmcData),
|
||||
Profile = _profileHelper.GetChatRoomMemberFromPmcProfile(friendProfile.CharacterData.PmcData)
|
||||
};
|
||||
_notificationSendHelper.SendMessage(sessionID, notification);
|
||||
},
|
||||
@@ -608,9 +563,6 @@ public class DialogueController(
|
||||
{
|
||||
var profile = _saveServer.GetProfile(sessionID);
|
||||
var friendIndex = profile.FriendProfileIds.IndexOf(request.FriendId);
|
||||
if (friendIndex != -1)
|
||||
{
|
||||
profile.FriendProfileIds.RemoveAt(friendIndex);
|
||||
}
|
||||
if (friendIndex != -1) profile.FriendProfileIds.RemoveAt(friendIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ public class GameController(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
protected CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
|
||||
protected double _deviation = 0.0001;
|
||||
protected HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
protected HttpConfig _httpConfig = _configServer.GetConfig<HttpConfig>();
|
||||
protected RagfairConfig _ragfairConfig = _configServer.GetConfig<RagfairConfig>();
|
||||
protected HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
protected double _deviation = 0.0001;
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/game/start
|
||||
@@ -82,40 +82,25 @@ public class GameController(
|
||||
_logger.Error($"{nameof(fullProfile)} is null on GameController.GameStart");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fullProfile.SptData ??= new Spt { Version = "Replace_me" };
|
||||
fullProfile.SptData.Migrations ??= new Dictionary<string, long>();
|
||||
fullProfile.FriendProfileIds ??= [];
|
||||
|
||||
if (fullProfile.ProfileInfo?.IsWiped is not null && fullProfile.ProfileInfo.IsWiped.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fullProfile.ProfileInfo?.IsWiped is not null && fullProfile.ProfileInfo.IsWiped.Value) return;
|
||||
|
||||
fullProfile.CharacterData!.PmcData!.WishList ??= new DictionaryOrList<string, int>(new Dictionary<string, int>(), []);
|
||||
fullProfile.CharacterData.ScavData!.WishList ??= new DictionaryOrList<string, int>(new Dictionary<string, int>(), []);
|
||||
|
||||
if (fullProfile.DialogueRecords is not null)
|
||||
{
|
||||
_profileFixerService.CheckForAndFixDialogueAttachments(fullProfile);
|
||||
}
|
||||
if (fullProfile.DialogueRecords is not null) _profileFixerService.CheckForAndFixDialogueAttachments(fullProfile);
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Started game with session {sessionId} {fullProfile.ProfileInfo?.Username}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Started game with session {sessionId} {fullProfile.ProfileInfo?.Username}");
|
||||
|
||||
var pmcProfile = fullProfile.CharacterData.PmcData;
|
||||
|
||||
if (_coreConfig.Fixes.FixProfileBreakingInventoryItemIssues)
|
||||
{
|
||||
_profileFixerService.FixProfileBreakingInventoryItemIssues(pmcProfile);
|
||||
}
|
||||
if (_coreConfig.Fixes.FixProfileBreakingInventoryItemIssues) _profileFixerService.FixProfileBreakingInventoryItemIssues(pmcProfile);
|
||||
|
||||
if (pmcProfile.Health is not null)
|
||||
{
|
||||
UpdateProfileHealthValues(pmcProfile);
|
||||
}
|
||||
if (pmcProfile.Health is not null) UpdateProfileHealthValues(pmcProfile);
|
||||
|
||||
if (pmcProfile.Inventory is not null)
|
||||
{
|
||||
@@ -143,10 +128,7 @@ public class GameController(
|
||||
CheckForAndRemoveUndefinedDialogues(fullProfile);
|
||||
}
|
||||
|
||||
if (pmcProfile.Skills?.Common is not null)
|
||||
{
|
||||
WarnOnActiveBotReloadSkill(pmcProfile);
|
||||
}
|
||||
if (pmcProfile.Skills?.Common is not null) WarnOnActiveBotReloadSkill(pmcProfile);
|
||||
|
||||
_seasonalEventService.GivePlayerSeasonalGifts(sessionId);
|
||||
}
|
||||
@@ -160,8 +142,9 @@ public class GameController(
|
||||
{
|
||||
var profile = _profileHelper.GetPmcProfile(sessionId);
|
||||
var gameTime = profile?.Stats?.Eft?.OverallCounters?.Items?
|
||||
.FirstOrDefault(c => c.Key!.Contains("LifeTime")
|
||||
&& c.Key.Contains("Pmc"))?.Value ?? 0D;
|
||||
.FirstOrDefault(c => c.Key!.Contains("LifeTime") && c.Key.Contains("Pmc"))
|
||||
?.Value ??
|
||||
0D;
|
||||
|
||||
var config = new GameConfigResponse
|
||||
{
|
||||
@@ -173,7 +156,7 @@ public class GameController(
|
||||
Aid = profile?.Aid,
|
||||
Taxonomy = 6,
|
||||
ActiveProfileId = sessionId,
|
||||
Backend = new()
|
||||
Backend = new Backend
|
||||
{
|
||||
Lobby = _httpServerHelper.GetBackendUrl(),
|
||||
Trading = _httpServerHelper.GetBackendUrl(),
|
||||
@@ -185,7 +168,7 @@ public class GameController(
|
||||
UtcTime = _timeUtil.GetTimeStamp(),
|
||||
TotalInGame = gameTime,
|
||||
SessionMode = "pve",
|
||||
PurchasedGames = new()
|
||||
PurchasedGames = new PurchasedGames
|
||||
{
|
||||
IsEftPurchased = true,
|
||||
IsArenaPurchased = false
|
||||
@@ -205,7 +188,7 @@ public class GameController(
|
||||
string sessionId,
|
||||
GameModeRequestData requestData)
|
||||
{
|
||||
return new()
|
||||
return new GameModeResponse
|
||||
{
|
||||
GameMode = "pve",
|
||||
BackendUrl = _httpServerHelper.GetBackendUrl()
|
||||
@@ -291,7 +274,7 @@ public class GameController(
|
||||
/// <returns></returns>
|
||||
public SurveyResponseData GetSurvey(string sessionId)
|
||||
{
|
||||
return this._coreConfig.Survey;
|
||||
return _coreConfig.Survey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -301,10 +284,7 @@ public class GameController(
|
||||
private void WarnOnActiveBotReloadSkill(PmcData pmcProfile)
|
||||
{
|
||||
var botReloadSkill = _profileHelper.GetSkillFromProfile(pmcProfile, SkillTypes.BotReload);
|
||||
if (botReloadSkill?.Progress > 0)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("server_start_player_active_botreload_skill"));
|
||||
}
|
||||
if (botReloadSkill?.Progress > 0) _logger.Warning(_localisationService.GetText("server_start_player_active_botreload_skill"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -323,40 +303,35 @@ public class GameController(
|
||||
// Base values
|
||||
double energyRegenPerHour = 60;
|
||||
double hydrationRegenPerHour = 60;
|
||||
double hpRegenPerHour = 456.6;
|
||||
var hpRegenPerHour = 456.6;
|
||||
|
||||
// Set new values, whatever is smallest
|
||||
energyRegenPerHour += pmcProfile.Bonuses!
|
||||
.Where(bonus => bonus.Type == BonusType.EnergyRegeneration)
|
||||
.Aggregate(0d, (sum, bonus) => sum + (bonus.Value!.Value));
|
||||
|
||||
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
|
||||
|
||||
hydrationRegenPerHour += pmcProfile.Bonuses!
|
||||
.Where(bonus => bonus.Type == BonusType.HydrationRegeneration)
|
||||
.Aggregate(0d, (sum, bonus) => sum + (bonus.Value!.Value));
|
||||
|
||||
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
|
||||
|
||||
hpRegenPerHour += pmcProfile.Bonuses!
|
||||
.Where(bonus => bonus.Type == BonusType.HealthRegeneration)
|
||||
.Aggregate(0d, (sum, bonus) => sum + (bonus.Value!.Value));
|
||||
.Aggregate(0d, (sum, bonus) => sum + bonus.Value!.Value);
|
||||
|
||||
// Player has energy deficit
|
||||
if (Math.Abs((pmcProfile.Health?.Energy?.Current - pmcProfile.Health?.Energy?.Maximum) ?? 1) <= _deviation)
|
||||
if (Math.Abs(pmcProfile.Health?.Energy?.Current - pmcProfile.Health?.Energy?.Maximum ?? 1) <= _deviation)
|
||||
{
|
||||
// Set new value, whatever is smallest
|
||||
pmcProfile.Health!.Energy!.Current += Math.Round(energyRegenPerHour * (diffSeconds!.Value / 3600));
|
||||
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum)
|
||||
{
|
||||
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
|
||||
}
|
||||
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum) pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
|
||||
}
|
||||
|
||||
// Player has hydration deficit
|
||||
if (Math.Abs((pmcProfile.Health?.Hydration?.Current - pmcProfile.Health?.Hydration?.Maximum) ?? 1) <= _deviation)
|
||||
if (Math.Abs(pmcProfile.Health?.Hydration?.Current - pmcProfile.Health?.Hydration?.Maximum ?? 1) <= _deviation)
|
||||
{
|
||||
pmcProfile.Health!.Hydration!.Current += Math.Round(hydrationRegenPerHour * (diffSeconds!.Value / 3600));
|
||||
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum)
|
||||
{
|
||||
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all body parts
|
||||
@@ -364,21 +339,12 @@ public class GameController(
|
||||
.Select(bodyPartKvP => bodyPartKvP.Value))
|
||||
{
|
||||
// Check part hp
|
||||
if (bodyPart.Health!.Current < bodyPart.Health.Maximum)
|
||||
{
|
||||
bodyPart.Health.Current += Math.Round(hpRegenPerHour * (diffSeconds!.Value / 3600));
|
||||
}
|
||||
if (bodyPart.Health!.Current < bodyPart.Health.Maximum) bodyPart.Health.Current += Math.Round(hpRegenPerHour * (diffSeconds!.Value / 3600));
|
||||
|
||||
if (bodyPart.Health.Current > bodyPart.Health.Maximum)
|
||||
{
|
||||
bodyPart.Health.Current = bodyPart.Health.Maximum;
|
||||
}
|
||||
if (bodyPart.Health.Current > bodyPart.Health.Maximum) bodyPart.Health.Current = bodyPart.Health.Maximum;
|
||||
|
||||
|
||||
if (bodyPart.Effects is null || bodyPart.Effects.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (bodyPart.Effects is null || bodyPart.Effects.Count == 0) continue;
|
||||
|
||||
// Look for effects
|
||||
foreach (var effectKvP in bodyPart.Effects)
|
||||
@@ -387,10 +353,7 @@ public class GameController(
|
||||
if (effectKvP.Value.Time < 1)
|
||||
{
|
||||
// More than 30 mins has passed
|
||||
if (diffSeconds > 1800)
|
||||
{
|
||||
bodyPart.Effects.Remove(effectKvP.Key);
|
||||
}
|
||||
if (diffSeconds > 1800) bodyPart.Effects.Remove(effectKvP.Key);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -398,10 +361,8 @@ public class GameController(
|
||||
// Decrement effect time value by difference between current time and time health was last updated
|
||||
effectKvP.Value.Time -= diffSeconds;
|
||||
if (effectKvP.Value.Time < 1)
|
||||
{
|
||||
// Effect time was sub 1, set floor it can be
|
||||
effectKvP.Value.Time = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,16 +382,10 @@ public class GameController(
|
||||
var currentTimeStamp = _timeUtil.GetTimeStamp();
|
||||
|
||||
// One day post-profile creation
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds)
|
||||
{
|
||||
_giftService.SendPraporStartingGift(pmcProfile.SessionId!, 1);
|
||||
}
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds) _giftService.SendPraporStartingGift(pmcProfile.SessionId!, 1);
|
||||
|
||||
// Two day post-profile creation
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2)
|
||||
{
|
||||
_giftService.SendPraporStartingGift(pmcProfile.SessionId!, 2);
|
||||
}
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2) _giftService.SendPraporStartingGift(pmcProfile.SessionId!, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -438,7 +393,7 @@ public class GameController(
|
||||
* @param pmcProfile Player profile
|
||||
*/
|
||||
protected void SendMechanicGiftsToNewProfile(PmcData pmcProfile)
|
||||
{
|
||||
{
|
||||
_giftService.SendGiftWithSilentReceivedCheck("MechanicGiftDay1", pmcProfile.SessionId, 1);
|
||||
}
|
||||
|
||||
@@ -495,27 +450,16 @@ public class GameController(
|
||||
var bots = _databaseService.GetBots().Types;
|
||||
|
||||
// Official names can only be 15 chars in length
|
||||
if (playerName.Length > _botConfig.BotNameLengthLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (playerName.Length > _botConfig.BotNameLengthLimit) return;
|
||||
|
||||
// Skip if player name exists already
|
||||
if (bots!.TryGetValue("bear", out var bearBot))
|
||||
{
|
||||
if (bearBot is not null && bearBot.FirstNames!.Any(x => x == playerName))
|
||||
{
|
||||
bearBot.FirstNames!.Add(playerName);
|
||||
}
|
||||
}
|
||||
|
||||
if (bots.TryGetValue("bear", out var usecBot))
|
||||
{
|
||||
if (usecBot is not null && usecBot.FirstNames!.Any(x => x == playerName))
|
||||
{
|
||||
usecBot.FirstNames!.Add(playerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,10 +469,7 @@ public class GameController(
|
||||
/// <param name="fullProfile">Profile to check for dialog in</param>
|
||||
private void CheckForAndRemoveUndefinedDialogues(SptProfile fullProfile)
|
||||
{
|
||||
if (fullProfile.DialogueRecords!.TryGetValue("undefined", out var _))
|
||||
{
|
||||
fullProfile.DialogueRecords.Remove("undefined");
|
||||
}
|
||||
if (fullProfile.DialogueRecords!.TryGetValue("undefined", out _)) fullProfile.DialogueRecords.Remove("undefined");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -540,7 +481,7 @@ public class GameController(
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Profile made with: {fullProfile.SptData?.Version}");
|
||||
_logger.Debug($"Server version: {(ProgramStatics.SPT_VERSION()) ?? _coreConfig.SptVersion} {ProgramStatics.COMMIT()}");
|
||||
_logger.Debug($"Server version: {ProgramStatics.SPT_VERSION() ?? _coreConfig.SptVersion} {ProgramStatics.COMMIT()}");
|
||||
_logger.Debug($"Debug enabled: {ProgramStatics.DEBUG()}");
|
||||
_logger.Debug($"Mods enabled: {ProgramStatics.MODS()}");
|
||||
}
|
||||
|
||||
@@ -69,15 +69,12 @@ public class HealthController(
|
||||
}
|
||||
|
||||
// Resource in medkit is spent, delete it
|
||||
if (healingItemToUse.Upd.MedKit.HpResource <= 0)
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, request.Item, sessionID, output);
|
||||
}
|
||||
if (healingItemToUse.Upd.MedKit.HpResource <= 0) _inventoryHelper.RemoveItem(pmcData, request.Item, sessionID, output);
|
||||
|
||||
var healingItemDbDetails = _itemHelper.GetItem(healingItemToUse.Template);
|
||||
|
||||
var healItemEffectDetails = healingItemDbDetails.Value.Properties.EffectsDamage;
|
||||
BodyPartHealth bodyPartToHeal = pmcData.Health.BodyParts.GetValueOrDefault(request.Part.ToString());
|
||||
var bodyPartToHeal = pmcData.Health.BodyParts.GetValueOrDefault(request.Part.ToString());
|
||||
if (bodyPartToHeal is null)
|
||||
{
|
||||
_logger.Warning($"Player: {sessionID} Tried to heal a non-existent body part: {request.Part}");
|
||||
@@ -98,10 +95,8 @@ public class HealthController(
|
||||
{
|
||||
// Check if healing item removes the effect on limb
|
||||
if (!healItemEffectDetails.TryGetValue(Enum.Parse<DamageEffectType>(effectKey), out var matchingEffectFromHealingItem))
|
||||
{
|
||||
// Healing item doesn't have matching effect, it doesn't remove the effect
|
||||
continue;
|
||||
}
|
||||
|
||||
// Adjust limb heal amount based on if its fixing an effect (request.count is TOTAL cost of hp resource on heal item, NOT amount to heal limb)
|
||||
amountToHealLimb -= (int)(matchingEffectFromHealingItem.Cost ?? 0);
|
||||
@@ -113,10 +108,7 @@ public class HealthController(
|
||||
bodyPartToHeal.Health.Current += amountToHealLimb;
|
||||
|
||||
// Ensure we've not healed beyond the limbs max hp
|
||||
if (bodyPartToHeal.Health.Current > bodyPartToHeal.Health.Maximum)
|
||||
{
|
||||
bodyPartToHeal.Health.Current = bodyPartToHeal.Health.Maximum;
|
||||
}
|
||||
if (bodyPartToHeal.Health.Current > bodyPartToHeal.Health.Maximum) bodyPartToHeal.Health.Current = bodyPartToHeal.Health.Maximum;
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -139,13 +131,11 @@ public class HealthController(
|
||||
|
||||
var itemToConsume = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
|
||||
if (itemToConsume is null)
|
||||
{
|
||||
// Item not found, very bad
|
||||
return _httpResponseUtil.AppendErrorToOutput(
|
||||
output,
|
||||
_localisationService.GetText("health-unable_to_find_item_to_consume", request.Item)
|
||||
);
|
||||
}
|
||||
|
||||
var consumedItemMaxResource = _itemHelper.GetItem(itemToConsume.Template).Value.Properties.MaxResource;
|
||||
if (consumedItemMaxResource > 1)
|
||||
@@ -154,22 +144,15 @@ public class HealthController(
|
||||
_itemHelper.AddUpdObjectToItem(itemToConsume);
|
||||
|
||||
if (itemToConsume.Upd.FoodDrink is null)
|
||||
{
|
||||
itemToConsume.Upd.FoodDrink = new UpdFoodDrink { HpPercent = consumedItemMaxResource - request.Count };
|
||||
}
|
||||
else
|
||||
{
|
||||
itemToConsume.Upd.FoodDrink.HpPercent -= request.Count;
|
||||
}
|
||||
|
||||
resourceLeft = itemToConsume.Upd.FoodDrink.HpPercent.Value;
|
||||
}
|
||||
|
||||
// Remove item from inventory if resource has dropped below threshold
|
||||
if (consumedItemMaxResource == 1 || resourceLeft < 1)
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, request.Item, sessionID, output);
|
||||
}
|
||||
if (consumedItemMaxResource == 1 || resourceLeft < 1) _inventoryHelper.RemoveItem(pmcData, request.Item, sessionID, output);
|
||||
|
||||
// Check what effect eating item has and handle
|
||||
var foodItemDbDetails = _itemHelper.GetItem(itemToConsume.Template).Value;
|
||||
@@ -198,14 +181,10 @@ public class HealthController(
|
||||
OffraidEatRequestData request)
|
||||
{
|
||||
if (foodIsSingleUse)
|
||||
{
|
||||
// Apply whole value from passed in parameter
|
||||
bodyValue.Current += consumptionDetails.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyValue.Current += request.Count;
|
||||
}
|
||||
|
||||
// Ensure current never goes over max
|
||||
if (bodyValue.Current > bodyValue.Maximum)
|
||||
@@ -216,10 +195,7 @@ public class HealthController(
|
||||
}
|
||||
|
||||
// Same as above but for the lower bound
|
||||
if (bodyValue.Current < 0)
|
||||
{
|
||||
bodyValue.Current = 0;
|
||||
}
|
||||
if (bodyValue.Current < 0) bodyValue.Current = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -248,10 +224,7 @@ public class HealthController(
|
||||
};
|
||||
|
||||
_paymentService.PayMoney(pmcData, payMoneyRequest, sessionID, output);
|
||||
if (output.Warnings.Count > 0)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (output.Warnings.Count > 0) return output;
|
||||
|
||||
foreach (var bodyPartKvP in healthTreatmentRequest.Difference.BodyParts.GetAllPropsAsDict())
|
||||
{
|
||||
@@ -261,25 +234,17 @@ public class HealthController(
|
||||
|
||||
// Bodypart healing is chosen when part request hp is above 0
|
||||
if (partRequest.Health > 0)
|
||||
{
|
||||
// Heal bodypart
|
||||
profilePart.Health.Current = profilePart.Health.Maximum;
|
||||
}
|
||||
|
||||
// Check for effects to remove
|
||||
if (partRequest.Effects?.Count > 0)
|
||||
{
|
||||
// Found some, loop over them and remove from pmc profile
|
||||
foreach (var effect in partRequest.Effects)
|
||||
{
|
||||
pmcData.Health.BodyParts[bodyPartKvP.Key].Effects.Remove(effect);
|
||||
}
|
||||
foreach (var effect in partRequest.Effects) pmcData.Health.BodyParts[bodyPartKvP.Key].Effects.Remove(effect);
|
||||
|
||||
// Remove empty effect object
|
||||
if (pmcData.Health.BodyParts[bodyPartKvP.Key].Effects.Count == 0)
|
||||
{
|
||||
pmcData.Health.BodyParts[bodyPartKvP.Key].Effects = null;
|
||||
}
|
||||
if (pmcData.Health.BodyParts[bodyPartKvP.Key].Effects.Count == 0) pmcData.Health.BodyParts[bodyPartKvP.Key].Effects = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ public class HideoutController(
|
||||
ConfigServer _configServer
|
||||
)
|
||||
{
|
||||
protected HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
public const string NameTaskConditionCountersCraftingId = "673f5d6fdd6ed700c703afdc";
|
||||
|
||||
protected List<HideoutAreas> _hideoutAreas =
|
||||
@@ -54,9 +53,11 @@ public class HideoutController(
|
||||
HideoutAreas.AIR_FILTERING,
|
||||
HideoutAreas.WATER_COLLECTOR,
|
||||
HideoutAreas.GENERATOR,
|
||||
HideoutAreas.BITCOIN_FARM,
|
||||
HideoutAreas.BITCOIN_FARM
|
||||
];
|
||||
|
||||
protected HideoutConfig _hideoutConfig = _configServer.GetConfig<HideoutConfig>();
|
||||
|
||||
public void StartUpgrade(PmcData pmcData, HideoutUpgradeRequestData request, string sessionID, ItemEventRouterResponse output)
|
||||
{
|
||||
var items = request.Items.Select(
|
||||
@@ -87,13 +88,9 @@ public class HideoutController(
|
||||
item.inventoryItem.Upd.StackObjectsCount is not null &&
|
||||
item.inventoryItem.Upd.StackObjectsCount > item.requestedItem.Count
|
||||
)
|
||||
{
|
||||
item.inventoryItem.Upd.StackObjectsCount -= item.requestedItem.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, item.inventoryItem.Id, sessionID, output);
|
||||
}
|
||||
}
|
||||
|
||||
// Construction time management
|
||||
@@ -122,10 +119,7 @@ public class HideoutController(
|
||||
var ctime = hideoutDataDb.Stages[(profileHideoutArea.Level + 1).ToString()].ConstructionTime;
|
||||
if (ctime > 0)
|
||||
{
|
||||
if (_profileHelper.IsDeveloperAccount(sessionID))
|
||||
{
|
||||
ctime = 40;
|
||||
}
|
||||
if (_profileHelper.IsDeveloperAccount(sessionID)) ctime = 40;
|
||||
|
||||
var timestamp = _timeUtil.GetTimeStamp();
|
||||
|
||||
@@ -168,16 +162,11 @@ public class HideoutController(
|
||||
var hideoutStage = hideoutData.Stages[profileHideoutArea.Level.ToString()];
|
||||
var bonuses = hideoutStage.Bonuses;
|
||||
if (bonuses?.Count > 0)
|
||||
{
|
||||
foreach (var bonus in bonuses)
|
||||
{
|
||||
_hideoutHelper.ApplyPlayerUpgradesBonuses(pmcData, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade includes a container improvement/addition
|
||||
if (!string.IsNullOrEmpty(hideoutStage?.Container))
|
||||
{
|
||||
AddContainerImprovementToProfile(
|
||||
output,
|
||||
sessionID,
|
||||
@@ -186,22 +175,17 @@ public class HideoutController(
|
||||
hideoutData,
|
||||
hideoutStage
|
||||
);
|
||||
}
|
||||
|
||||
// Upgrading water collector / med station
|
||||
if (
|
||||
profileHideoutArea.Type == HideoutAreas.WATER_COLLECTOR ||
|
||||
profileHideoutArea.Type == HideoutAreas.MEDSTATION
|
||||
)
|
||||
{
|
||||
SetWallVisibleIfPrereqsMet(pmcData);
|
||||
}
|
||||
|
||||
// Cleanup temporary buffs/debuffs from wall if complete
|
||||
if (profileHideoutArea.Type == HideoutAreas.EMERGENCY_WALL && profileHideoutArea.Level == 6)
|
||||
{
|
||||
_hideoutHelper.RemoveHideoutWallBuffsAndDebuffs(hideoutData, pmcData);
|
||||
}
|
||||
|
||||
// Add Skill Points Per Area Upgrade
|
||||
_profileHelper.AddSkillPointsToPlayer(
|
||||
@@ -218,10 +202,7 @@ public class HideoutController(
|
||||
if (medStation?.Level >= 1 && waterCollector?.Level >= 1)
|
||||
{
|
||||
var wall = pmcData.Hideout.Areas.FirstOrDefault((area) => area.Type == HideoutAreas.EMERGENCY_WALL);
|
||||
if (wall?.Level == 0)
|
||||
{
|
||||
wall.Level = 3;
|
||||
}
|
||||
if (wall?.Level == 0) wall.Level = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,27 +211,21 @@ public class HideoutController(
|
||||
{
|
||||
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
||||
if (!pmcData.Inventory.HideoutAreaStashes.ContainsKey(dbHideoutArea.Type.ToString()))
|
||||
{
|
||||
pmcData.Inventory.HideoutAreaStashes[dbHideoutArea.Type.ToString()] = dbHideoutArea.Id;
|
||||
}
|
||||
|
||||
// Add/upgrade stash item in player inventory
|
||||
AddUpdateInventoryItemToProfile(sessionID, pmcData, dbHideoutArea, hideoutStage);
|
||||
|
||||
// Edge case, add/update `stand1/stand2/stand3` children
|
||||
if (dbHideoutArea.Type == HideoutAreas.EQUIPMENT_PRESETS_STAND)
|
||||
{
|
||||
// Can have multiple 'standx' children depending on upgrade level
|
||||
AddMissingPresetStandItemsToProfile(sessionID, hideoutStage, pmcData, dbHideoutArea, output);
|
||||
}
|
||||
|
||||
// Dont inform client when upgraded area is hall of fame or equipment stand, BSG doesn't inform client this specifc upgrade has occurred
|
||||
// will break client if sent
|
||||
List<HideoutAreas> check = [HideoutAreas.PLACE_OF_FAME];
|
||||
if (!check.Contains(dbHideoutArea.Type ?? HideoutAreas.NOTSET))
|
||||
{
|
||||
AddContainerUpgradeToClientOutput(sessionID, dbHideoutArea.Type, dbHideoutArea, hideoutStage, output);
|
||||
}
|
||||
|
||||
// Some hideout areas (Gun stand) have child areas linked to it
|
||||
var childDbArea = _databaseService
|
||||
@@ -260,9 +235,7 @@ public class HideoutController(
|
||||
{
|
||||
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
|
||||
if (pmcData.Inventory.HideoutAreaStashes.GetValueOrDefault(childDbArea.Type.ToString()) is null)
|
||||
{
|
||||
pmcData.Inventory.HideoutAreaStashes[childDbArea.Type.ToString()] = childDbArea.Id;
|
||||
}
|
||||
|
||||
// Set child area level to same as parent area
|
||||
pmcData.Hideout.Areas.FirstOrDefault((hideoutArea) => hideoutArea.Type == childDbArea.Type).Level =
|
||||
@@ -297,15 +270,13 @@ public class HideoutController(
|
||||
ItemEventRouterResponse output)
|
||||
{
|
||||
if (output.ProfileChanges[sessionID].ChangedHideoutStashes is null)
|
||||
{
|
||||
output.ProfileChanges[sessionID].ChangedHideoutStashes = new Dictionary<string, HideoutStashItem>();
|
||||
}
|
||||
|
||||
// Inform client of changes
|
||||
output.ProfileChanges[sessionID].ChangedHideoutStashes[areaType.ToString()] = new HideoutStashItem
|
||||
{
|
||||
Id = hideoutDbData.Id,
|
||||
Template = hideoutStage.Container,
|
||||
Template = hideoutStage.Container
|
||||
};
|
||||
}
|
||||
|
||||
@@ -370,7 +341,7 @@ public class HideoutController(
|
||||
Id = item.inventoryItem.Id,
|
||||
Template = item.inventoryItem.Template,
|
||||
Upd = item.inventoryItem.Upd
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
_inventoryHelper.RemoveItem(pmcData, item.inventoryItem.Id, sessionID, output);
|
||||
@@ -445,15 +416,13 @@ public class HideoutController(
|
||||
ItemWithModsToAdd = [itemToReturn.ConvertToItem()],
|
||||
FoundInRaid = itemToReturn.Upd?.SpawnedInSession,
|
||||
Callback = null,
|
||||
UseSortingTable = false,
|
||||
UseSortingTable = false
|
||||
};
|
||||
|
||||
_inventoryHelper.AddItemToStash(sessionID, request, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
// Adding to stash failed, drop out - don't remove item from hideout area slot
|
||||
return output;
|
||||
}
|
||||
|
||||
// Remove items from slot, locationIndex remains
|
||||
var hideoutSlotIndex = hideoutArea.Slots.FindIndex((slot) => slot.LocationIndex == slotIndexToRemove);
|
||||
@@ -510,18 +479,12 @@ public class HideoutController(
|
||||
|
||||
// Handle tools not having a `count`, but always only requiring 1
|
||||
var requiredCount = requirement.Count ?? 1;
|
||||
if (requiredCount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (requiredCount <= 0) continue;
|
||||
|
||||
_inventoryHelper.RemoveItemByCount(pmcData, itemToDelete.Id, requiredCount, sessionID, output);
|
||||
|
||||
// Tools don't have a count
|
||||
if (requirement.Type != "Tool")
|
||||
{
|
||||
requirement.Count -= (int)itemToDelete.Count;
|
||||
}
|
||||
if (requirement.Type != "Tool") requirement.Count -= (int)itemToDelete.Count;
|
||||
}
|
||||
|
||||
return output;
|
||||
@@ -546,13 +509,9 @@ public class HideoutController(
|
||||
}
|
||||
|
||||
if (inventoryItem.Upd?.StackObjectsCount is not null && inventoryItem.Upd.StackObjectsCount > requestedItem.Count)
|
||||
{
|
||||
inventoryItem.Upd.StackObjectsCount -= requestedItem.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, requestedItem.Id, sessionID, output);
|
||||
}
|
||||
}
|
||||
|
||||
var recipe = _databaseService.GetHideout().Production?.ScavRecipes?.FirstOrDefault(r => r.Id == body.RecipeId);
|
||||
@@ -592,10 +551,7 @@ public class HideoutController(
|
||||
private double? GetScavCaseTime(PmcData pmcData, double? productionTime)
|
||||
{
|
||||
var fenceLevel = _fenceService.GetFenceInfo(pmcData);
|
||||
if (fenceLevel is null)
|
||||
{
|
||||
return productionTime;
|
||||
}
|
||||
if (fenceLevel is null) return productionTime;
|
||||
|
||||
return productionTime * fenceLevel.ScavCaseTimeModifier;
|
||||
}
|
||||
@@ -661,10 +617,7 @@ public class HideoutController(
|
||||
foreach (var production in productionDict)
|
||||
{
|
||||
// Skip undefined production objects
|
||||
if (production.Value is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (production.Value is null) continue;
|
||||
|
||||
// Production or ScavCase
|
||||
if (production.Value.RecipeId == request.RecipeId)
|
||||
@@ -723,11 +676,11 @@ public class HideoutController(
|
||||
if (rewardIsStackable ?? false)
|
||||
{
|
||||
// Create root item
|
||||
Item rewardToAdd = new Item
|
||||
var rewardToAdd = new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = recipe.EndProduct,
|
||||
Upd = new Upd { StackObjectsCount = recipe.Count },
|
||||
Upd = new Upd { StackObjectsCount = recipe.Count }
|
||||
};
|
||||
|
||||
// Split item into separate items with acceptable stack sizes
|
||||
@@ -739,50 +692,39 @@ public class HideoutController(
|
||||
// Not stackable, may have to send send multiple of reward
|
||||
|
||||
// Add the first reward item to array when not a preset (first preset added above earlier)
|
||||
if (!rewardIsPreset)
|
||||
{
|
||||
itemAndChildrenToSendToPlayer.Add([new Item { Id = _hashUtil.Generate(), Template = recipe.EndProduct }]);
|
||||
}
|
||||
if (!rewardIsPreset) itemAndChildrenToSendToPlayer.Add([new Item { Id = _hashUtil.Generate(), Template = recipe.EndProduct }]);
|
||||
|
||||
// Add multiple of item if recipe requests it
|
||||
// Start index at one so we ignore first item in array
|
||||
var countOfItemsToReward = recipe.Count;
|
||||
for (var index = 1; index < countOfItemsToReward; index++)
|
||||
{
|
||||
List<Item> itemAndMods = _itemHelper.ReplaceIDs(_cloner.Clone(itemAndChildrenToSendToPlayer.FirstOrDefault()));
|
||||
var itemAndMods = _itemHelper.ReplaceIDs(_cloner.Clone(itemAndChildrenToSendToPlayer.FirstOrDefault()));
|
||||
itemAndChildrenToSendToPlayer.AddRange([itemAndMods]);
|
||||
}
|
||||
}
|
||||
|
||||
// Recipe has an `isEncoded` requirement for reward(s), Add `RecodableComponent` property
|
||||
if (recipe.IsEncoded ?? false)
|
||||
{
|
||||
foreach (var reward in itemAndChildrenToSendToPlayer)
|
||||
{
|
||||
_itemHelper.AddUpdObjectToItem(reward.FirstOrDefault());
|
||||
|
||||
reward.FirstOrDefault().Upd.RecodableComponent = new UpdRecodableComponent { IsEncoded = true };
|
||||
}
|
||||
}
|
||||
|
||||
// Build an array of the tools that need to be returned to the player
|
||||
List<List<Item>> toolsToSendToPlayer = [];
|
||||
var hideoutProduction = pmcData.Hideout.Production[prodId];
|
||||
if (hideoutProduction.SptRequiredTools?.Count > 0)
|
||||
{
|
||||
foreach (var tool in hideoutProduction.SptRequiredTools)
|
||||
{
|
||||
toolsToSendToPlayer.Add([tool]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times
|
||||
var area = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == recipe.AreaType);
|
||||
if (area is not null && request.RecipeId != area.LastRecipe)
|
||||
{
|
||||
// 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module
|
||||
craftingExpAmount += _hideoutConfig.ExpCraftAmount; // Default is 10
|
||||
}
|
||||
|
||||
// Update variable with time spent crafting item(s)
|
||||
// 1 point per 8 hours of crafting
|
||||
@@ -814,34 +756,28 @@ public class HideoutController(
|
||||
foreach (var toolItem in toolsToSendToPlayer)
|
||||
{
|
||||
// Note: FIR state will be based on the first item's SpawnedInSession property per item group
|
||||
AddItemsDirectRequest addToolsRequest = new AddItemsDirectRequest
|
||||
var addToolsRequest = new AddItemsDirectRequest
|
||||
{
|
||||
ItemsWithModsToAdd = [toolItem],
|
||||
FoundInRaid = toolItem[0].Upd?.SpawnedInSession ?? false,
|
||||
UseSortingTable = false,
|
||||
Callback = null,
|
||||
Callback = null
|
||||
};
|
||||
|
||||
_inventoryHelper.AddItemsToStash(sessionID, addToolsRequest, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return;
|
||||
}
|
||||
|
||||
// Add the crafting result to the stash, marked as FiR
|
||||
AddItemsDirectRequest addItemsRequest = new AddItemsDirectRequest
|
||||
var addItemsRequest = new AddItemsDirectRequest
|
||||
{
|
||||
ItemsWithModsToAdd = itemAndChildrenToSendToPlayer,
|
||||
FoundInRaid = true,
|
||||
UseSortingTable = false,
|
||||
Callback = null,
|
||||
Callback = null
|
||||
};
|
||||
_inventoryHelper.AddItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return;
|
||||
|
||||
// - increment skill point for crafting
|
||||
// - delete the production in profile Hideout.Production
|
||||
@@ -861,10 +797,7 @@ public class HideoutController(
|
||||
_profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Crafting, craftingExpAmount);
|
||||
|
||||
var intellectAmountToGive = 0.5 * Math.Round((double)(craftingExpAmount / 15));
|
||||
if (intellectAmountToGive > 0)
|
||||
{
|
||||
_profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Intellect, intellectAmountToGive);
|
||||
}
|
||||
if (intellectAmountToGive > 0) _profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Intellect, intellectAmountToGive);
|
||||
}
|
||||
|
||||
area.LastRecipe = request.RecipeId;
|
||||
@@ -879,36 +812,29 @@ public class HideoutController(
|
||||
// Continious recipies need the craft time refreshed as it gets created once on initial craft and stays the same regardless of what
|
||||
// production.json is set to
|
||||
if (recipe.Continuous ?? false)
|
||||
{
|
||||
pmcData.Hideout.Production[prodId].ProductionTime = _hideoutHelper.GetAdjustedCraftTimeWithSkills(
|
||||
pmcData,
|
||||
recipe.Id,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// Flag normal (non continious) crafts as complete
|
||||
if (!recipe.Continuous ?? false)
|
||||
{
|
||||
pmcData.Hideout.Production[prodId].InProgress = false;
|
||||
}
|
||||
if (!recipe.Continuous ?? false) pmcData.Hideout.Production[prodId].InProgress = false;
|
||||
}
|
||||
|
||||
private TaskConditionCounter GetHoursCraftingTaskConditionCounter(PmcData pmcData, HideoutProduction recipe)
|
||||
{
|
||||
if (!pmcData.TaskConditionCounters.TryGetValue(HideoutController.NameTaskConditionCountersCraftingId, out var _))
|
||||
{
|
||||
if (!pmcData.TaskConditionCounters.TryGetValue(NameTaskConditionCountersCraftingId, out _))
|
||||
// Doesn't exist, create
|
||||
pmcData.TaskConditionCounters[HideoutController.NameTaskConditionCountersCraftingId] = new TaskConditionCounter
|
||||
pmcData.TaskConditionCounters[NameTaskConditionCountersCraftingId] = new TaskConditionCounter
|
||||
{
|
||||
Id = recipe.Id,
|
||||
Type = HideoutController.NameTaskConditionCountersCraftingId,
|
||||
Type = NameTaskConditionCountersCraftingId,
|
||||
SourceId = "CounterCrafting",
|
||||
Value = 0,
|
||||
Value = 0
|
||||
};
|
||||
}
|
||||
|
||||
return pmcData.TaskConditionCounters[HideoutController.NameTaskConditionCountersCraftingId];
|
||||
return pmcData.TaskConditionCounters[NameTaskConditionCountersCraftingId];
|
||||
}
|
||||
|
||||
private void HandleScavCase(string sessionID, PmcData pmcData, HideoutTakeProductionRequestData request, ItemEventRouterResponse output)
|
||||
@@ -916,14 +842,12 @@ public class HideoutController(
|
||||
var ongoingProductions = pmcData.Hideout.Production;
|
||||
string? prodId = null;
|
||||
foreach (var production in ongoingProductions)
|
||||
{
|
||||
// Production or ScavCase
|
||||
if ((production.Value).RecipeId == request.RecipeId)
|
||||
if (production.Value.RecipeId == request.RecipeId)
|
||||
{
|
||||
prodId = production.Key; // Set to objects key
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prodId == null)
|
||||
{
|
||||
@@ -942,19 +866,16 @@ public class HideoutController(
|
||||
// Create rewards for scav case
|
||||
var scavCaseRewards = _scavCaseRewardGenerator.Generate(request.RecipeId);
|
||||
|
||||
AddItemsDirectRequest addItemsRequest = new AddItemsDirectRequest
|
||||
var addItemsRequest = new AddItemsDirectRequest
|
||||
{
|
||||
ItemsWithModsToAdd = scavCaseRewards,
|
||||
FoundInRaid = true,
|
||||
Callback = null,
|
||||
UseSortingTable = false,
|
||||
UseSortingTable = false
|
||||
};
|
||||
|
||||
_inventoryHelper.AddItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return;
|
||||
|
||||
// Remove the old production from output object before its sent to client
|
||||
output.ProfileChanges[sessionID].Production.Remove(request.RecipeId);
|
||||
@@ -981,7 +902,6 @@ public class HideoutController(
|
||||
var qteDb = _databaseService.GetHideout().Qte;
|
||||
var relevantQte = qteDb.FirstOrDefault(qte => qte.Id == request.Id);
|
||||
foreach (var outcome in request.Results)
|
||||
{
|
||||
if (outcome)
|
||||
{
|
||||
// Success
|
||||
@@ -994,17 +914,10 @@ public class HideoutController(
|
||||
pmcData.Health.Energy.Current += relevantQte.Results[QteEffectType.singleFailEffect].Energy;
|
||||
pmcData.Health.Hydration.Current += relevantQte.Results[QteEffectType.singleFailEffect].Hydration;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmcData.Health.Energy.Current < 1)
|
||||
{
|
||||
pmcData.Health.Energy.Current = 1;
|
||||
}
|
||||
if (pmcData.Health.Energy.Current < 1) pmcData.Health.Energy.Current = 1;
|
||||
|
||||
if (pmcData.Health.Hydration.Current < 1)
|
||||
{
|
||||
pmcData.Health.Hydration.Current = 1;
|
||||
}
|
||||
if (pmcData.Health.Hydration.Current < 1) pmcData.Health.Hydration.Current = 1;
|
||||
|
||||
HandleMusclePain(pmcData, relevantQte.Results[QteEffectType.finishEffect]);
|
||||
}
|
||||
@@ -1021,7 +934,7 @@ public class HideoutController(
|
||||
pmcData.Health.BodyParts["Chest"].Effects ??= new Dictionary<string, BodyPartEffectProperties>();
|
||||
pmcData.Health.BodyParts["Chest"].Effects["MildMusclePain"] = new BodyPartEffectProperties
|
||||
{
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time, // TODO - remove hard coded access, get value properly
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time // TODO - remove hard coded access, get value properly
|
||||
};
|
||||
|
||||
return;
|
||||
@@ -1034,7 +947,7 @@ public class HideoutController(
|
||||
|
||||
pmcData.Health.BodyParts["Chest"].Effects["SevereMusclePain"] = new BodyPartEffectProperties
|
||||
{
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time,
|
||||
Time = finishEffect.RewardEffects.FirstOrDefault().Time
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1086,13 +999,9 @@ public class HideoutController(
|
||||
item.inventoryItem.Upd.StackObjectsCount is not null &&
|
||||
item.inventoryItem.Upd.StackObjectsCount > item.requestedItem.Count
|
||||
)
|
||||
{
|
||||
item.inventoryItem.Upd.StackObjectsCount -= item.requestedItem.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, item.inventoryItem.Id, sessionId, output);
|
||||
}
|
||||
}
|
||||
|
||||
var profileHideoutArea = pmcData.Hideout.Areas.FirstOrDefault(x => x.Type == request.AreaType);
|
||||
@@ -1115,17 +1024,14 @@ public class HideoutController(
|
||||
var improvements = hideoutDbData.Stages[profileHideoutArea.Level.ToString()].Improvements;
|
||||
var timestamp = _timeUtil.GetTimeStamp();
|
||||
|
||||
if (output.ProfileChanges[sessionId].Improvements is null)
|
||||
{
|
||||
output.ProfileChanges[sessionId].Improvements = new Dictionary<string, HideoutImprovement>();
|
||||
}
|
||||
if (output.ProfileChanges[sessionId].Improvements is null) output.ProfileChanges[sessionId].Improvements = new Dictionary<string, HideoutImprovement>();
|
||||
|
||||
foreach (var improvement in improvements)
|
||||
{
|
||||
var improvementDetails = new HideoutImprovement
|
||||
{
|
||||
Completed = false,
|
||||
ImproveCompleteTimestamp = (long)(timestamp + improvement.ImprovementTime),
|
||||
ImproveCompleteTimestamp = (long)(timestamp + improvement.ImprovementTime)
|
||||
};
|
||||
output.ProfileChanges[sessionId].Improvements[improvement.Id] = improvementDetails;
|
||||
|
||||
@@ -1232,7 +1138,7 @@ public class HideoutController(
|
||||
Id = standId,
|
||||
Template = ItemTpl.INVENTORY_DEFAULT,
|
||||
ParentId = equipmentPresetHideoutArea.Id,
|
||||
SlotId = mannequinSlot.Name,
|
||||
SlotId = mannequinSlot.Name
|
||||
};
|
||||
pmcData.Inventory.Items.Add(mannequinToAdd);
|
||||
|
||||
@@ -1245,7 +1151,7 @@ public class HideoutController(
|
||||
)
|
||||
.Template, // Same pocket tpl as players profile (unheard get bigger, matching pockets etc)
|
||||
ParentId = standId,
|
||||
SlotId = "Pockets",
|
||||
SlotId = "Pockets"
|
||||
};
|
||||
pmcData.Inventory.Items.Add(mannequinPocketItemToAdd);
|
||||
output.ProfileChanges[sessionId].Items.NewItems.Add(mannequinToAdd);
|
||||
@@ -1290,16 +1196,12 @@ public class HideoutController(
|
||||
public void Update()
|
||||
{
|
||||
foreach (var sessionID in _saveServer.GetProfiles())
|
||||
{
|
||||
if (sessionID.Value.CharacterData.PmcData.Hideout is not null &&
|
||||
_profileActivityService.ActiveWithinLastMinutes(
|
||||
sessionID.Key,
|
||||
_hideoutConfig.UpdateProfileHideoutWhenActiveWithinMinutes
|
||||
)
|
||||
)
|
||||
{
|
||||
_hideoutHelper.UpdatePlayerHideout(sessionID.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ public class InRaidController(
|
||||
ConfigServer _configServer
|
||||
)
|
||||
{
|
||||
protected InRaidConfig _inRaidConfig = _configServer.GetConfig<InRaidConfig>();
|
||||
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
protected InRaidConfig _inRaidConfig = _configServer.GetConfig<InRaidConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Save locationId to active profiles in-raid object AND app context
|
||||
@@ -43,9 +43,7 @@ public class InRaidController(
|
||||
// If equipment match overwrite existing data from update to date raid data for scavenger screen to work correctly.
|
||||
// otherwise Scav inventory will be overwritten and break scav regeneration, breaking profile.
|
||||
if (serverScavProfile.Inventory.Equipment == offRaidProfileData.Inventory.Equipment)
|
||||
{
|
||||
serverScavProfile.Inventory.Items = offRaidProfileData.Inventory.Items;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -86,12 +86,8 @@ public class InsuranceController(
|
||||
|
||||
var profileInsuranceDetails = _saveServer.GetProfile(sessionId).InsuranceList;
|
||||
if (profileInsuranceDetails.Count > 0)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Found {profileInsuranceDetails.Count} insurance packages in profile {sessionId}");
|
||||
}
|
||||
}
|
||||
|
||||
return profileInsuranceDetails.Where(insured => insuranceTime >= insured.ScheduledTime).ToList();
|
||||
}
|
||||
@@ -105,12 +101,10 @@ public class InsuranceController(
|
||||
*/
|
||||
protected void ProcessInsuredItems(List<Insurance> insuranceDetails, string sessionId)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Processing {insuranceDetails.Count} insurance packages, which includes a total of: {CountAllInsuranceItems(insuranceDetails)} items, in profile: {sessionId}"
|
||||
);
|
||||
}
|
||||
|
||||
// Iterate over each of the insurance packages.
|
||||
foreach (var insured in insuranceDetails)
|
||||
@@ -170,10 +164,7 @@ public class InsuranceController(
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Removed processed insurance package. Remaining packages: {profile.InsuranceList.Count}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Removed processed insurance package. Remaining packages: {profile.InsuranceList.Count}");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,10 +189,7 @@ public class InsuranceController(
|
||||
);
|
||||
|
||||
// Process all items that are not attached, attachments; those are handled separately, by value.
|
||||
if (hasRegularItems)
|
||||
{
|
||||
ProcessRegularItems(insured, toDelete, parentAttachmentsMap);
|
||||
}
|
||||
if (hasRegularItems) ProcessRegularItems(insured, toDelete, parentAttachmentsMap);
|
||||
|
||||
// Process attached, attachments, by value, only if there are any.
|
||||
if (parentAttachmentsMap.Count > 0)
|
||||
@@ -215,12 +203,8 @@ public class InsuranceController(
|
||||
|
||||
// Log the number of items marked for deletion, if any
|
||||
if (!toDelete.Any())
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Marked {toDelete.Count} items for deletion from insurance.");
|
||||
}
|
||||
}
|
||||
|
||||
return toDelete;
|
||||
}
|
||||
@@ -253,7 +237,7 @@ public class InsuranceController(
|
||||
{
|
||||
insuredItemId = insuredItem.Id,
|
||||
insuredItemTpl = insuredItem.Template,
|
||||
parentId = insuredItem.ParentId,
|
||||
parentId = insuredItem.ParentId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -273,7 +257,7 @@ public class InsuranceController(
|
||||
new
|
||||
{
|
||||
insuredItemId = insuredItem.Id,
|
||||
insuredItemTpl = insuredItem.Template,
|
||||
insuredItemTpl = insuredItem.Template
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -293,7 +277,7 @@ public class InsuranceController(
|
||||
{
|
||||
insuredItemId = insuredItem.Id,
|
||||
insuredItemTpl = insuredItem.Template,
|
||||
parentId = insuredItem.ParentId,
|
||||
parentId = insuredItem.ParentId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -304,10 +288,7 @@ public class InsuranceController(
|
||||
// Update (or add to) the main-parent to attachments map.
|
||||
if (mainParentToAttachmentsMap.ContainsKey(mainParent.Id))
|
||||
{
|
||||
if (mainParentToAttachmentsMap.TryGetValue(mainParent.Id, out var parent))
|
||||
{
|
||||
parent.Add(insuredItem);
|
||||
}
|
||||
if (mainParentToAttachmentsMap.TryGetValue(mainParent.Id, out var parent)) parent.Add(insuredItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -344,24 +325,14 @@ public class InsuranceController(
|
||||
// attachment on the main-parent. For example, if the attachment is a stock, we need to check to see if
|
||||
// it's moddable in the upper receiver (attachment/parent), which is attached to the gun (main-parent).
|
||||
if (attachment.ParentId is not null)
|
||||
{
|
||||
if (itemsMap.TryGetValue(attachment.ParentId, out var directParentItem))
|
||||
{
|
||||
attachmentParentItem = directParentItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (_itemHelper.IsRaidModdable(attachment, attachmentParentItem) ?? false)
|
||||
{
|
||||
moddableAttachments.Add(attachment);
|
||||
}
|
||||
if (_itemHelper.IsRaidModdable(attachment, attachmentParentItem) ?? false) moddableAttachments.Add(attachment);
|
||||
}
|
||||
|
||||
// If any moddable attachments remain, add them to the updated map.
|
||||
if (moddableAttachments.Count > 0)
|
||||
{
|
||||
updatedMap.TryAdd(map.Key, moddableAttachments);
|
||||
}
|
||||
if (moddableAttachments.Count > 0) updatedMap.TryAdd(map.Key, moddableAttachments);
|
||||
}
|
||||
|
||||
return updatedMap;
|
||||
@@ -382,10 +353,7 @@ public class InsuranceController(
|
||||
foreach (var insuredItem in insured.Items)
|
||||
{
|
||||
// Skip if the item is an attachment. These are handled separately.
|
||||
if (_itemHelper.IsAttachmentAttached(insuredItem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_itemHelper.IsAttachmentAttached(insuredItem)) continue;
|
||||
|
||||
// Roll for item deletion
|
||||
var itemRoll = RollForDelete(insured.TraderId, insuredItem);
|
||||
@@ -401,10 +369,7 @@ public class InsuranceController(
|
||||
insured.Items,
|
||||
insuredItem.Id
|
||||
);
|
||||
foreach (var item in itemAndChildren)
|
||||
{
|
||||
toDelete.Add(item.Id);
|
||||
}
|
||||
foreach (var item in itemAndChildren) toDelete.Add(item.Id);
|
||||
|
||||
// Remove the parent (and its children) from the parentAttachmentsMap.
|
||||
parentAttachmentsMap.Remove(insuredItem.Id);
|
||||
@@ -433,18 +398,12 @@ public class InsuranceController(
|
||||
{
|
||||
// Skip processing if parentId is already marked for deletion, as all attachments for that parent will
|
||||
// already be marked for deletion as well.
|
||||
if (toDelete.Contains(parentObj.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (toDelete.Contains(parentObj.Key)) continue;
|
||||
|
||||
// Log the parent item's name.
|
||||
itemsMap.TryGetValue(parentObj.Key, out var parentItem);
|
||||
var parentName = _itemHelper.GetItemName(parentItem.Template);
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Processing attachments of parent {parentName}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Processing attachments of parent {parentName}");
|
||||
|
||||
// Process the attachments for this individual parent item.
|
||||
ProcessAttachmentByParent(parentObj.Value, insuredTraderId, toDelete);
|
||||
@@ -473,25 +432,17 @@ public class InsuranceController(
|
||||
// Create prob array and add all attachments with rouble price as the weight
|
||||
var attachmentsProbabilityArray = new ProbabilityObjectArray<string, double?>(_mathUtil, _cloner);
|
||||
foreach (var attachmentTpl in weightedAttachmentByPrice)
|
||||
{
|
||||
attachmentsProbabilityArray.Add(
|
||||
new ProbabilityObject<string, double?>(attachmentTpl.Key, attachmentTpl.Value, null)
|
||||
);
|
||||
}
|
||||
|
||||
// Draw x attachments from weighted array to remove from parent, remove from pool after being picked
|
||||
var attachmentIdsToRemove = attachmentsProbabilityArray.Draw((int)countOfAttachmentsToRemove, false);
|
||||
foreach (var attachmentId in attachmentIdsToRemove)
|
||||
{
|
||||
toDelete.Add(attachmentId);
|
||||
}
|
||||
foreach (var attachmentId in attachmentIdsToRemove) toDelete.Add(attachmentId);
|
||||
|
||||
LogAttachmentsBeingRemoved(attachmentIdsToRemove, attachments, weightedAttachmentByPrice);
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Number of attachments to be deleted: {attachmentIdsToRemove.Count}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Number of attachments to be deleted: {attachmentIdsToRemove.Count}");
|
||||
}
|
||||
|
||||
private void LogAttachmentsBeingRemoved(List<string> attachmentIdsToRemove, List<Item> attachments, Dictionary<string, double> attachmentPrices)
|
||||
@@ -500,12 +451,10 @@ public class InsuranceController(
|
||||
foreach (var attachmentId in attachmentIdsToRemove)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Attachment {index} Id: {attachmentId} Tpl: {attachments.FirstOrDefault((x) => x.Id == attachmentId)?.Template} - " +
|
||||
$"Price: {attachmentPrices[attachmentId]}"
|
||||
);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
@@ -518,10 +467,7 @@ public class InsuranceController(
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(attachment.Template, Money.ROUBLES);
|
||||
if (price is not null)
|
||||
{
|
||||
result[attachment.Id] = Math.Round(price ?? 0);
|
||||
}
|
||||
if (price is not null) result[attachment.Id] = Math.Round(price ?? 0);
|
||||
}
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(result);
|
||||
@@ -533,10 +479,7 @@ public class InsuranceController(
|
||||
{
|
||||
var removeCount = 0;
|
||||
|
||||
if (_randomUtil.GetChance100(_insuranceConfig.ChanceNoAttachmentsTakenPercent))
|
||||
{
|
||||
return removeCount;
|
||||
}
|
||||
if (_randomUtil.GetChance100(_insuranceConfig.ChanceNoAttachmentsTakenPercent)) return removeCount;
|
||||
|
||||
// Get attachments count above or equal to price set in config
|
||||
return weightedAttachmentByPrice
|
||||
@@ -564,19 +507,13 @@ public class InsuranceController(
|
||||
|
||||
// Map is labs + insurance is disabled in base.json
|
||||
if (IsMapLabsAndInsuranceDisabled(insurance))
|
||||
{
|
||||
// Trader has labs-specific messages
|
||||
// Wipe out returnable items
|
||||
HandleLabsInsurance(traderDialogMessages, insurance);
|
||||
}
|
||||
else if (insurance.Items?.Count == 0)
|
||||
{
|
||||
// Not labs and no items to return
|
||||
if (traderDialogMessages.TryGetValue("insuranceFailed", out var insuranceFailedTemplates))
|
||||
{
|
||||
insurance.MessageTemplateId = _randomUtil.GetArrayValue(insuranceFailedTemplates);
|
||||
}
|
||||
}
|
||||
|
||||
// Send the insurance message
|
||||
_mailSendService.SendLocalisedNpcMessageToPlayer(
|
||||
@@ -592,9 +529,8 @@ public class InsuranceController(
|
||||
|
||||
private bool IsMapLabsAndInsuranceDisabled(Insurance insurance, string labsId = "laboratory")
|
||||
{
|
||||
return (insurance.SystemData?.Location?.ToLower() == labsId
|
||||
&& !(_databaseService.GetLocation(labsId)?.Base?.Insurance.GetValueOrDefault(false) ?? false)
|
||||
);
|
||||
return insurance.SystemData?.Location?.ToLower() == labsId &&
|
||||
!(_databaseService.GetLocation(labsId)?.Base?.Insurance.GetValueOrDefault(false) ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -618,10 +554,7 @@ public class InsuranceController(
|
||||
private bool? RollForDelete(string traderId, Item? insuredItem = null)
|
||||
{
|
||||
var trader = _traderHelper.GetTraderById(traderId);
|
||||
if (trader is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (trader is null) return null;
|
||||
|
||||
const int maxRoll = 9999;
|
||||
const int conversionFactor = 100;
|
||||
@@ -633,10 +566,8 @@ public class InsuranceController(
|
||||
// Log the roll with as much detail as possible.
|
||||
var itemName = insuredItem is not null ? $"{_itemHelper.GetItemName(insuredItem.Template)}" : "";
|
||||
var status = roll ? "Delete" : "Keep";
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Rolling {itemName} with {trader} - Return {traderReturnChance}% - Roll: {returnChance} - Status: {status}");
|
||||
}
|
||||
|
||||
return roll;
|
||||
}
|
||||
@@ -661,7 +592,6 @@ public class InsuranceController(
|
||||
|
||||
// Get price of all items being insured, add to 'itemsToPay'
|
||||
foreach (var key in body.Items)
|
||||
{
|
||||
itemsToPay.Add(
|
||||
new IdWithCount
|
||||
{
|
||||
@@ -673,7 +603,6 @@ public class InsuranceController(
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var options = new ProcessBuyTradeRequestData
|
||||
{
|
||||
@@ -688,10 +617,7 @@ public class InsuranceController(
|
||||
|
||||
// pay for the item insurance
|
||||
_paymentService.PayMoney(pmcData, options, sessionId, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return output;
|
||||
|
||||
// add items to InsuredItems list once money has been paid
|
||||
pmcData.InsuredItems ??= [];
|
||||
@@ -699,10 +625,7 @@ public class InsuranceController(
|
||||
{
|
||||
pmcData.InsuredItems.Add(new InsuredItem { TId = body.TransactionId, ItemId = inventoryItemsHash[key].Id });
|
||||
// If Item is Helmet or Body Armour -> Handle insurance of soft inserts
|
||||
if (_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(inventoryItemsHash[key].Template))
|
||||
{
|
||||
InsureSoftInserts(inventoryItemsHash[key], pmcData, body);
|
||||
}
|
||||
if (_itemHelper.ArmorItemHasRemovableOrSoftInsertSlots(inventoryItemsHash[key].Template)) InsureSoftInserts(inventoryItemsHash[key], pmcData, body);
|
||||
}
|
||||
|
||||
_profileHelper.AddSkillPointsToPlayer(pmcData, SkillTypes.Charisma, itemsToInsureCount * 0.01);
|
||||
@@ -726,11 +649,8 @@ public class InsuranceController(
|
||||
|
||||
foreach (var softInsertSlot in softInsertSlots)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"SoftInsertSlots: {softInsertSlot.SlotId}");
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"SoftInsertSlots: {softInsertSlot.SlotId}");
|
||||
|
||||
pmcData.InsuredItems.Add(new InsuredItem { TId = body.TransactionId, ItemId = softInsertSlot.Id });
|
||||
}
|
||||
}
|
||||
@@ -761,10 +681,7 @@ public class InsuranceController(
|
||||
// Ensure hash has item in it
|
||||
if (!inventoryItemsHash.ContainsKey(itemId))
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Item with id: {itemId} missing from player inventory, skipping");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Item with id: {itemId} missing from player inventory, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,7 @@ public class InventoryController(
|
||||
// Item is moving into or out of place of fame dog tag slot
|
||||
if (moveRequest.To?.Container != null &&
|
||||
(moveRequest.To.Container.StartsWith("dogtag") || originalLocationSlotId.StartsWith("dogtag")))
|
||||
{
|
||||
_hideoutHelper.ApplyPlaceOfFameDogtagBonus(pmcData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -155,7 +153,7 @@ public class InventoryController(
|
||||
_logger.Success($"Set trader {mailEvent.Entity}: Standing to: {mailEvent.Value}");
|
||||
break;
|
||||
case ProfileChangeEventType.ProfileLevel:
|
||||
pmcData.Info.Experience = (int) mailEvent.Value.Value;
|
||||
pmcData.Info.Experience = (int)mailEvent.Value.Value;
|
||||
// Will calculate level below
|
||||
_traderHelper.ValidateTraderStandingsAndPlayerLevelForProfile(sessionId);
|
||||
_logger.Success($"Set profile xp to: {mailEvent.Value}");
|
||||
@@ -347,23 +345,16 @@ public class InventoryController(
|
||||
inventoryItem.ParentId = change.ParentId;
|
||||
inventoryItem.SlotId = change.SlotId;
|
||||
if (change.Location is not null)
|
||||
{
|
||||
inventoryItem.Location = change.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
inventoryItem.Location = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData body,
|
||||
string sessionId)
|
||||
{
|
||||
foreach (var id in body.Ids)
|
||||
{
|
||||
pmcData.Encyclopedia[id] = true;
|
||||
}
|
||||
foreach (var id in body.Ids) pmcData.Encyclopedia[id] = true;
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
@@ -373,7 +364,6 @@ public class InventoryController(
|
||||
{
|
||||
string? itemId = null;
|
||||
if (request.FromOwner is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
itemId = GetExaminedItemTpl(request, sessionId);
|
||||
@@ -382,25 +372,17 @@ public class InventoryController(
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("inventory-examine_item_does_not_exist", request.Item));
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId is null)
|
||||
{
|
||||
// item template
|
||||
if (_databaseService.GetItems().ContainsKey(request.Item))
|
||||
{
|
||||
itemId = request.Item;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId is null)
|
||||
{
|
||||
// Player inventory
|
||||
var target = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
|
||||
if (target is not null)
|
||||
{
|
||||
itemId = target.Template;
|
||||
}
|
||||
if (target is not null) itemId = target.Template;
|
||||
}
|
||||
|
||||
if (itemId is not null)
|
||||
@@ -415,29 +397,22 @@ public class InventoryController(
|
||||
if (_presetHelper.IsPreset(request.Item)) return _presetHelper.GetBaseItemTpl(request.Item);
|
||||
|
||||
if (request.FromOwner.Id == Traders.FENCE)
|
||||
{
|
||||
// Get tpl from fence assorts
|
||||
return _fenceService.GetRawFenceAssorts().Items.FirstOrDefault(x => x.Id == request.Item)?.Template;
|
||||
}
|
||||
|
||||
if (request.FromOwner.Type == "Trader")
|
||||
{
|
||||
// Not fence
|
||||
// get tpl from trader assort
|
||||
return _databaseService
|
||||
.GetTrader(request.FromOwner.Id)
|
||||
.Assort.Items.FirstOrDefault(item => item.Id == request.Item)
|
||||
?.Template;
|
||||
}
|
||||
|
||||
if (request.FromOwner.Type == "RagFair")
|
||||
{
|
||||
// Try to get tplId from items.json first
|
||||
var item = _itemHelper.GetItem(request.Item);
|
||||
if (item.Key)
|
||||
{
|
||||
return item.Value.Id;
|
||||
}
|
||||
if (item.Key) return item.Value.Id;
|
||||
|
||||
// Try alternate way of getting offer if first approach fails
|
||||
var offer = _ragfairOfferService.GetOfferByOfferId(request.Item) ??
|
||||
@@ -445,20 +420,14 @@ public class InventoryController(
|
||||
|
||||
// Try find examine item inside offer items array
|
||||
var matchingItem = offer.Items.FirstOrDefault(offerItem => offerItem.Id == request.Item);
|
||||
if (matchingItem is not null)
|
||||
{
|
||||
return matchingItem.Template;
|
||||
}
|
||||
if (matchingItem is not null) return matchingItem.Template;
|
||||
|
||||
// Unable to find item in database or ragfair
|
||||
_logger.Warning(_localisationService.GetText("inventory-unable_to_find_item", request.Item));
|
||||
}
|
||||
|
||||
|
||||
// get hideout item
|
||||
if (request.FromOwner.Type == "HideoutProduction")
|
||||
{
|
||||
return request.Item;
|
||||
}
|
||||
if (request.FromOwner.Type == "HideoutProduction") return request.Item;
|
||||
|
||||
if (request.FromOwner.Type == "Mail")
|
||||
{
|
||||
@@ -472,10 +441,7 @@ public class InventoryController(
|
||||
// get the Id given and get the Template ID from that
|
||||
var item = message.Items.Data.FirstOrDefault(item => item.Id == request.Item);
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
return item.Template;
|
||||
}
|
||||
if (item is not null) return item.Template;
|
||||
}
|
||||
|
||||
_logger.Error($"Unable to get item with id: {request.Item}");
|
||||
@@ -559,10 +525,7 @@ public class InventoryController(
|
||||
var playerData = pmcData;
|
||||
|
||||
// We may be folding data on scav profile, get that profile instead
|
||||
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id)
|
||||
{
|
||||
playerData = _profileHelper.GetScavProfile(sessionId);
|
||||
}
|
||||
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id) playerData = _profileHelper.GetScavProfile(sessionId);
|
||||
|
||||
var itemToFold = playerData.Inventory.Items.FirstOrDefault(item => item?.Id == request.Item);
|
||||
if (itemToFold is null)
|
||||
@@ -592,10 +555,7 @@ public class InventoryController(
|
||||
{
|
||||
// During post-raid scav transfer, the swap may be in the scav inventory
|
||||
var playerData = pmcData;
|
||||
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id)
|
||||
{
|
||||
playerData = _profileHelper.GetScavProfile(sessionId);
|
||||
}
|
||||
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id) playerData = _profileHelper.GetScavProfile(sessionId);
|
||||
|
||||
var itemOne = playerData.Inventory.Items.FirstOrDefault(x => x.Id == request.Item);
|
||||
if (itemOne is null)
|
||||
@@ -631,24 +591,16 @@ public class InventoryController(
|
||||
|
||||
// Request object has location data, add it in, otherwise remove existing location from object
|
||||
if (request.To.Location is not null)
|
||||
{
|
||||
itemOne.Location = request.To.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemOne.Location = null;
|
||||
}
|
||||
|
||||
itemTwo.ParentId = request.To2.Id;
|
||||
itemTwo.SlotId = request.To2.Container;
|
||||
if (request.To2.Location is not null)
|
||||
{
|
||||
itemTwo.Location = request.To2.Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemTwo.Location = null;
|
||||
}
|
||||
|
||||
// Client already informed of inventory locations, nothing for us to do
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
@@ -696,15 +648,11 @@ public class InventoryController(
|
||||
|
||||
var sourceStackCount = sourceItem.Upd.StackObjectsCount;
|
||||
if (sourceStackCount > request.Count)
|
||||
{
|
||||
// Source items stack count greater than new desired count
|
||||
sourceItem.Upd.StackObjectsCount = sourceStackCount - request.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Moving a full stack onto a smaller stack
|
||||
sourceItem.Upd.StackObjectsCount = sourceStackCount - 1;
|
||||
}
|
||||
|
||||
destinationItem.Upd ??= new Upd { StackObjectsCount = 1 };
|
||||
|
||||
@@ -743,27 +691,19 @@ public class InventoryController(
|
||||
}
|
||||
|
||||
if (destinationItem.Upd?.StackObjectsCount is null)
|
||||
{
|
||||
// No stackcount on destination, add one
|
||||
destinationItem.Upd = new Upd { StackObjectsCount = 1 };
|
||||
}
|
||||
|
||||
if (sourceItem.Upd is null)
|
||||
{
|
||||
sourceItem.Upd = new Upd { StackObjectsCount = 1 };
|
||||
}
|
||||
else if (sourceItem.Upd.StackObjectsCount is null)
|
||||
{
|
||||
// Items pulled out of raid can have no stack count if the stack should be 1
|
||||
sourceItem.Upd.StackObjectsCount = 1;
|
||||
}
|
||||
|
||||
// Remove FiR status from destination stack when source stack has no FiR but destination does
|
||||
if (!sourceItem.Upd.SpawnedInSession.GetValueOrDefault(false) &&
|
||||
destinationItem.Upd.SpawnedInSession.GetValueOrDefault(false))
|
||||
{
|
||||
destinationItem.Upd.SpawnedInSession = false;
|
||||
}
|
||||
|
||||
destinationItem.Upd.StackObjectsCount +=
|
||||
sourceItem.Upd.StackObjectsCount; // Add source stackcount to destination
|
||||
|
||||
@@ -45,7 +45,7 @@ public class LauncherController(
|
||||
BackendUrl = _httpServerHelper.GetBackendUrl(),
|
||||
Name = _coreConfig.ServerName,
|
||||
Editions = profiles,
|
||||
ProfileDescriptions = GetProfileDescriptions(),
|
||||
ProfileDescriptions = GetProfileDescriptions()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,10 +83,7 @@ public class LauncherController(
|
||||
foreach (var kvp in _saveServer.GetProfiles())
|
||||
{
|
||||
var account = _saveServer.GetProfile(kvp.Key).ProfileInfo;
|
||||
if (info?.Username == account?.Username)
|
||||
{
|
||||
return kvp.Key;
|
||||
}
|
||||
if (info?.Username == account?.Username) return kvp.Key;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -95,12 +92,8 @@ public class LauncherController(
|
||||
public string Register(RegisterData info)
|
||||
{
|
||||
foreach (var kvp in _saveServer.GetProfiles())
|
||||
{
|
||||
if (info.Username == _saveServer.GetProfile(kvp.Key).ProfileInfo?.Username)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return CreateAccount(info);
|
||||
}
|
||||
@@ -117,7 +110,7 @@ public class LauncherController(
|
||||
Username = info.Username,
|
||||
Password = info.Password,
|
||||
IsWiped = true,
|
||||
Edition = info.Edition,
|
||||
Edition = info.Edition
|
||||
};
|
||||
_saveServer.CreateProfile(newProfileDetails);
|
||||
|
||||
@@ -146,10 +139,7 @@ public class LauncherController(
|
||||
{
|
||||
var sessionID = Login(info);
|
||||
|
||||
if (!string.IsNullOrEmpty(sessionID))
|
||||
{
|
||||
_saveServer.GetProfile(sessionID).ProfileInfo!.Username = info.Change;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(sessionID)) _saveServer.GetProfile(sessionID).ProfileInfo!.Username = info.Change;
|
||||
|
||||
return sessionID;
|
||||
}
|
||||
@@ -158,10 +148,7 @@ public class LauncherController(
|
||||
{
|
||||
var sessionID = Login(info);
|
||||
|
||||
if (!string.IsNullOrEmpty(sessionID))
|
||||
{
|
||||
_saveServer.GetProfile(sessionID).ProfileInfo!.Password = info.Change;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(sessionID)) _saveServer.GetProfile(sessionID).ProfileInfo!.Password = info.Change;
|
||||
|
||||
return sessionID;
|
||||
}
|
||||
@@ -173,10 +160,7 @@ public class LauncherController(
|
||||
*/
|
||||
public string? Wipe(RegisterData info)
|
||||
{
|
||||
if (!_coreConfig.AllowProfileWipe)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!_coreConfig.AllowProfileWipe) return null;
|
||||
|
||||
var sessionID = Login(info);
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public class LauncherV2Controller(
|
||||
var casterPropertyValue = propertyValue as ProfileSides;
|
||||
result[templatesProperty.GetJsonName()] = _localisationService.GetText(casterPropertyValue?.DescriptionLocaleKey!);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -82,12 +82,8 @@ public class LauncherV2Controller(
|
||||
public bool Register(RegisterData info)
|
||||
{
|
||||
foreach (var session in _saveServer.GetProfiles())
|
||||
{
|
||||
if (info.Username == _saveServer.GetProfile(session.Key).ProfileInfo!.Username)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CreateAccount(info);
|
||||
return true;
|
||||
@@ -104,7 +100,7 @@ public class LauncherV2Controller(
|
||||
|
||||
if (sessionId is null)
|
||||
return false;
|
||||
|
||||
|
||||
_saveServer.GetProfile(sessionId).ProfileInfo!.Password = info.Password;
|
||||
return true;
|
||||
}
|
||||
@@ -117,7 +113,7 @@ public class LauncherV2Controller(
|
||||
public bool Remove(LoginRequestData info)
|
||||
{
|
||||
var sessionId = GetSessionId(info);
|
||||
|
||||
|
||||
return sessionId is not null && _saveServer.RemoveProfile(sessionId);
|
||||
}
|
||||
|
||||
@@ -169,22 +165,22 @@ public class LauncherV2Controller(
|
||||
IsWiped = true,
|
||||
Edition = info.Edition
|
||||
};
|
||||
|
||||
|
||||
_saveServer.CreateProfile(newProfileDetails);
|
||||
|
||||
|
||||
_saveServer.LoadProfile(profileId);
|
||||
_saveServer.SaveProfile(profileId);
|
||||
|
||||
|
||||
return profileId;
|
||||
}
|
||||
|
||||
|
||||
protected string GenerateProfileId()
|
||||
{
|
||||
var timestamp = _timeUtil.GetTimeStamp();
|
||||
|
||||
return FormatID(timestamp, timestamp * _randomUtil.GetInt(1, 1000000));
|
||||
}
|
||||
|
||||
|
||||
protected string FormatID(long timeStamp, long counter)
|
||||
{
|
||||
var timeStampStr = Convert.ToString(timeStamp, 16).PadLeft(8, '0');
|
||||
@@ -196,13 +192,8 @@ public class LauncherV2Controller(
|
||||
protected string? GetSessionId(LoginRequestData info)
|
||||
{
|
||||
foreach (var profile in _saveServer.GetProfiles())
|
||||
{
|
||||
if (info.Username == profile.Value.ProfileInfo!.Username
|
||||
&& info.Password == profile.Value.ProfileInfo.Password)
|
||||
{
|
||||
if (info.Username == profile.Value.ProfileInfo!.Username && info.Password == profile.Value.ProfileInfo.Password)
|
||||
return profile.Key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ public class LocationController(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Handle client/locations
|
||||
/// Get all maps base location properties without loot data
|
||||
@@ -38,10 +37,7 @@ public class LocationController(
|
||||
var mapBase = kvp.Value.Base;
|
||||
if (mapBase == null)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Map: {kvp} has no base json file, skipping generation");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Map: {kvp} has no base json file, skipping generation");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -65,10 +61,7 @@ public class LocationController(
|
||||
/// <returns></returns>
|
||||
public GetAirdropLootResponse? GetAirDropLoot(GetAirdropLootRequest? request)
|
||||
{
|
||||
if (request?.ContainerId is not null)
|
||||
{
|
||||
return _airdropService.GenerateCustomAirdropLoot(request);
|
||||
}
|
||||
if (request?.ContainerId is not null) return _airdropService.GenerateCustomAirdropLoot(request);
|
||||
|
||||
return _airdropService.GenerateAirdropLoot();
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class MatchController(
|
||||
/// <returns></returns>
|
||||
public ProfileStatusResponse JoinMatch(MatchGroupStartGameRequest info, string sessionId)
|
||||
{
|
||||
ProfileStatusResponse output = new ProfileStatusResponse
|
||||
var output = new ProfileStatusResponse
|
||||
{
|
||||
MaxPveCountExceeded = false,
|
||||
// get list of players joining into the match
|
||||
@@ -105,11 +105,9 @@ public class MatchController(
|
||||
|
||||
// Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled
|
||||
if (!_pmcConfig.UseDifficultyOverride)
|
||||
{
|
||||
_pmcConfig.Difficulty = ConvertDifficultyDropdownIntoBotDifficulty(
|
||||
request.WavesSettings.BotDifficulty.ToString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,10 +118,7 @@ public class MatchController(
|
||||
private string ConvertDifficultyDropdownIntoBotDifficulty(string botDifficulty)
|
||||
{
|
||||
// Edge case medium - must be altered
|
||||
if (botDifficulty.ToLower() == "medium")
|
||||
{
|
||||
return "normal";
|
||||
}
|
||||
if (botDifficulty.ToLower() == "medium") return "normal";
|
||||
|
||||
return botDifficulty;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class NoteController(
|
||||
NoteActionData body,
|
||||
string sessionId)
|
||||
{
|
||||
Note newNote = new Note { Time = body.Note.Time, Text = body.Note.Text };
|
||||
var newNote = new Note { Time = body.Note.Time, Text = body.Note.Text };
|
||||
pmcData.Notes.DataNotes.Add(newNote);
|
||||
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ public class NoteController(
|
||||
NoteActionData body,
|
||||
string sessionId)
|
||||
{
|
||||
Note noteToEdit = pmcData.Notes.DataNotes[body.Index!.Value];
|
||||
var noteToEdit = pmcData.Notes.DataNotes[body.Index!.Value];
|
||||
noteToEdit.Time = body.Note.Time;
|
||||
noteToEdit.Text = body.Note.Text;
|
||||
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Core.Controllers;
|
||||
public class NotifierController(
|
||||
HttpServerHelper _httpServerHelper,
|
||||
NotifierHelper _notifierHelper
|
||||
)
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolve an array of session notifications.
|
||||
@@ -59,9 +59,9 @@ public class NotifierController(
|
||||
|
||||
// _notificationService.UpdateMessageOnQueue(sessionID, []);
|
||||
// resolve(messages);
|
||||
//};
|
||||
//};
|
||||
|
||||
// immediately check
|
||||
// immediately check
|
||||
// checkNotifications();
|
||||
//});
|
||||
}
|
||||
|
||||
@@ -55,25 +55,25 @@ public class PrestigeController(
|
||||
ObtainPrestigeRequestList request)
|
||||
{
|
||||
// Going to prestige 1
|
||||
|
||||
|
||||
// transfer
|
||||
// 5% of skills should be transfered over
|
||||
// 5% of mastering should be transfered over
|
||||
// earned achievements should be transfered over
|
||||
// profile stats should be transfered over
|
||||
// prestige progress should be transfered over
|
||||
|
||||
|
||||
// reset
|
||||
// trader standing
|
||||
// task progress
|
||||
// character level
|
||||
// stash
|
||||
// hideout progress
|
||||
|
||||
|
||||
// going to prestige 2
|
||||
// most likely the same, but wait for dump of new beginnings quest
|
||||
|
||||
|
||||
|
||||
|
||||
// Clone existing profile, create a new one
|
||||
var prePrestigeProfileClone = _cloner.Clone(_profileHelper.GetFullProfile(sessionId));
|
||||
var prePrestigePmc = prePrestigeProfileClone.CharacterData.PmcData;
|
||||
@@ -87,7 +87,7 @@ public class PrestigeController(
|
||||
(customisation) => customisation.Value.Name == prePrestigePmc.Info.Voice
|
||||
)
|
||||
.Value.Id,
|
||||
SptForcePrestigeLevel = prePrestigeProfileClone.CharacterData.PmcData.Info.PrestigeLevel.GetValueOrDefault(0) + 1, // Current + 1
|
||||
SptForcePrestigeLevel = prePrestigeProfileClone.CharacterData.PmcData.Info.PrestigeLevel.GetValueOrDefault(0) + 1 // Current + 1
|
||||
};
|
||||
|
||||
// Reset profile
|
||||
@@ -104,15 +104,11 @@ public class PrestigeController(
|
||||
skillToCopy.Progress = skillToCopy.Progress.Value * 0.05;
|
||||
var existingSkill = newProfile.CharacterData.PmcData.Skills.Common.FirstOrDefault((skill) => skill.Id == skillToCopy.Id);
|
||||
if (existingSkill is not null)
|
||||
{
|
||||
existingSkill.Progress = skillToCopy.Progress;
|
||||
}
|
||||
else
|
||||
{
|
||||
newProfile.CharacterData.PmcData.Skills.Common.Add(skillToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Copy mastering to new profile
|
||||
var masteringSkillsToCopy = prePrestigePmc.Skills.Mastering;
|
||||
foreach (var skillToCopy in masteringSkillsToCopy)
|
||||
@@ -123,35 +119,29 @@ public class PrestigeController(
|
||||
(skill) => skill.Id == skillToCopy.Id
|
||||
);
|
||||
if (existingSkill is not null)
|
||||
{
|
||||
existingSkill.Progress = skillToCopy.Progress;
|
||||
}
|
||||
else
|
||||
{
|
||||
newProfile.CharacterData.PmcData.Skills.Mastering.Add(skillToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add existing completed achievements and new one for prestige
|
||||
newProfile.CharacterData.PmcData.Achievements = prePrestigeProfileClone.CharacterData.PmcData.Achievements; // this *should* only contain completed ones
|
||||
|
||||
// Add "Prestigious" achievement
|
||||
if (!newProfile.CharacterData.PmcData.Achievements.ContainsKey("676091c0f457869a94017a23"))
|
||||
{
|
||||
newProfile.CharacterData.PmcData.Achievements.Add("676091c0f457869a94017a23", _timeUtil.GetTimeStamp());
|
||||
}
|
||||
// TODO: is there one for second prestige
|
||||
|
||||
// Add existing Stats to profile
|
||||
newProfile.CharacterData.PmcData.Stats = prePrestigePmc.Stats;
|
||||
|
||||
|
||||
// Assumes Prestige data is in descending order
|
||||
var indexOfPrestigeObtained = (int)Math.Min(createRequest.SptForcePrestigeLevel.Value - 1, 1); // Index starts at 0
|
||||
var currentPrestigeData = _databaseService.GetTemplates().Prestige.Elements[indexOfPrestigeObtained];
|
||||
var prestigeRewards = _databaseService.GetTemplates()
|
||||
.Prestige.Elements.Slice(0, indexOfPrestigeObtained + 1)
|
||||
.SelectMany((prestige) => prestige.Rewards);
|
||||
|
||||
|
||||
AddPrestigeRewardsToProfile(sessionId, newProfile, prestigeRewards);
|
||||
|
||||
// Flag profile as having achieved this prestige level
|
||||
@@ -159,7 +149,6 @@ public class PrestigeController(
|
||||
newProfile.CharacterData.PmcData.Info.PrestigeLevel++;
|
||||
|
||||
if (request is not null)
|
||||
{
|
||||
// Copy transferred items
|
||||
foreach (var transferRequest in request)
|
||||
{
|
||||
@@ -169,7 +158,7 @@ public class PrestigeController(
|
||||
ItemWithModsToAdd = [item],
|
||||
FoundInRaid = item.Upd?.SpawnedInSession,
|
||||
UseSortingTable = false,
|
||||
Callback = null,
|
||||
Callback = null
|
||||
};
|
||||
_inventoryHelper.AddItemToStash(
|
||||
sessionId,
|
||||
@@ -178,40 +167,39 @@ public class PrestigeController(
|
||||
_eventOutputHolder.GetOutput(sessionId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Force save of above changes to disk
|
||||
_saveServer.SaveProfile(sessionId);
|
||||
}
|
||||
|
||||
private void AddPrestigeRewardsToProfile(string sessionId, SptProfile newProfile, IEnumerable<Reward> rewards)
|
||||
{
|
||||
foreach (var reward in rewards) {
|
||||
switch (reward.Type) {
|
||||
case RewardType.CustomizationDirect: {
|
||||
foreach (var reward in rewards)
|
||||
switch (reward.Type)
|
||||
{
|
||||
case RewardType.CustomizationDirect:
|
||||
{
|
||||
_profileHelper.AddHideoutCustomisationUnlock(newProfile, reward, CustomisationSource.PRESTIGE);
|
||||
break;
|
||||
}
|
||||
case RewardType.Skill:
|
||||
if (Enum.TryParse(reward.Target, out SkillTypes result))
|
||||
{
|
||||
_profileHelper.AddSkillPointsToPlayer(
|
||||
newProfile.CharacterData.PmcData,
|
||||
result,
|
||||
((JsonElement)reward.Value).ToObject<double>()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error($"Unable to parse reward Target to Enum: {reward.Target}");
|
||||
}
|
||||
break;
|
||||
case RewardType.Item: {
|
||||
AddItemDirectRequest addItemRequest = new AddItemDirectRequest {
|
||||
case RewardType.Item:
|
||||
{
|
||||
var addItemRequest = new AddItemDirectRequest
|
||||
{
|
||||
ItemWithModsToAdd = reward.Items,
|
||||
FoundInRaid = reward.Items.FirstOrDefault()?.Upd?.SpawnedInSession,
|
||||
UseSortingTable = false,
|
||||
Callback = null,
|
||||
Callback = null
|
||||
};
|
||||
_inventoryHelper.AddItemToStash(
|
||||
sessionId,
|
||||
@@ -229,6 +217,5 @@ public class PrestigeController(
|
||||
_logger.Error($"Unhandled prestige reward type: {reward.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,19 +51,15 @@ public class ProfileController(
|
||||
public MiniProfile GetMiniProfile(string sessionID)
|
||||
{
|
||||
var profile = _saveServer.GetProfile(sessionID);
|
||||
if (profile?.CharacterData == null)
|
||||
{
|
||||
throw new Exception($"Unable to find character data for id: {sessionID}. Profile may be corrupt");
|
||||
}
|
||||
if (profile?.CharacterData == null) throw new Exception($"Unable to find character data for id: {sessionID}. Profile may be corrupt");
|
||||
|
||||
var pmc = profile.CharacterData.PmcData;
|
||||
var maxLvl = _profileHelper.GetMaxLevel();
|
||||
|
||||
// Player hasn't completed profile creation process, send defaults
|
||||
var currlvl = pmc?.Info?.Level.GetValueOrDefault(1);
|
||||
var xpToNextLevel = _profileHelper.GetExperience(((currlvl ?? 1) + 1));
|
||||
var xpToNextLevel = _profileHelper.GetExperience((currlvl ?? 1) + 1);
|
||||
if (pmc?.Info?.Level == null)
|
||||
{
|
||||
return new MiniProfile
|
||||
{
|
||||
Username = profile.ProfileInfo?.Username ?? "",
|
||||
@@ -77,9 +73,8 @@ public class ProfileController(
|
||||
MaxLevel = maxLvl,
|
||||
Edition = profile.ProfileInfo?.Edition ?? "",
|
||||
ProfileId = profile.ProfileInfo?.ProfileId ?? "",
|
||||
SptData = _profileHelper.GetDefaultSptDataObject(),
|
||||
SptData = _profileHelper.GetDefaultSptDataObject()
|
||||
};
|
||||
}
|
||||
|
||||
return new MiniProfile
|
||||
{
|
||||
@@ -87,14 +82,14 @@ public class ProfileController(
|
||||
Nickname = pmc.Info.Nickname,
|
||||
HasPassword = profile.ProfileInfo.Password != "",
|
||||
Side = pmc.Info.Side,
|
||||
CurrentLevel = (int)(pmc.Info.Level),
|
||||
CurrentExperience = (pmc.Info.Experience ?? 0),
|
||||
CurrentLevel = (int)pmc.Info.Level,
|
||||
CurrentExperience = pmc.Info.Experience ?? 0,
|
||||
PreviousExperience = currlvl == 0 ? 0 : _profileHelper.GetExperience((int)currlvl),
|
||||
NextLevel = xpToNextLevel,
|
||||
MaxLevel = maxLvl,
|
||||
Edition = profile.ProfileInfo?.Edition ?? "",
|
||||
ProfileId = profile.ProfileInfo?.ProfileId ?? "",
|
||||
SptData = profile.SptData,
|
||||
SptData = profile.SptData
|
||||
};
|
||||
}
|
||||
|
||||
@@ -133,15 +128,9 @@ public class ProfileController(
|
||||
*/
|
||||
public string ValidateNickname(ValidateNicknameRequestData info, string sessionID)
|
||||
{
|
||||
if (info.Nickname.Length < 3)
|
||||
{
|
||||
return "tooshort";
|
||||
}
|
||||
if (info.Nickname.Length < 3) return "tooshort";
|
||||
|
||||
if (_profileHelper.IsNicknameTaken(info, sessionID))
|
||||
{
|
||||
return "taken";
|
||||
}
|
||||
if (_profileHelper.IsNicknameTaken(info, sessionID)) return "taken";
|
||||
|
||||
return "OK";
|
||||
}
|
||||
@@ -188,10 +177,7 @@ public class ProfileController(
|
||||
{
|
||||
var pmcProfile = profile?.CharacterData?.PmcData;
|
||||
|
||||
if (!pmcProfile?.Info?.LowerNickname?.Contains(info.Nickname.ToLower()) ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!pmcProfile?.Info?.LowerNickname?.Contains(info.Nickname.ToLower()) ?? false) continue;
|
||||
|
||||
result.Add(_profileHelper.GetChatRoomMemberFromPmcProfile(pmcProfile));
|
||||
}
|
||||
@@ -210,8 +196,8 @@ public class ProfileController(
|
||||
MaxPveCountExceeded = false,
|
||||
Profiles =
|
||||
[
|
||||
new() { ProfileId = account.ScavengerId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 },
|
||||
new() { ProfileId = account.ProfileId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 },
|
||||
new ProfileStatusData { ProfileId = account.ScavengerId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 },
|
||||
new ProfileStatusData { ProfileId = account.ProfileId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 }
|
||||
]
|
||||
};
|
||||
|
||||
@@ -261,7 +247,7 @@ public class ProfileController(
|
||||
MemberCategory = profileToViewPmc.Info.MemberCategory as int?,
|
||||
BannedState = profileToViewPmc.Info.BannedState,
|
||||
BannedUntil = profileToViewPmc.Info.BannedUntil,
|
||||
RegistrationDate = profileToViewPmc.Info.RegistrationDate,
|
||||
RegistrationDate = profileToViewPmc.Info.RegistrationDate
|
||||
},
|
||||
Customization =
|
||||
{
|
||||
@@ -269,13 +255,13 @@ public class ProfileController(
|
||||
Body = profileToViewPmc.Customization.Body,
|
||||
Feet = profileToViewPmc.Customization.Feet,
|
||||
Hands = profileToViewPmc.Customization.Hands,
|
||||
Dogtag = profileToViewPmc.Customization.DogTag,
|
||||
Dogtag = profileToViewPmc.Customization.DogTag
|
||||
},
|
||||
Skills = profileToViewPmc.Skills,
|
||||
Equipment =
|
||||
{
|
||||
Id = profileToViewPmc.Inventory.Equipment,
|
||||
Items = profileToViewPmc.Inventory.Items,
|
||||
Items = profileToViewPmc.Inventory.Items
|
||||
},
|
||||
Achievements = profileToViewPmc.Achievements,
|
||||
FavoriteItems = _profileHelper.GetOtherProfileFavorites(profileToViewPmc),
|
||||
@@ -284,15 +270,15 @@ public class ProfileController(
|
||||
Eft =
|
||||
{
|
||||
TotalInGameTime = profileToViewPmc.Stats.Eft.TotalInGameTime,
|
||||
OverAllCounters = profileToViewPmc.Stats.Eft.OverallCounters,
|
||||
},
|
||||
OverAllCounters = profileToViewPmc.Stats.Eft.OverallCounters
|
||||
}
|
||||
},
|
||||
ScavStats =
|
||||
{
|
||||
Eft =
|
||||
{
|
||||
TotalInGameTime = profileToViewScav.Stats.Eft.TotalInGameTime,
|
||||
OverAllCounters = profileToViewScav.Stats.Eft.OverallCounters,
|
||||
OverAllCounters = profileToViewScav.Stats.Eft.OverallCounters
|
||||
}
|
||||
},
|
||||
Hideout = profileToViewPmc.Hideout,
|
||||
@@ -308,20 +294,11 @@ public class ProfileController(
|
||||
public bool SetChosenProfileIcon(string sessionId, GetProfileSettingsRequest request)
|
||||
{
|
||||
var profileToUpdate = _profileHelper.GetPmcProfile(sessionId);
|
||||
if (profileToUpdate == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (profileToUpdate == null) return false;
|
||||
|
||||
if (request.MemberCategory != null)
|
||||
{
|
||||
profileToUpdate.Info.SelectedMemberCategory = request.MemberCategory as MemberCategory?;
|
||||
}
|
||||
if (request.MemberCategory != null) profileToUpdate.Info.SelectedMemberCategory = request.MemberCategory as MemberCategory?;
|
||||
|
||||
if (request.SquadInviteRestriction != null)
|
||||
{
|
||||
profileToUpdate.Info.SquadInviteRestriction = request.SquadInviteRestriction;
|
||||
}
|
||||
if (request.SquadInviteRestriction != null) profileToUpdate.Info.SquadInviteRestriction = request.SquadInviteRestriction;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,10 +107,7 @@ public class QuestController(
|
||||
acceptedQuest.QuestId,
|
||||
sessionID
|
||||
);
|
||||
if (newlyAccessibleQuests.Count > 0)
|
||||
{
|
||||
acceptQuestResponse.ProfileChanges[sessionID].Quests.AddRange(newlyAccessibleQuests);
|
||||
}
|
||||
if (newlyAccessibleQuests.Count > 0) acceptQuestResponse.ProfileChanges[sessionID].Quests.AddRange(newlyAccessibleQuests);
|
||||
|
||||
return acceptQuestResponse;
|
||||
}
|
||||
@@ -120,11 +117,9 @@ public class QuestController(
|
||||
foreach (var condition in questConditions)
|
||||
{
|
||||
if (pmcData.TaskConditionCounters.TryGetValue(condition.Id, out var counter))
|
||||
{
|
||||
_logger.Error(
|
||||
$"Unable to add new task condition counter: {condition.ConditionType} for qeust: {questId} to profile: {pmcData.SessionId} as it already exists:"
|
||||
);
|
||||
}
|
||||
|
||||
switch (condition.ConditionType)
|
||||
{
|
||||
@@ -134,7 +129,7 @@ public class QuestController(
|
||||
Id = condition.Id,
|
||||
SourceId = questId,
|
||||
Type = condition.ConditionType,
|
||||
Value = 0,
|
||||
Value = 0
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -186,10 +181,7 @@ public class QuestController(
|
||||
var matchingQuest = repeatableQuest.ActiveQuests.FirstOrDefault(x => x.Id == acceptedQuest.QuestId);
|
||||
if (matchingQuest is not null)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Accepted repeatable quest {acceptedQuest.QuestId} from {repeatableQuest.Name}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Accepted repeatable quest {acceptedQuest.QuestId} from {repeatableQuest.Name}");
|
||||
matchingQuest.SptRepatableGroupName = repeatableQuest.Name;
|
||||
|
||||
return matchingQuest;
|
||||
@@ -217,7 +209,6 @@ public class QuestController(
|
||||
// Decrement number of items handed in
|
||||
QuestCondition? handoverRequirements = null;
|
||||
foreach (var condition in quest.Conditions.AvailableForFinish)
|
||||
{
|
||||
if (condition.Id == handoverQuestRequest.ConditionId && handoverQuestTypes.Contains(condition.ConditionType))
|
||||
{
|
||||
handedInCount = int.Parse(condition.Value.ToString());
|
||||
@@ -249,19 +240,14 @@ public class QuestController(
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isItemHandoverQuest && handedInCount == 0)
|
||||
{
|
||||
return ShowRepeatableQuestInvalidConditionError(handoverQuestRequest, output);
|
||||
}
|
||||
if (isItemHandoverQuest && handedInCount == 0) return ShowRepeatableQuestInvalidConditionError(handoverQuestRequest, output);
|
||||
|
||||
var totalItemCountToRemove = 0d;
|
||||
foreach (var itemHandover in handoverQuestRequest.Items)
|
||||
{
|
||||
var matchingItemInProfile = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == itemHandover.Id);
|
||||
if (!(matchingItemInProfile is not null && handoverRequirements.Target.List.Contains(matchingItemInProfile.Template)))
|
||||
{
|
||||
// Item handed in by player doesnt match what was requested
|
||||
return ShowQuestItemHandoverMatchError(
|
||||
handoverQuestRequest,
|
||||
@@ -269,7 +255,6 @@ public class QuestController(
|
||||
handoverRequirements,
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the right quantity of given items
|
||||
var itemCountToRemove = Math.Min(itemHandover.Count ?? 0, handedInCount - totalItemCountToRemove);
|
||||
@@ -284,10 +269,7 @@ public class QuestController(
|
||||
sessionID,
|
||||
output
|
||||
);
|
||||
if (totalItemCountToRemove == handedInCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (totalItemCountToRemove == handedInCount) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -306,13 +288,12 @@ public class QuestController(
|
||||
|
||||
// Important: loop backward when removing items from the array we're looping on
|
||||
while (index-- > 0)
|
||||
{
|
||||
if (toRemove.Contains(pmcData.Inventory.Items[index].Id))
|
||||
{
|
||||
var removedItem = _cloner.Clone(pmcData.Inventory.Items[index]);
|
||||
pmcData.Inventory.Items.RemoveAt(index);
|
||||
// Remove the item
|
||||
|
||||
// Remove the item
|
||||
// If the removed item has a numeric `location` property, re-calculate all the child
|
||||
// element `location` properties of the parent so they are sequential, while retaining order
|
||||
if (removedItem.Location.GetType() == typeof(int))
|
||||
@@ -324,15 +305,11 @@ public class QuestController(
|
||||
childItems.RemoveAt(0); // Remove the parent
|
||||
|
||||
// Sort by the current `location` and update
|
||||
childItems.Sort((a, b) => (((int)a.Location) > ((int)b.Location) ? 1 : -1));
|
||||
childItems.Sort((a, b) => (int)a.Location > (int)b.Location ? 1 : -1);
|
||||
|
||||
for (int i = 0; i < childItems.Count; i++)
|
||||
{
|
||||
childItems[i].Location = i;
|
||||
}
|
||||
for (var i = 0; i < childItems.Count; i++) childItems[i].Location = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +347,7 @@ public class QuestController(
|
||||
{
|
||||
questId = handoverQuestRequest.QuestId,
|
||||
handedInTpl = itemHandedOver?.Template ?? "UNKNOWN",
|
||||
requiredTpl = handoverRequirements.Target.List.FirstOrDefault(),
|
||||
requiredTpl = handoverRequirements.Target.List.FirstOrDefault()
|
||||
}
|
||||
);
|
||||
_logger.Error(errorMessage);
|
||||
@@ -392,7 +369,7 @@ public class QuestController(
|
||||
Id = conditionId,
|
||||
SourceId = questId,
|
||||
Type = "HandoverItem",
|
||||
Value = counterValue,
|
||||
Value = counterValue
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -25,32 +25,32 @@ namespace Core.Controllers;
|
||||
[Injectable]
|
||||
public class RagfairController
|
||||
{
|
||||
private readonly ISptLogger<RagfairController> _logger;
|
||||
private readonly TimeUtil _timeUtil;
|
||||
private readonly JsonUtil _jsonUtil;
|
||||
private readonly HttpResponseUtil _httpResponseUtil;
|
||||
private readonly EventOutputHolder _eventOutputHolder;
|
||||
private readonly RagfairServer _ragfairServer;
|
||||
private readonly ItemHelper _itemHelper;
|
||||
private readonly InventoryHelper _inventoryHelper;
|
||||
private readonly RagfairSellHelper _ragfairSellHelper;
|
||||
private readonly HandbookHelper _handbookHelper;
|
||||
private readonly ProfileHelper _profileHelper;
|
||||
private readonly PaymentHelper _paymentHelper;
|
||||
private readonly RagfairHelper _ragfairHelper;
|
||||
private readonly RagfairSortHelper _ragfairSortHelper;
|
||||
private readonly RagfairOfferHelper _ragfairOfferHelper;
|
||||
private readonly TraderHelper _traderHelper;
|
||||
private readonly DatabaseService _databaseService;
|
||||
private readonly LocalisationService _localisationService;
|
||||
private readonly RagfairTaxService _ragfairTaxService;
|
||||
private readonly RagfairOfferService _ragfairOfferService;
|
||||
private readonly PaymentService _paymentService;
|
||||
private readonly RagfairPriceService _ragfairPriceService;
|
||||
private readonly RagfairOfferGenerator _ragfairOfferGenerator;
|
||||
private readonly ConfigServer _configServer;
|
||||
private readonly DatabaseService _databaseService;
|
||||
private readonly EventOutputHolder _eventOutputHolder;
|
||||
private readonly HandbookHelper _handbookHelper;
|
||||
private readonly HttpResponseUtil _httpResponseUtil;
|
||||
private readonly InventoryHelper _inventoryHelper;
|
||||
private readonly ItemHelper _itemHelper;
|
||||
private readonly JsonUtil _jsonUtil;
|
||||
private readonly LocalisationService _localisationService;
|
||||
private readonly ISptLogger<RagfairController> _logger;
|
||||
private readonly PaymentHelper _paymentHelper;
|
||||
private readonly PaymentService _paymentService;
|
||||
private readonly ProfileHelper _profileHelper;
|
||||
|
||||
private readonly RagfairConfig _ragfairConfig;
|
||||
private readonly RagfairHelper _ragfairHelper;
|
||||
private readonly RagfairOfferGenerator _ragfairOfferGenerator;
|
||||
private readonly RagfairOfferHelper _ragfairOfferHelper;
|
||||
private readonly RagfairOfferService _ragfairOfferService;
|
||||
private readonly RagfairPriceService _ragfairPriceService;
|
||||
private readonly RagfairSellHelper _ragfairSellHelper;
|
||||
private readonly RagfairServer _ragfairServer;
|
||||
private readonly RagfairSortHelper _ragfairSortHelper;
|
||||
private readonly RagfairTaxService _ragfairTaxService;
|
||||
private readonly TimeUtil _timeUtil;
|
||||
private readonly TraderHelper _traderHelper;
|
||||
|
||||
public RagfairController(
|
||||
ISptLogger<RagfairController> logger,
|
||||
@@ -119,9 +119,7 @@ public class RagfairController
|
||||
if (
|
||||
pmcProfile.RagfairInfo is not null && pmcProfile.Info.Level >= _databaseService.GetGlobals().Configuration.RagFair.MinUserLevel
|
||||
)
|
||||
{
|
||||
_ragfairOfferHelper.ProcessOffersOnProfile(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,16 +140,14 @@ public class RagfairController
|
||||
{
|
||||
Offers = [],
|
||||
OffersCount = searchRequest.Limit,
|
||||
SelectedCategory = searchRequest.HandbookId,
|
||||
SelectedCategory = searchRequest.HandbookId
|
||||
};
|
||||
|
||||
result.Offers = GetOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, profile.CharacterData.PmcData);
|
||||
|
||||
// Client requested a category refresh
|
||||
if (searchRequest.UpdateOfferCount.GetValueOrDefault(false))
|
||||
{
|
||||
result.Categories = GetSpecificCategories(profile.CharacterData.PmcData, searchRequest, result.Offers);
|
||||
}
|
||||
|
||||
AddIndexValueToOffers(result.Offers);
|
||||
|
||||
@@ -167,10 +163,7 @@ public class RagfairController
|
||||
{
|
||||
// For the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
|
||||
// to true if the quest is not completed yet
|
||||
if (_ragfairOfferHelper.TraderOfferItemQuestLocked(traderOffer, traderAssorts))
|
||||
{
|
||||
traderOffer.Locked = true;
|
||||
}
|
||||
if (_ragfairOfferHelper.TraderOfferItemQuestLocked(traderOffer, traderAssorts)) traderOffer.Locked = true;
|
||||
|
||||
// Update offers BuyRestrictionCurrent/BuyRestrictionMax values
|
||||
SetTraderOfferPurchaseLimits(traderOffer, profile);
|
||||
@@ -208,7 +201,7 @@ public class RagfairController
|
||||
new
|
||||
{
|
||||
offerId = offer.Items.First().Id,
|
||||
traderId = offer.User.Id,
|
||||
traderId = offer.User.Id
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -263,10 +256,7 @@ public class RagfairController
|
||||
{
|
||||
var counter = 0;
|
||||
|
||||
foreach (var offer in offers)
|
||||
{
|
||||
offer.InternalId = ++counter;
|
||||
}
|
||||
foreach (var offer in offers) offer.InternalId = ++counter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,10 +283,7 @@ public class RagfairController
|
||||
else
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("ragfair-unable_to_get_categories"));
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(_jsonUtil.Serialize(searchRequest));
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug(_jsonUtil.Serialize(searchRequest));
|
||||
return new Dictionary<string, int>();
|
||||
}
|
||||
|
||||
@@ -335,15 +322,9 @@ public class RagfairController
|
||||
PmcData pmcProfile)
|
||||
{
|
||||
// Searching for items in preset menu
|
||||
if (searchRequest.BuildCount > 0)
|
||||
{
|
||||
return _ragfairOfferHelper.GetOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
|
||||
}
|
||||
if (searchRequest.BuildCount > 0) return _ragfairOfferHelper.GetOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
|
||||
|
||||
if (searchRequest.NeededSearchId?.Length > 0)
|
||||
{
|
||||
return _ragfairOfferHelper.GetOffersThatRequireItem(searchRequest, pmcProfile);
|
||||
}
|
||||
if (searchRequest.NeededSearchId?.Length > 0) return _ragfairOfferHelper.GetOffersThatRequireItem(searchRequest, pmcProfile);
|
||||
|
||||
// Searching for general items
|
||||
return _ragfairOfferHelper.GetValidOffers(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
|
||||
@@ -375,10 +356,7 @@ public class RagfairController
|
||||
// No offers listed, get price from live ragfair price list prices.json
|
||||
// No flea price, get handbook price
|
||||
var fleaPrices = _databaseService.GetPrices();
|
||||
if (!fleaPrices.TryGetValue(getPriceRequest.TemplateId, out var tplPrice))
|
||||
{
|
||||
tplPrice = _handbookHelper.GetTemplatePrice(getPriceRequest.TemplateId);
|
||||
}
|
||||
if (!fleaPrices.TryGetValue(getPriceRequest.TemplateId, out var tplPrice)) tplPrice = _handbookHelper.GetTemplatePrice(getPriceRequest.TemplateId);
|
||||
|
||||
return new GetItemPriceResult { Avg = tplPrice, Min = tplPrice, Max = tplPrice };
|
||||
}
|
||||
@@ -391,40 +369,26 @@ public class RagfairController
|
||||
foreach (var offer in offers)
|
||||
{
|
||||
// Exclude barter items, they tend to have outrageous equivalent prices
|
||||
if (offer.Requirements.Any(req => !_paymentHelper.IsMoneyTpl(req.Template)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (offer.Requirements.Any(req => !_paymentHelper.IsMoneyTpl(req.Template))) continue;
|
||||
|
||||
if (ignoreTraderOffers && _ragfairOfferHelper.OfferIsFromTrader(offer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ignoreTraderOffers && _ragfairOfferHelper.OfferIsFromTrader(offer)) continue;
|
||||
|
||||
// Figure out how many items the requirementsCost is applying to, and what the per-item price is
|
||||
var offerItemCount = offer.SellInOnePiece.GetValueOrDefault(false)
|
||||
? (offer.Items.First().Upd?.StackObjectsCount ?? 1)
|
||||
? offer.Items.First().Upd?.StackObjectsCount ?? 1
|
||||
: 1;
|
||||
var perItemPrice = offer.RequirementsCost / offerItemCount;
|
||||
|
||||
// Handle min/max calculations based on the per-item price
|
||||
if (perItemPrice < minMax.Min)
|
||||
{
|
||||
minMax.Min = perItemPrice;
|
||||
}
|
||||
else if (perItemPrice > minMax.Max)
|
||||
{
|
||||
minMax.Max = perItemPrice;
|
||||
}
|
||||
else if (perItemPrice > minMax.Max) minMax.Max = perItemPrice;
|
||||
|
||||
sum += perItemPrice.Value;
|
||||
totalOfferCount++;
|
||||
}
|
||||
|
||||
if (totalOfferCount == 0)
|
||||
{
|
||||
return -1d;
|
||||
}
|
||||
if (totalOfferCount == 0) return -1d;
|
||||
|
||||
return sum / totalOfferCount;
|
||||
}
|
||||
@@ -442,16 +406,11 @@ public class RagfairController
|
||||
var fullProfile = _profileHelper.GetFullProfile(sessionID);
|
||||
|
||||
var validationMessage = "";
|
||||
if (!IsValidPlayerOfferRequest(offerRequest, validationMessage))
|
||||
{
|
||||
return _httpResponseUtil.AppendErrorToOutput(output, validationMessage);
|
||||
}
|
||||
if (!IsValidPlayerOfferRequest(offerRequest, validationMessage)) return _httpResponseUtil.AppendErrorToOutput(output, validationMessage);
|
||||
|
||||
var typeOfOffer = GetOfferType(offerRequest);
|
||||
if (typeOfOffer == FleaOfferType.UNKNOWN)
|
||||
{
|
||||
return _httpResponseUtil.AppendErrorToOutput(output, $"Unknown offer type: {typeOfOffer}, cannot list item on flea");
|
||||
}
|
||||
|
||||
switch (typeOfOffer)
|
||||
{
|
||||
@@ -504,21 +463,12 @@ public class RagfairController
|
||||
|
||||
if (!sellInOncePiece)
|
||||
{
|
||||
if (offerRequest.Items.Count == 1)
|
||||
{
|
||||
return FleaOfferType.SINGLE;
|
||||
}
|
||||
if (offerRequest.Items.Count == 1) return FleaOfferType.SINGLE;
|
||||
|
||||
if (offerRequest.Items.Count > 1)
|
||||
{
|
||||
return FleaOfferType.MULTI;
|
||||
}
|
||||
if (offerRequest.Items.Count > 1) return FleaOfferType.MULTI;
|
||||
}
|
||||
|
||||
if (sellInOncePiece)
|
||||
{
|
||||
return FleaOfferType.PACK;
|
||||
}
|
||||
if (sellInOncePiece) return FleaOfferType.PACK;
|
||||
|
||||
return FleaOfferType.UNKNOWN;
|
||||
}
|
||||
@@ -542,14 +492,12 @@ public class RagfairController
|
||||
// Get first item and its children and use as template
|
||||
var firstListingAndChidren = _itemHelper.FindAndReturnChildrenAsItems(
|
||||
pmcData.Inventory.Items,
|
||||
offerRequest.Items[0]);
|
||||
offerRequest.Items[0]
|
||||
);
|
||||
|
||||
// Find items to be listed on flea (+ children) from player inventory
|
||||
var result = GetItemsToListOnFleaFromInventory(pmcData, offerRequest.Items);
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage))
|
||||
{
|
||||
_httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
}
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage)) _httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
|
||||
// Total count of items summed using their stack counts
|
||||
var stackCountTotal = _ragfairOfferHelper.GetTotalStackCountSize(result.Items);
|
||||
@@ -557,7 +505,7 @@ public class RagfairController
|
||||
// When listing identical items on flea, condense separate items into one stack with a merged stack count
|
||||
// e.g. 2 ammo items, stackObjectCount = 3 for each, will result in 1 stack of 6
|
||||
|
||||
firstListingAndChidren[0].Upd ??= new Upd{ };
|
||||
firstListingAndChidren[0].Upd ??= new Upd { };
|
||||
|
||||
firstListingAndChidren[0].Upd.StackObjectsCount = stackCountTotal;
|
||||
|
||||
@@ -568,14 +516,11 @@ public class RagfairController
|
||||
var newRootOfferItem = offer.Items[0];
|
||||
|
||||
// Average offer price for single item (or whole weapon)
|
||||
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData{ TemplateId = offer.Items[0].Template });
|
||||
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData { TemplateId = offer.Items[0].Template });
|
||||
var averageOfferPrice = averages.Avg;
|
||||
|
||||
// Check for and apply item price modifer if it exists in config
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(newRootOfferItem.Template, out var itemPriceModifer))
|
||||
{
|
||||
averageOfferPrice *= itemPriceModifer;
|
||||
}
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(newRootOfferItem.Template, out var itemPriceModifer)) averageOfferPrice *= itemPriceModifer;
|
||||
|
||||
// Get average of item+children quality
|
||||
var qualityMultiplier = _itemHelper.GetItemQualityModifierForItems(offer.Items, true);
|
||||
@@ -590,7 +535,8 @@ public class RagfairController
|
||||
var sellChancePercent = _ragfairSellHelper.CalculateSellChance(
|
||||
averageOfferPrice.Value,
|
||||
playerListedPriceInRub,
|
||||
qualityMultiplier);
|
||||
qualityMultiplier
|
||||
);
|
||||
|
||||
// Create array of sell times for items listed
|
||||
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int)stackCountTotal);
|
||||
@@ -605,11 +551,9 @@ public class RagfairController
|
||||
playerListedPriceInRub,
|
||||
(int)stackCountTotal,
|
||||
offerRequest,
|
||||
output);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
output
|
||||
);
|
||||
if (taxFeeChargeFailed) return output;
|
||||
}
|
||||
|
||||
// Add offer to players profile + add to client response
|
||||
@@ -617,9 +561,7 @@ public class RagfairController
|
||||
output.ProfileChanges[sessionID].RagFairOffers.Add(offer);
|
||||
|
||||
// Remove items from inventory after creating offer
|
||||
foreach (var itemToRemove in offerRequest.Items) {
|
||||
_inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
}
|
||||
foreach (var itemToRemove in offerRequest.Items) _inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -643,14 +585,12 @@ public class RagfairController
|
||||
// Get first item and its children and use as template
|
||||
var firstListingAndChidren = _itemHelper.FindAndReturnChildrenAsItems(
|
||||
pmcData.Inventory.Items,
|
||||
offerRequest.Items[0]);
|
||||
offerRequest.Items[0]
|
||||
);
|
||||
|
||||
// Find items to be listed on flea (+ children) from player inventory
|
||||
var result = GetItemsToListOnFleaFromInventory(pmcData, offerRequest.Items);
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage))
|
||||
{
|
||||
_httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
}
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage)) _httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
|
||||
// Total count of items summed using their stack counts
|
||||
var stackCountTotal = _ragfairOfferHelper.GetTotalStackCountSize(result.Items);
|
||||
@@ -668,14 +608,11 @@ public class RagfairController
|
||||
var newRootOfferItem = offer.Items[0];
|
||||
|
||||
// Single price for an item
|
||||
var averages = GetItemMinAvgMaxFleaPriceValues( new GetMarketPriceRequestData{ TemplateId = firstListingAndChidren[0].Template });
|
||||
var averages = GetItemMinAvgMaxFleaPriceValues(new GetMarketPriceRequestData { TemplateId = firstListingAndChidren[0].Template });
|
||||
var singleItemPrice = averages.Avg;
|
||||
|
||||
// Check for and apply item price modifer if it exists in config
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(newRootOfferItem.Template, out double itemPriceModifer))
|
||||
{
|
||||
singleItemPrice *= itemPriceModifer;
|
||||
}
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(newRootOfferItem.Template, out var itemPriceModifer)) singleItemPrice *= itemPriceModifer;
|
||||
|
||||
// Get average of item+children quality
|
||||
var qualityMultiplier = _itemHelper.GetItemQualityModifierForItems(offer.Items, true);
|
||||
@@ -690,7 +627,8 @@ public class RagfairController
|
||||
var sellChancePercent = _ragfairSellHelper.CalculateSellChance(
|
||||
singleItemPrice.Value * stackCountTotal,
|
||||
playerListedPriceInRub,
|
||||
qualityMultiplier);
|
||||
qualityMultiplier
|
||||
);
|
||||
|
||||
// Create array of sell times for items listed + sell all at once as its a pack
|
||||
offer.SellResults = _ragfairSellHelper.RollForSale(sellChancePercent, (int)stackCountTotal, true);
|
||||
@@ -705,11 +643,9 @@ public class RagfairController
|
||||
playerListedPriceInRub,
|
||||
(int)stackCountTotal,
|
||||
offerRequest,
|
||||
output);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
output
|
||||
);
|
||||
if (taxFeeChargeFailed) return output;
|
||||
}
|
||||
|
||||
// Add offer to players profile + add to client response
|
||||
@@ -717,9 +653,7 @@ public class RagfairController
|
||||
output.ProfileChanges[sessionID].RagFairOffers.Add(offer);
|
||||
|
||||
// Remove items from inventory after creating offer
|
||||
foreach (var itemToRemove in offerRequest.Items) {
|
||||
_inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
}
|
||||
foreach (var itemToRemove in offerRequest.Items) _inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -741,10 +675,7 @@ public class RagfairController
|
||||
|
||||
// Find items to be listed on flea from player inventory
|
||||
var result = GetItemsToListOnFleaFromInventory(pmcData, offerRequest.Items);
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage))
|
||||
{
|
||||
_httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
}
|
||||
if (result.Items is null || !string.IsNullOrEmpty(result.ErrorMessage)) _httpResponseUtil.AppendErrorToOutput(output, result.ErrorMessage);
|
||||
|
||||
// Total count of items summed using their stack counts
|
||||
var stackCountTotal = _ragfairOfferHelper.GetTotalStackCountSize(result.Items);
|
||||
@@ -767,10 +698,8 @@ public class RagfairController
|
||||
var averageOfferPriceSingleItem = averages.Avg;
|
||||
|
||||
// Check for and apply item price modifer if it exists in config
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(rootItem.Template, out double itemPriceModifer))
|
||||
{
|
||||
if (_ragfairConfig.Dynamic.ItemPriceMultiplier.TryGetValue(rootItem.Template, out var itemPriceModifer))
|
||||
averageOfferPriceSingleItem *= itemPriceModifer;
|
||||
}
|
||||
|
||||
// Multiply single item price by quality
|
||||
averageOfferPriceSingleItem *= qualityMultiplier;
|
||||
@@ -795,10 +724,7 @@ public class RagfairController
|
||||
offerRequest,
|
||||
output
|
||||
);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (taxFeeChargeFailed) return output;
|
||||
}
|
||||
|
||||
// Add offer to players profile + add to client response
|
||||
@@ -806,10 +732,7 @@ public class RagfairController
|
||||
output.ProfileChanges[sessionID].RagFairOffers.Add(offer);
|
||||
|
||||
// Remove items from inventory after creating offer
|
||||
foreach (var itemToRemove in offerRequest.Items)
|
||||
{
|
||||
_inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
}
|
||||
foreach (var itemToRemove in offerRequest.Items) _inventoryHelper.RemoveItem(pmcData, itemToRemove, sessionID, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -846,10 +769,7 @@ public class RagfairController
|
||||
offerRequest.SellInOnePiece.GetValueOrDefault(false)
|
||||
);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Offer tax to charge: {tax}, pulled from client: {storedClientTaxValue.Count is not null}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Offer tax to charge: {tax}, pulled from client: {storedClientTaxValue.Count is not null}");
|
||||
|
||||
// cleanup of cache now we've used the tax value from it
|
||||
_ragfairTaxService.ClearStoredOfferTaxById(offerRequest.Items.First());
|
||||
@@ -892,7 +812,7 @@ public class RagfairController
|
||||
{
|
||||
Template = item.Template,
|
||||
Count = item.Count,
|
||||
OnlyFunctional = item.OnlyFunctional,
|
||||
OnlyFunctional = item.OnlyFunctional
|
||||
}
|
||||
);
|
||||
|
||||
@@ -919,13 +839,9 @@ public class RagfairController
|
||||
var requestedItemTpl = item.Template;
|
||||
|
||||
if (_paymentHelper.IsMoneyTpl(requestedItemTpl))
|
||||
{
|
||||
requirementsPriceInRub += _handbookHelper.InRUB(item.Count.Value, requestedItemTpl);
|
||||
}
|
||||
else
|
||||
{
|
||||
requirementsPriceInRub += _itemHelper.GetDynamicItemPrice(requestedItemTpl).Value * item.Count.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return requirementsPriceInRub;
|
||||
@@ -963,12 +879,6 @@ public class RagfairController
|
||||
return new GetItemsToListOnFleaFromInventoryResult { Items = itemsToReturn, ErrorMessage = errorMessage };
|
||||
}
|
||||
|
||||
public record GetItemsToListOnFleaFromInventoryResult
|
||||
{
|
||||
public List<List<Item>>? Items { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
public ItemEventRouterResponse RemoveOffer(RemoveOfferRequestData removeRequest, string sessionId)
|
||||
{
|
||||
var output = _eventOutputHolder.GetOutput(sessionId);
|
||||
@@ -1050,10 +960,7 @@ public class RagfairController
|
||||
{
|
||||
var count = 1;
|
||||
var sellInOncePiece = playerOffer.SellInOnePiece.GetValueOrDefault(false);
|
||||
if (!sellInOncePiece)
|
||||
{
|
||||
count = (int)playerOffer.Items.Sum(offerItem => offerItem.Upd?.StackObjectsCount ?? 0);
|
||||
}
|
||||
if (!sellInOncePiece) count = (int)playerOffer.Items.Sum(offerItem => offerItem.Upd?.StackObjectsCount ?? 0);
|
||||
|
||||
var tax = _ragfairTaxService.CalculateTax(
|
||||
playerOffer.Items.First(),
|
||||
@@ -1066,12 +973,10 @@ public class RagfairController
|
||||
var request = CreateBuyTradeRequestObject(CurrencyType.RUB, tax);
|
||||
_paymentService.PayMoney(pmcData, request, sessionId, output);
|
||||
if (output.Warnings.Count > 0)
|
||||
{
|
||||
return _httpResponseUtil.AppendErrorToOutput(
|
||||
output,
|
||||
_localisationService.GetText("ragfair-unable_to_pay_commission_fee")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra time to offer
|
||||
@@ -1096,7 +1001,7 @@ public class RagfairController
|
||||
Type = "",
|
||||
ItemId = "",
|
||||
Count = 0,
|
||||
SchemeId = 0,
|
||||
SchemeId = 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1117,4 +1022,10 @@ public class RagfairController
|
||||
|
||||
return offerToReturn;
|
||||
}
|
||||
|
||||
public record GetItemsToListOnFleaFromInventoryResult
|
||||
{
|
||||
public List<List<Item>>? Items { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Core.Controllers;
|
||||
public class RepairController(
|
||||
EventOutputHolder _eventOutputHolder,
|
||||
RepairService _repairService
|
||||
)
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle TraderRepair event
|
||||
@@ -29,7 +29,8 @@ public class RepairController(
|
||||
var output = _eventOutputHolder.GetOutput(sessionID);
|
||||
|
||||
// find the item to repair
|
||||
foreach (var repairItem in body.RepairItems) {
|
||||
foreach (var repairItem in body.RepairItems)
|
||||
{
|
||||
var repairDetails = _repairService.RepairItemByTrader(sessionID, pmcData, repairItem, body.TId);
|
||||
|
||||
_repairService.PayForRepair(
|
||||
@@ -38,12 +39,10 @@ public class RepairController(
|
||||
repairItem.Id,
|
||||
repairDetails.RepairCost.Value,
|
||||
body.TId,
|
||||
output);
|
||||
output
|
||||
);
|
||||
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return output;
|
||||
|
||||
// Add repaired item to output object
|
||||
output.ProfileChanges[sessionID].Items.ChangedItems.Add(repairDetails.RepairedItem);
|
||||
|
||||
@@ -89,10 +89,7 @@ public class RepeatableQuestController(
|
||||
);
|
||||
|
||||
// If the configuration dictates to replace with the same quest type, adjust the available quest types
|
||||
if (repeatableConfig?.KeepDailyQuestTypeOnReplacement is not null)
|
||||
{
|
||||
repeatableConfig.Types = [questToReplace.Type.ToString()];
|
||||
}
|
||||
if (repeatableConfig?.KeepDailyQuestTypeOnReplacement is not null) repeatableConfig.Types = [questToReplace.Type.ToString()];
|
||||
|
||||
// Generate meta-data for what type/levelrange of quests can be generated for player
|
||||
var allowedQuestTypes = GenerateQuestPool(repeatableConfig, pmcData.Info.Level);
|
||||
@@ -117,11 +114,9 @@ public class RepeatableQuestController(
|
||||
repeatablesOfTypeInProfile.ActiveQuests.Add(newRepeatableQuest);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Removing: {repeatableConfig.Name} quest: {questToReplace.Id} from trader: {questToReplace.TraderId} as its been replaced"
|
||||
);
|
||||
}
|
||||
|
||||
RemoveQuestFromProfile(fullProfile, questToReplace.Id);
|
||||
|
||||
@@ -153,10 +148,7 @@ public class RepeatableQuestController(
|
||||
// Not free, Charge player + appy charisma bonus to cost of replacement
|
||||
cost.Count = (int)Math.Truncate(cost.Count.Value * (1 - Math.Truncate(charismaBonus / 100) * 0.001));
|
||||
_paymentService.AddPaymentToOutput(pmcData, cost.TemplateId, cost.Count.Value, sessionID, output);
|
||||
if (output.Warnings.Count > 0)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (output.Warnings.Count > 0) return output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,16 +219,12 @@ public class RepeatableQuestController(
|
||||
string replacedQuestId)
|
||||
{
|
||||
if (repeatablesOfTypeInProfile.ActiveQuests.Count == 1)
|
||||
{
|
||||
// Only one repeatable quest being replaced (e.g. scav_daily), remove everything ready for new quest requirement to be added
|
||||
// Will assist in cleanup of existing profiles data
|
||||
repeatablesOfTypeInProfile.ChangeRequirement.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multiple active quests of this type (e.g. daily or weekly) are active, just remove the single replaced quest
|
||||
repeatablesOfTypeInProfile.ChangeRequirement.Remove(replacedQuestId);
|
||||
}
|
||||
}
|
||||
|
||||
private RepeatableQuest? AttemptToGenerateRepeatableQuest(string sessionId, PmcData pmcData,
|
||||
@@ -256,18 +244,13 @@ public class RepeatableQuestController(
|
||||
);
|
||||
|
||||
if (newRepeatableQuest is not null)
|
||||
{
|
||||
// Successfully generated a quest, exit loop
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (attempts > maxAttempts)
|
||||
{
|
||||
_logger.Error("We were stuck in repeatable quest generation. This should never happen. Please report");
|
||||
}
|
||||
if (attempts > maxAttempts) _logger.Error("We were stuck in repeatable quest generation. This should never happen. Please report");
|
||||
|
||||
return newRepeatableQuest;
|
||||
}
|
||||
@@ -279,12 +262,10 @@ public class RepeatableQuestController(
|
||||
|
||||
// Find quest we're replacing in scav profile quests array and remove it
|
||||
if (fullProfile.CharacterData.ScavData is not null)
|
||||
{
|
||||
_questHelper.FindAndRemoveQuestFromArrayIfExists(
|
||||
questToReplaceId,
|
||||
fullProfile.CharacterData.ScavData.Quests
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,10 +282,8 @@ public class RepeatableQuestController(
|
||||
var questToReplace =
|
||||
repeatablesInProfile.ActiveQuests.FirstOrDefault(repeatable => repeatable.Id == questId);
|
||||
if (questToReplace is null)
|
||||
{
|
||||
// Not found, skip to next repeatable sub-type
|
||||
continue;
|
||||
}
|
||||
|
||||
return new GetRepeatableByIdResult { Quest = questToReplace, RepeatableType = repeatablesInProfile };
|
||||
}
|
||||
@@ -328,20 +307,15 @@ public class RepeatableQuestController(
|
||||
|
||||
var canAccessRepeatables = CanProfileAccessRepeatableQuests(repeatableConfig, pmcData);
|
||||
if (!canAccessRepeatables)
|
||||
{
|
||||
// Don't send any repeatables, even existing ones
|
||||
continue;
|
||||
}
|
||||
|
||||
// Existing repeatables are still valid, add to return data and move to next sub-type
|
||||
if (currentTime < generatedRepeatables.EndTime - 1)
|
||||
{
|
||||
returnData.Add(generatedRepeatables);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"[Quest Check] {repeatableTypeLower} quests are still valid.");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"[Quest Check] {repeatableTypeLower} quests are still valid.");
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -351,10 +325,7 @@ public class RepeatableQuestController(
|
||||
// Set endtime to be now + new duration
|
||||
generatedRepeatables.EndTime = currentTime + repeatableConfig.ResetTime;
|
||||
generatedRepeatables.InactiveQuests = [];
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generating new {repeatableTypeLower}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Generating new {repeatableTypeLower}");
|
||||
|
||||
// Put old quests to inactive (this is required since only then the client makes them fail due to non-completion)
|
||||
// Also need to push them to the "inactiveQuests" list since we need to remove them from offraidData.profile.Quests
|
||||
@@ -391,10 +362,7 @@ public class RepeatableQuestController(
|
||||
}
|
||||
|
||||
// check if there are no more quest types available
|
||||
if (questTypePool.Types.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (questTypePool.Types.Count == 0) break;
|
||||
|
||||
quest.Side = repeatableConfig.Side;
|
||||
generatedRepeatables.ActiveQuests.Add(quest);
|
||||
@@ -473,18 +441,12 @@ public class RepeatableQuestController(
|
||||
private bool CanProfileAccessRepeatableQuests(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
|
||||
{
|
||||
// PMC and daily quests not unlocked yet
|
||||
if (repeatableConfig.Side == "Pmc" && !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (repeatableConfig.Side == "Pmc" && !PlayerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)) return false;
|
||||
|
||||
// Scav and daily quests not unlocked yet
|
||||
if (repeatableConfig.Side == "Scav" && !PlayerHasDailyScavQuestsUnlocked(pmcData))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug("Daily scav quests still locked, Intel center not built");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug("Daily scav quests still locked, Intel center not built");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -521,21 +483,16 @@ public class RepeatableQuestController(
|
||||
foreach (var activeQuest in generatedRepeatables.ActiveQuests)
|
||||
{
|
||||
var questStatusInProfile = pmcData.Quests.FirstOrDefault(quest => quest.QId == activeQuest.Id);
|
||||
if (questStatusInProfile is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (questStatusInProfile is null) continue;
|
||||
|
||||
// Keep finished quests in list so player can hand in
|
||||
if (questStatusInProfile.Status == QuestStatusEnum.AvailableForFinish)
|
||||
{
|
||||
questsToKeep.Add(activeQuest);
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug( // TODO: this shouldnt happen, doesnt on live
|
||||
$"Keeping repeatable quest: {activeQuest.Id} in activeQuests since it is available to hand in"
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -632,17 +589,10 @@ public class RepeatableQuestController(
|
||||
{
|
||||
var locationNames = new List<string>();
|
||||
foreach (var locationName in value)
|
||||
{
|
||||
if (IsPmcLevelAllowedOnLocation(locationName, pmcLevel))
|
||||
{
|
||||
locationNames.Add(locationName);
|
||||
}
|
||||
}
|
||||
|
||||
if (locationNames.Count > 0)
|
||||
{
|
||||
allowedLocation[location] = locationNames;
|
||||
}
|
||||
if (locationNames.Count > 0) allowedLocation[location] = locationNames;
|
||||
}
|
||||
|
||||
return allowedLocation;
|
||||
@@ -657,16 +607,10 @@ public class RepeatableQuestController(
|
||||
protected bool IsPmcLevelAllowedOnLocation(string location, int pmcLevel)
|
||||
{
|
||||
// All PMC levels are allowed for 'any' location requirement
|
||||
if (location == ELocationName.any.ToString())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (location == ELocationName.any.ToString()) return true;
|
||||
|
||||
var locationBase = _databaseService.GetLocation(location.ToLower())?.Base;
|
||||
if (locationBase is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (locationBase is null) return true;
|
||||
|
||||
return pmcLevel <= locationBase.RequiredPlayerLevelMax && pmcLevel >= locationBase.RequiredPlayerLevelMin;
|
||||
}
|
||||
@@ -680,14 +624,10 @@ public class RepeatableQuestController(
|
||||
private int GetQuestCount(RepeatableQuestConfig repeatableConfig, PmcData pmcData)
|
||||
{
|
||||
var questCount = repeatableConfig.NumQuests.GetValueOrDefault(0);
|
||||
if (questCount == 0)
|
||||
{
|
||||
_logger.Warning($"Repeatable {repeatableConfig.Name} quests have a count of 0");
|
||||
}
|
||||
if (questCount == 0) _logger.Warning($"Repeatable {repeatableConfig.Name} quests have a count of 0");
|
||||
|
||||
// Add elite bonus to daily quests
|
||||
if (repeatableConfig.Name.ToLower() == "daily" && _profileHelper.HasEliteSkillLevel(SkillTypes.Charisma, pmcData))
|
||||
{
|
||||
// Elite charisma skill gives extra daily quest(s)
|
||||
questCount += _databaseService
|
||||
.GetGlobals()
|
||||
@@ -698,7 +638,6 @@ public class RepeatableQuestController(
|
||||
.EliteBonusSettings
|
||||
.RepeatableQuestExtraCount
|
||||
.GetValueOrDefault(0);
|
||||
}
|
||||
|
||||
return questCount;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class TradeController(
|
||||
if (request.Type == "buy_from_trader")
|
||||
{
|
||||
var foundInRaid = _traderConfig.PurchasesAreFoundInRaid;
|
||||
ProcessBuyTradeRequestData buyData = (ProcessBuyTradeRequestData)request;
|
||||
var buyData = (ProcessBuyTradeRequestData)request;
|
||||
_tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output);
|
||||
|
||||
return output;
|
||||
@@ -67,7 +67,7 @@ public class TradeController(
|
||||
// Selling
|
||||
if (request.Type == "sell_to_trader")
|
||||
{
|
||||
ProcessSellTradeRequestData sellData = (ProcessSellTradeRequestData)request;
|
||||
var sellData = (ProcessSellTradeRequestData)request;
|
||||
_tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID, output);
|
||||
|
||||
return output;
|
||||
@@ -97,13 +97,11 @@ public class TradeController(
|
||||
{
|
||||
var fleaOffer = _ragfairServer.GetOffer(offer.Id);
|
||||
if (fleaOffer is null)
|
||||
{
|
||||
return _httpResponseUtil.AppendErrorToOutput(
|
||||
output,
|
||||
$"Offer with ID {offer.Id} not found",
|
||||
BackendErrorCodes.OfferNotFound
|
||||
);
|
||||
}
|
||||
|
||||
if (offer.Count == 0)
|
||||
{
|
||||
@@ -115,19 +113,12 @@ public class TradeController(
|
||||
}
|
||||
|
||||
if (_ragfairOfferHelper.OfferIsFromTrader(fleaOffer))
|
||||
{
|
||||
BuyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
BuyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
|
||||
}
|
||||
|
||||
// Exit loop early if problem found
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return output;
|
||||
}
|
||||
|
||||
return output;
|
||||
@@ -152,17 +143,14 @@ public class TradeController(
|
||||
if (PlayerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
|
||||
{
|
||||
var errorMessage = $"Unable to buy item: {fleaOffer.Items[0].Template} from trader: {fleaOffer.User.Id} as loyalty level too low, skipping";
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(errorMessage);
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug(errorMessage);
|
||||
|
||||
_httpResponseUtil.AppendErrorToOutput(output, errorMessage, BackendErrorCodes.RagfairUnavailable);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessBuyTradeRequestData buyData = new ProcessBuyTradeRequestData
|
||||
var buyData = new ProcessBuyTradeRequestData
|
||||
{
|
||||
Action = "TradingConfirm",
|
||||
Type = "buy_from_ragfair",
|
||||
@@ -170,7 +158,7 @@ public class TradeController(
|
||||
ItemId = fleaOffer.Root,
|
||||
Count = requestOffer.Count,
|
||||
SchemeId = 0,
|
||||
SchemeItems = requestOffer.Items,
|
||||
SchemeItems = requestOffer.Items
|
||||
};
|
||||
|
||||
_tradeHelper.buyItem(pmcData, buyData, sessionId, _traderConfig.PurchasesAreFoundInRaid, output);
|
||||
@@ -191,7 +179,7 @@ public class TradeController(
|
||||
OfferRequest requestOffer,
|
||||
ItemEventRouterResponse output)
|
||||
{
|
||||
ProcessBuyTradeRequestData buyData = new ProcessBuyTradeRequestData
|
||||
var buyData = new ProcessBuyTradeRequestData
|
||||
{
|
||||
Action = "TradingConfirm",
|
||||
Type = "buy_from_ragfair",
|
||||
@@ -199,15 +187,12 @@ public class TradeController(
|
||||
ItemId = fleaOffer.Id, // Store ragfair offerId in buyRequestData.item_id
|
||||
Count = requestOffer.Count,
|
||||
SchemeId = 0,
|
||||
SchemeItems = requestOffer.Items,
|
||||
SchemeItems = requestOffer.Items
|
||||
};
|
||||
|
||||
// buyItem() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use
|
||||
_tradeHelper.buyItem(pmcData, buyData, sessionId, _ragfairConfig.Dynamic.PurchasesAreFoundInRaid, output);
|
||||
if (output.Warnings?.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (output.Warnings?.Count > 0) return;
|
||||
|
||||
// resolve when a profile buy another profile's offer
|
||||
var offerOwnerId = fleaOffer.User?.Id;
|
||||
@@ -237,17 +222,12 @@ public class TradeController(
|
||||
string? offerOwnerId)
|
||||
{
|
||||
// No ownerid, not player offer
|
||||
if (offerOwnerId is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (offerOwnerId is null) return false;
|
||||
|
||||
var offerCreatorProfile = _profileHelper.GetPmcProfile(offerOwnerId);
|
||||
if (offerCreatorProfile is null || offerCreatorProfile.RagfairInfo.Offers?.Count == 0)
|
||||
{
|
||||
// No profile or no offers
|
||||
return false;
|
||||
}
|
||||
|
||||
// Does offer id exist in profile
|
||||
return offerCreatorProfile.RagfairInfo.Offers.Any(offer => offer.Id == offerId);
|
||||
@@ -296,17 +276,14 @@ public class TradeController(
|
||||
int roublesToSend,
|
||||
string trader)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Selling scav items to fence for {roublesToSend} roubles");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Selling scav items to fence for {roublesToSend} roubles");
|
||||
|
||||
// Create single currency item with all currency on it
|
||||
Item rootCurrencyReward = new Item
|
||||
var rootCurrencyReward = new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = Money.ROUBLES,
|
||||
Upd = new Upd { StackObjectsCount = roublesToSend },
|
||||
Upd = new Upd { StackObjectsCount = roublesToSend }
|
||||
};
|
||||
|
||||
// Ensure money is properly split to follow its max stack size limit
|
||||
@@ -317,7 +294,7 @@ public class TradeController(
|
||||
sessionId,
|
||||
_traderHelper.GetTraderById(trader).ToString(),
|
||||
MessageType.MESSAGE_WITH_ITEMS,
|
||||
_randomUtil.GetArrayValue((_databaseService.GetTrader(trader).Dialogue.TryGetValue("SoldItems", out var items)) ? items : new List<string>()),
|
||||
_randomUtil.GetArrayValue(_databaseService.GetTrader(trader).Dialogue.TryGetValue("SoldItems", out var items) ? items : new List<string>()),
|
||||
curencyReward.SelectMany(x => x).ToList(),
|
||||
_timeUtil.GetHoursAsSeconds(72)
|
||||
);
|
||||
@@ -344,10 +321,8 @@ public class TradeController(
|
||||
{
|
||||
var itemDetails = _itemHelper.GetItem(itemToSell.Template);
|
||||
if (!(itemDetails.Key && _itemHelper.IsOfBaseclasses(itemDetails.Value.Id, traderDetails.ItemsBuy.Category)))
|
||||
{
|
||||
// Skip if tpl isn't item OR item doesn't fulfil match traders buy categories
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get price of item multiplied by how many are in stack
|
||||
totalPrice += (int)((handbookPrices[itemToSell.Template] ?? 0) * (itemToSell.Upd?.StackObjectsCount ?? 1));
|
||||
|
||||
@@ -59,17 +59,13 @@ public class TraderController(
|
||||
|
||||
// Adjust price by traderPriceMultipler config property
|
||||
if (_traderConfig.TraderPriceMultipler != 1)
|
||||
{
|
||||
foreach (var kvp in trader.Value?.Assort?.BarterScheme)
|
||||
{
|
||||
var barterSchemeItem = kvp.Value[0][0];
|
||||
|
||||
if (barterSchemeItem != null && _paymentHelper.IsMoneyTpl(barterSchemeItem?.Template))
|
||||
{
|
||||
barterSchemeItem.Count += Math.Round((barterSchemeItem?.Count * _traderConfig?.TraderPriceMultipler) ?? 0D, 2);
|
||||
}
|
||||
barterSchemeItem.Count += Math.Round(barterSchemeItem?.Count * _traderConfig?.TraderPriceMultipler ?? 0D, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Create dict of pristine trader assorts on server start
|
||||
if (_traderAssortService.GetPristineTraderAssort(trader.Key) == null)
|
||||
@@ -81,7 +77,8 @@ public class TraderController(
|
||||
_traderPurchasePersisterService.RemoveStalePurchasesFromProfiles(trader.Key);
|
||||
|
||||
// Set to next hour on clock or current time + 60 mins
|
||||
trader.Value.Base.NextResupply = traderResetStartsWithServer ? (int)_traderHelper.GetNextUpdateTimestamp(trader.Value.Base.Id) : (int)nextHourTimestamp;
|
||||
trader.Value.Base.NextResupply =
|
||||
traderResetStartsWithServer ? (int)_traderHelper.GetNextUpdateTimestamp(trader.Value.Base.Id) : (int)nextHourTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,10 +98,7 @@ public class TraderController(
|
||||
continue;
|
||||
case Traders.FENCE:
|
||||
{
|
||||
if (_fenceService.NeedsPartialRefresh())
|
||||
{
|
||||
_fenceService.GenerateFenceAssorts();
|
||||
}
|
||||
if (_fenceService.NeedsPartialRefresh()) _fenceService.GenerateFenceAssorts();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -135,10 +129,7 @@ public class TraderController(
|
||||
{
|
||||
traders.Add(_traderHelper.GetTrader(traderId, sessionId));
|
||||
|
||||
if (pmcData?.Info != null)
|
||||
{
|
||||
_traderHelper.LevelUp(traderId, pmcData);
|
||||
}
|
||||
if (pmcData?.Info != null) _traderHelper.LevelUp(traderId, pmcData);
|
||||
}
|
||||
|
||||
traders.Sort(SortByTraderId);
|
||||
@@ -186,7 +177,7 @@ public class TraderController(
|
||||
{
|
||||
var handbookPrices = _ragfairPriceService.GetAllStaticPrices();
|
||||
|
||||
return new()
|
||||
return new GetItemPricesResponse
|
||||
{
|
||||
SupplyNextTime = _traderHelper.GetNextUpdateTimestamp(traderId),
|
||||
Prices = handbookPrices,
|
||||
|
||||
@@ -23,10 +23,7 @@ public class WishlistController(
|
||||
AddToWishlistRequest request,
|
||||
string sessionId)
|
||||
{
|
||||
foreach (var item in request.Items)
|
||||
{
|
||||
pmcData.WishList.Dictionary.Add(item.Key, item.Value);
|
||||
}
|
||||
foreach (var item in request.Items) pmcData.WishList.Dictionary.Add(item.Key, item.Value);
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
@@ -43,10 +40,7 @@ public class WishlistController(
|
||||
RemoveFromWishlistRequest request,
|
||||
string sessionId)
|
||||
{
|
||||
foreach (var itemId in request.Items)
|
||||
{
|
||||
pmcData.WishList.Dictionary.Remove(itemId);
|
||||
}
|
||||
foreach (var itemId in request.Items) pmcData.WishList.Dictionary.Remove(itemId);
|
||||
|
||||
return _eventOutputHolder.GetOutput(sessionId);
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ public interface OnLoad
|
||||
{
|
||||
Task OnLoad();
|
||||
string GetRoute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,7 @@ public abstract class Router
|
||||
|
||||
protected List<HandledRoute> GetInternalHandledRoutes()
|
||||
{
|
||||
if (handledRoutes.Count == 0)
|
||||
{
|
||||
handledRoutes = GetHandledRoutes();
|
||||
}
|
||||
if (handledRoutes.Count == 0) handledRoutes = GetHandledRoutes();
|
||||
|
||||
return handledRoutes;
|
||||
}
|
||||
@@ -31,11 +28,9 @@ public abstract class Router
|
||||
public bool CanHandle(string url, bool partialMatch = false)
|
||||
{
|
||||
if (partialMatch)
|
||||
{
|
||||
return GetInternalHandledRoutes()
|
||||
.Where((r) => r.dynamic)
|
||||
.Any((r) => url.Contains(r.route));
|
||||
}
|
||||
|
||||
return GetInternalHandledRoutes()
|
||||
.Where((r) => !r.dynamic)
|
||||
@@ -59,10 +54,7 @@ public abstract class StaticRouter : Router
|
||||
var action = _actions.Single(route => route.url == url);
|
||||
var type = action.bodyType;
|
||||
IRequestData? info = null;
|
||||
if (type != null && !string.IsNullOrEmpty(body))
|
||||
{
|
||||
info = (IRequestData?)_jsonUtil.Deserialize(body, type);
|
||||
}
|
||||
if (type != null && !string.IsNullOrEmpty(body)) info = (IRequestData?)_jsonUtil.Deserialize(body, type);
|
||||
return action.action(url, info, sessionID, output);
|
||||
}
|
||||
|
||||
@@ -74,8 +66,8 @@ public abstract class StaticRouter : Router
|
||||
|
||||
public abstract class DynamicRouter : Router
|
||||
{
|
||||
private readonly List<RouteAction> actions;
|
||||
private readonly JsonUtil _jsonUtil;
|
||||
private readonly List<RouteAction> actions;
|
||||
|
||||
public DynamicRouter(JsonUtil jsonUtil, List<RouteAction> routes) : base()
|
||||
{
|
||||
@@ -88,10 +80,7 @@ public abstract class DynamicRouter : Router
|
||||
var action = actions.First(r => url.Contains(r.url));
|
||||
var type = action.bodyType;
|
||||
IRequestData? info = null;
|
||||
if (type != null && !string.IsNullOrEmpty(body))
|
||||
{
|
||||
info = (IRequestData?)_jsonUtil.Deserialize(body, type);
|
||||
}
|
||||
if (type != null && !string.IsNullOrEmpty(body)) info = (IRequestData?)_jsonUtil.Deserialize(body, type);
|
||||
return action.action(url, info, sessionID, output);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
// Get mod pool for the desired item
|
||||
if (!settings.ModPool.TryGetValue(parentTemplate.Id, out var compatibleModsPool))
|
||||
{
|
||||
_logger.Warning($"bot: {settings.BotData.Role} lacks a mod slot pool for item: {parentTemplate.Id} {parentTemplate.Name}");
|
||||
}
|
||||
|
||||
// Iterate over mod pool and choose mods to add to item
|
||||
foreach (var (modSlotName, modPool) in compatibleModsPool ?? [])
|
||||
@@ -93,16 +91,10 @@ public class BotEquipmentModGenerator(
|
||||
);
|
||||
|
||||
// Rolled to skip mod and it shouldn't be force-spawned
|
||||
if (modSpawnResult == ModSpawn.SKIP && !forceSpawn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (modSpawnResult == ModSpawn.SKIP && !forceSpawn) continue;
|
||||
|
||||
// Ensure submods for nvgs all spawn together
|
||||
if (modSlotName == "mod_nvg")
|
||||
{
|
||||
forceSpawn = true;
|
||||
}
|
||||
if (modSlotName == "mod_nvg") forceSpawn = true;
|
||||
|
||||
// Get pool of items we can add for this slot
|
||||
var modPoolToChooseFrom = modPool;
|
||||
@@ -110,10 +102,8 @@ public class BotEquipmentModGenerator(
|
||||
// Filter the pool of items in blacklist
|
||||
var filteredModPool = FilterModsByBlacklist(modPoolToChooseFrom, specificBlacklist, modSlotName);
|
||||
if (filteredModPool.Count > 0)
|
||||
{
|
||||
// use filtered pool as it has items in it
|
||||
modPoolToChooseFrom = filteredModPool;
|
||||
}
|
||||
|
||||
// Slot can hold armor plates + we are filtering possible items by bot level, handle
|
||||
if (
|
||||
@@ -131,11 +121,9 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
case Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER:
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Plate slot: {modSlotName} selection for armor: {parentTemplate.Id} failed: {plateSlotFilteringOutcome.Result}, skipping"
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
case Result.LACKS_PLATE_WEIGHTS:
|
||||
@@ -172,10 +160,7 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Compatible item not found + not required - skip
|
||||
if (!(found || itemSlotTemplate.Required.GetValueOrDefault(false)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!(found || itemSlotTemplate.Required.GetValueOrDefault(false))) continue;
|
||||
|
||||
// Get chosen mods db template and check it fits into slot
|
||||
var modTemplate = _itemHelper.GetItem(modTpl);
|
||||
@@ -188,9 +173,7 @@ public class BotEquipmentModGenerator(
|
||||
settings.BotData.Role
|
||||
)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate new id to ensure all items are unique on bot
|
||||
var modId = _hashUtil.Generate();
|
||||
@@ -200,7 +183,6 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
// Does item being added exist in mod pool - has its own mod pool
|
||||
if (settings.ModPool.ContainsKey(modTpl))
|
||||
{
|
||||
// Call self again with mod being added as item to add child mods to
|
||||
GenerateModsForEquipment(
|
||||
equipment,
|
||||
@@ -210,7 +192,6 @@ public class BotEquipmentModGenerator(
|
||||
specificBlacklist,
|
||||
forceSpawn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return equipment;
|
||||
@@ -230,7 +211,7 @@ public class BotEquipmentModGenerator(
|
||||
var result = new FilterPlateModsForSlotByLevelResult
|
||||
{
|
||||
Result = Result.UNKNOWN_FAILURE,
|
||||
PlateModTemplates = null,
|
||||
PlateModTemplates = null
|
||||
};
|
||||
|
||||
// Not pmc or not a plate slot, return original mod pool array
|
||||
@@ -299,29 +280,21 @@ public class BotEquipmentModGenerator(
|
||||
chosenArmorPlateLevelString = chosenArmorPlateLevelDouble.ToString();
|
||||
|
||||
// New chosen plate class is higher than max, then set to min and check if valid
|
||||
if (chosenArmorPlateLevelDouble > minMaxArmorPlateClass.Max)
|
||||
{
|
||||
chosenArmorPlateLevelString = minMaxArmorPlateClass.Min.ToString();
|
||||
}
|
||||
if (chosenArmorPlateLevelDouble > minMaxArmorPlateClass.Max) chosenArmorPlateLevelString = minMaxArmorPlateClass.Min.ToString();
|
||||
|
||||
findCompatiblePlateAttempts++;
|
||||
|
||||
platesOfDesiredLevel = platesFromDb.Where((item) => item.Properties.ArmorClass == chosenArmorPlateLevelDouble);
|
||||
// Valid plates found, exit
|
||||
if (platesOfDesiredLevel.Any())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (platesOfDesiredLevel.Any()) break;
|
||||
|
||||
// No valid plate class found in 3 tries, attempt default plates
|
||||
if (findCompatiblePlateAttempts >= maxAttempts)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Plate filter too restrictive for armor: {armorItem.Name} {armorItem.Id}, unable to find plates of level: {chosenArmorPlateLevelString}, using items default plate"
|
||||
);
|
||||
}
|
||||
|
||||
var defaultPlate = GetDefaultPlateTpl(armorItem, modSlot);
|
||||
if (defaultPlate is not null)
|
||||
@@ -365,15 +338,9 @@ public class BotEquipmentModGenerator(
|
||||
platePool.Sort(
|
||||
(x, y) =>
|
||||
{
|
||||
if (x.Properties.ArmorClass < y.Properties.ArmorClass)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (x.Properties.ArmorClass < y.Properties.ArmorClass) return -1;
|
||||
|
||||
if (x.Properties.ArmorClass > y.Properties.ArmorClass)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (x.Properties.ArmorClass > y.Properties.ArmorClass) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -381,8 +348,8 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
return new MinMax
|
||||
{
|
||||
Min = (platePool[0].Properties.ArmorClass),
|
||||
Max = (platePool[platePool.Count - 1].Properties.ArmorClass),
|
||||
Min = platePool[0].Properties.ArmorClass,
|
||||
Max = platePool[platePool.Count - 1].Properties.ArmorClass
|
||||
};
|
||||
}
|
||||
|
||||
@@ -430,7 +397,7 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
weaponName = request.ParentTemplate.Name,
|
||||
weaponId = request.ParentTemplate.Id,
|
||||
botRole = request.BotData.Role,
|
||||
botRole = request.BotData.Role
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -469,7 +436,7 @@ public class BotEquipmentModGenerator(
|
||||
modSlot = modSlot,
|
||||
weaponId = request.ParentTemplate.Id,
|
||||
weaponName = request.ParentTemplate.Name,
|
||||
botRole = request.BotData.Role,
|
||||
botRole = request.BotData.Role
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -484,10 +451,7 @@ public class BotEquipmentModGenerator(
|
||||
request.ModSpawnChances,
|
||||
botEquipConfig
|
||||
);
|
||||
if (modSpawnResult == ModSpawn.SKIP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (modSpawnResult == ModSpawn.SKIP) continue;
|
||||
|
||||
var isRandomisableSlot = randomisationSettings?.RandomisedWeaponModSlots?.Contains(modSlot) ?? false;
|
||||
ModToSpawnRequest modToSpawnRequest = new()
|
||||
@@ -509,15 +473,9 @@ public class BotEquipmentModGenerator(
|
||||
var modToAdd = ChooseModToPutIntoSlot(modToSpawnRequest);
|
||||
|
||||
// Compatible mod not found
|
||||
if (modToAdd is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (modToAdd is null) continue;
|
||||
|
||||
if (!IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role)) continue;
|
||||
|
||||
var modToAddTemplate = modToAdd.Value;
|
||||
// Skip adding mod to weapon if type limit reached
|
||||
@@ -530,9 +488,7 @@ public class BotEquipmentModGenerator(
|
||||
request.Weapon
|
||||
)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If item is a mount for scopes, set scope chance to 100%, this helps fix empty mounts appearing on weapons
|
||||
if (ModSlotCanHoldScope(modSlot, modToAddTemplate.Value.Parent))
|
||||
@@ -543,7 +499,6 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
// Hydrate pool of mods that fit into mount as its a randomisable slot
|
||||
if (isRandomisableSlot)
|
||||
{
|
||||
// Add scope mods to modPool dictionary to ensure the mount has a scope in the pool to pick
|
||||
AddCompatibleModsForProvidedMod(
|
||||
"mod_scope",
|
||||
@@ -551,7 +506,6 @@ public class BotEquipmentModGenerator(
|
||||
request.ModPool,
|
||||
botEquipBlacklist
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If picked item is muzzle adapter that can hold a child, adjust spawn chance
|
||||
@@ -572,14 +526,12 @@ public class BotEquipmentModGenerator(
|
||||
// Handguard mod can take a sub handguard mod + weapon has no UBGL (takes same slot)
|
||||
// Force spawn chance to be 100% to ensure it gets added
|
||||
if (
|
||||
modSlot == "mod_handguard" &&
|
||||
modToAddTemplate.Value.Properties.Slots.Any((slot) => slot.Name == "mod_handguard") &&
|
||||
!request.Weapon.Any((item) => item.SlotId == "mod_launcher")
|
||||
)
|
||||
{
|
||||
modSlot == "mod_handguard" &&
|
||||
modToAddTemplate.Value.Properties.Slots.Any((slot) => slot.Name == "mod_handguard") &&
|
||||
!request.Weapon.Any((item) => item.SlotId == "mod_launcher")
|
||||
)
|
||||
// Needed for handguards with lower
|
||||
request.ModSpawnChances["mod_handguard"] = 100;
|
||||
}
|
||||
|
||||
// If stock mod can take a sub stock mod, force spawn chance to be 100% to ensure sub-stock gets added
|
||||
// Or if bot has stock force enabled
|
||||
@@ -594,13 +546,8 @@ public class BotEquipmentModGenerator(
|
||||
if (_itemHelper.IsOfBaseclass(modToAddTemplate.Value.Id, BaseClasses.IRON_SIGHT))
|
||||
{
|
||||
if (modSlot == "mod_sight_front")
|
||||
{
|
||||
request.WeaponStats.HasFrontIronSight = true;
|
||||
}
|
||||
else if (modSlot == "mod_sight_rear")
|
||||
{
|
||||
request.WeaponStats.HasRearIronSight = true;
|
||||
}
|
||||
else if (modSlot == "mod_sight_rear") request.WeaponStats.HasRearIronSight = true;
|
||||
}
|
||||
else if (!(request.WeaponStats.HasOptic ?? false) && _itemHelper.IsOfBaseclass(modToAddTemplate.Value.Id, BaseClasses.SIGHTS))
|
||||
{
|
||||
@@ -620,10 +567,7 @@ public class BotEquipmentModGenerator(
|
||||
);
|
||||
|
||||
// Update conflicting item list now item has been chosen
|
||||
foreach (var conflictingItem in modToAddTemplate.Value.Properties.ConflictingItems)
|
||||
{
|
||||
request.ConflictingItemTpls.Add(conflictingItem);
|
||||
}
|
||||
foreach (var conflictingItem in modToAddTemplate.Value.Properties.ConflictingItems) request.ConflictingItemTpls.Add(conflictingItem);
|
||||
|
||||
// I first thought we could use the recursive generateModsForItems as previously for cylinder magazines.
|
||||
// However, the recursion doesn't go over the slots of the parent mod but over the modPool which is given by the bot config
|
||||
@@ -673,7 +617,7 @@ public class BotEquipmentModGenerator(
|
||||
ParentTemplate = modToAddTemplate.Value,
|
||||
ModSpawnChances = request.ModSpawnChances,
|
||||
AmmoTpl = request.AmmoTpl,
|
||||
BotData = new()
|
||||
BotData = new BotData
|
||||
{
|
||||
Role = request.BotData.Role,
|
||||
Level = request.BotData.Level,
|
||||
@@ -725,10 +669,8 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
// Gas block /w front sight is special case, deem it a 'front sight' too
|
||||
if (modSlot == "mod_gas_block" && tpl == "5ae30e795acfc408fb139a0b")
|
||||
{
|
||||
// M4A1 front sight with gas block
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((string[]) ["mod_sight_front", "mod_sight_rear"]).Contains(modSlot);
|
||||
}
|
||||
@@ -741,19 +683,17 @@ public class BotEquipmentModGenerator(
|
||||
/// <returns>true if it can hold a scope</returns>
|
||||
public bool ModSlotCanHoldScope(string modSlot, string modsParentId)
|
||||
{
|
||||
return (
|
||||
((string[])
|
||||
[
|
||||
"mod_scope",
|
||||
"mod_mount",
|
||||
"mod_mount_000",
|
||||
"mod_scope_000",
|
||||
"mod_scope_001",
|
||||
"mod_scope_002",
|
||||
"mod_scope_003",
|
||||
]).Contains(modSlot.ToLower()) &&
|
||||
modsParentId == BaseClasses.MOUNT
|
||||
);
|
||||
return ((string[])
|
||||
[
|
||||
"mod_scope",
|
||||
"mod_mount",
|
||||
"mod_mount_000",
|
||||
"mod_scope_000",
|
||||
"mod_scope_001",
|
||||
"mod_scope_002",
|
||||
"mod_scope_003"
|
||||
]).Contains(modSlot.ToLower()) &&
|
||||
modsParentId == BaseClasses.MOUNT;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -778,10 +718,7 @@ public class BotEquipmentModGenerator(
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var modName in modSlotsToAdjust)
|
||||
{
|
||||
modSpawnChances[modName] = newChancePercent;
|
||||
}
|
||||
foreach (var modName in modSlotsToAdjust) modSpawnChances[modName] = newChancePercent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -804,10 +741,7 @@ public class BotEquipmentModGenerator(
|
||||
public List<string> SortModKeys(List<string> unsortedSlotKeys, string itemTplWithKeysToSort)
|
||||
{
|
||||
// No need to sort with only 1 item in array
|
||||
if (unsortedSlotKeys.Count <= 1)
|
||||
{
|
||||
return unsortedSlotKeys;
|
||||
}
|
||||
if (unsortedSlotKeys.Count <= 1) return unsortedSlotKeys;
|
||||
|
||||
var isMount = _itemHelper.IsOfBaseclass(itemTplWithKeysToSort, BaseClasses.MOUNT);
|
||||
|
||||
@@ -940,17 +874,13 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
var slotRequired = itemSlot.Required;
|
||||
if (GetAmmoContainers().Contains(modSlotName))
|
||||
{
|
||||
// Always force mags/cartridges in weapon to spawn
|
||||
return ModSpawn.SPAWN;
|
||||
}
|
||||
|
||||
var spawnMod = _probabilityHelper.RollChance(modSpawnChances.GetValueOrDefault(modSlotName.ToLower()));
|
||||
if (!spawnMod && (slotRequired.GetValueOrDefault(false) || (botEquipConfig.WeaponSlotIdsToMakeRequired?.Contains(modSlotName) ?? false)))
|
||||
{
|
||||
// Edge case: Mod is required but spawn chance roll failed, choose default mod spawn for slot
|
||||
return ModSpawn.DEFAULT_MOD;
|
||||
}
|
||||
|
||||
return spawnMod ? ModSpawn.SPAWN : ModSpawn.SKIP;
|
||||
}
|
||||
@@ -967,10 +897,7 @@ public class BotEquipmentModGenerator(
|
||||
var weaponTemplate = _itemHelper.GetItem(request.Weapon[0].Template).Value;
|
||||
|
||||
// It's ammo, use predefined ammo parameter
|
||||
if (GetAmmoContainers().Contains(request.ModSlot) && request.ModSlot != "mod_magazine")
|
||||
{
|
||||
return _itemHelper.GetItem(request.AmmoTpl);
|
||||
}
|
||||
if (GetAmmoContainers().Contains(request.ModSlot) && request.ModSlot != "mod_magazine") return _itemHelper.GetItem(request.AmmoTpl);
|
||||
|
||||
// Ensure there's a pool of mods to pick from
|
||||
var modPool = GetModPoolForSlot(request, weaponTemplate);
|
||||
@@ -978,47 +905,35 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
// Nothing in mod pool + item not required
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Mod pool for optional slot: {request.ModSlot} on item: {request.ParentTemplate.Name} was empty, skipping mod");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter out non-whitelisted scopes, use full modpool if filtered pool would have no elements
|
||||
if (request.ModSlot.Contains("mod_scope") && request.BotWeaponSightWhitelist is not null)
|
||||
{
|
||||
// scope pool has more than one scope
|
||||
if (modPool.Count > 1)
|
||||
{
|
||||
modPool = FilterSightsByWeaponType(request.Weapon[0], modPool, request.BotWeaponSightWhitelist);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.ModSlot == "mod_gas_block")
|
||||
{
|
||||
if (request.WeaponStats.HasOptic ?? false && modPool.Count > 1)
|
||||
if (request.WeaponStats.HasOptic ?? (false && modPool.Count > 1))
|
||||
{
|
||||
// Attempt to limit modpool to low profile gas blocks when weapon has an optic
|
||||
var onlyLowProfileGasBlocks = modPool.Where(
|
||||
(tpl) =>
|
||||
_botConfig.LowProfileGasBlockTpls.Contains(tpl)
|
||||
);
|
||||
if (onlyLowProfileGasBlocks.Count() > 0)
|
||||
{
|
||||
modPool = onlyLowProfileGasBlocks.ToHashSet();
|
||||
}
|
||||
if (onlyLowProfileGasBlocks.Count() > 0) modPool = onlyLowProfileGasBlocks.ToHashSet();
|
||||
}
|
||||
else if (request.WeaponStats.HasRearIronSight ?? false && modPool.Count() > 1)
|
||||
else if (request.WeaponStats.HasRearIronSight ?? (false && modPool.Count() > 1))
|
||||
{
|
||||
// Attempt to limit modpool to high profile gas blocks when weapon has rear iron sight + no front iron sight
|
||||
var onlyHighProfileGasBlocks = modPool.Where(
|
||||
(tpl) => !_botConfig.LowProfileGasBlockTpls.Contains(tpl)
|
||||
);
|
||||
if (onlyHighProfileGasBlocks.Count() > 0)
|
||||
{
|
||||
modPool = onlyHighProfileGasBlocks.ToHashSet();
|
||||
}
|
||||
if (onlyHighProfileGasBlocks.Count() > 0) modPool = onlyHighProfileGasBlocks.ToHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,9 +943,7 @@ public class BotEquipmentModGenerator(
|
||||
(request?.IsRandomisableSlot ?? false) &&
|
||||
request.RandomisationSettings.MinimumMagazineSize is not null
|
||||
)
|
||||
{
|
||||
modPool = GetFilterdMagazinePoolByCapacity(request, modPool).ToHashSet();
|
||||
}
|
||||
|
||||
// Pick random mod that's compatible
|
||||
var chosenModResult = GetCompatibleWeaponModTplForSlotFromPool(
|
||||
@@ -1042,19 +955,13 @@ public class BotEquipmentModGenerator(
|
||||
request.ModSlot
|
||||
);
|
||||
if (chosenModResult.SlotBlocked.GetValueOrDefault(false) && !parentSlot.Required.GetValueOrDefault(false))
|
||||
{
|
||||
// Don't bother trying to fit mod, slot is completely blocked
|
||||
return null;
|
||||
}
|
||||
|
||||
// Log if mod chosen was incompatible
|
||||
if (chosenModResult.Incompatible.GetValueOrDefault(false) && !(parentSlot.Required.GetValueOrDefault(false)))
|
||||
{
|
||||
if (chosenModResult.Incompatible.GetValueOrDefault(false) && !parentSlot.Required.GetValueOrDefault(false))
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to find compatible mod of type: {parentSlot.Name}, in slot: {request.ModSlot} reason: {chosenModResult.Reason}");
|
||||
}
|
||||
}
|
||||
|
||||
// Get random mod to attach from items db for required slots if none found above
|
||||
if (!(chosenModResult.Found ?? false) && parentSlot != null && (parentSlot.Required ?? false))
|
||||
@@ -1064,19 +971,14 @@ public class BotEquipmentModGenerator(
|
||||
}
|
||||
|
||||
// Compatible item not found + not required
|
||||
if (!(chosenModResult.Found.GetValueOrDefault(false)) && parentSlot is not null && (!parentSlot.Required.GetValueOrDefault(false)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!chosenModResult.Found.GetValueOrDefault(false) && parentSlot is not null && !parentSlot.Required.GetValueOrDefault(false)) return null;
|
||||
|
||||
if (!(chosenModResult.Found ?? false) && parentSlot is not null)
|
||||
{
|
||||
if (parentSlot.Required.GetValueOrDefault(false))
|
||||
{
|
||||
_logger.Warning(
|
||||
$"Required slot unable to be filled, {request.ModSlot} on {request.ParentTemplate.Name} {request.ParentTemplate.Id} for weapon: {request.Weapon[0].Template}"
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1105,7 +1007,7 @@ public class BotEquipmentModGenerator(
|
||||
|
||||
if (!desiredMagazineTpls.Any())
|
||||
{
|
||||
_logger.Warning($"Magazine size filter for { weaponTpl} was too strict, ignoring filter");
|
||||
_logger.Warning($"Magazine size filter for {weaponTpl} was too strict, ignoring filter");
|
||||
|
||||
return modPool;
|
||||
}
|
||||
@@ -1130,21 +1032,17 @@ public class BotEquipmentModGenerator(
|
||||
// Filter out incompatible mods from pool
|
||||
var preFilteredModPool = GetFilteredModPool(modPool, request.ConflictingItemTpls);
|
||||
if (preFilteredModPool.Count == 0)
|
||||
{
|
||||
return new()
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"Unable to add mod to {choiceTypeEnum.ToString()} slot: {modSlotName}. All: {modPool.Count()} had conflicts"
|
||||
};
|
||||
}
|
||||
|
||||
// Filter mod pool to only items that appear in parents allowed list
|
||||
preFilteredModPool = preFilteredModPool.Where((tpl) => parentSlot.Props.Filters[0].Filter.Contains(tpl)).ToList();
|
||||
if (preFilteredModPool.Count == 0)
|
||||
{
|
||||
return new() { Incompatible = true, Found = false, Reason = "No mods found in parents allowed list" };
|
||||
}
|
||||
return new ChooseRandomCompatibleModResult { Incompatible = true, Found = false, Reason = "No mods found in parents allowed list" };
|
||||
|
||||
return GetCompatibleModFromPool(preFilteredModPool, choiceTypeEnum, weapon);
|
||||
}
|
||||
@@ -1166,7 +1064,7 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = "unknown",
|
||||
Reason = "unknown"
|
||||
};
|
||||
|
||||
// Limit how many attempts to find a compatible mod can occur before giving up
|
||||
@@ -1178,16 +1076,12 @@ public class BotEquipmentModGenerator(
|
||||
chosenTpl = exhaustableModPool.GetRandomValue();
|
||||
var pickedItemDetails = _itemHelper.GetItem(chosenTpl);
|
||||
if (!pickedItemDetails.Key)
|
||||
{
|
||||
// Not valid item, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pickedItemDetails.Value.Properties is null)
|
||||
{
|
||||
// no props data, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
// Success - Default wanted + only 1 item in pool
|
||||
if (modSpawnType == ModSpawn.DEFAULT_MOD && modPool.Count == 1)
|
||||
@@ -1270,15 +1164,10 @@ public class BotEquipmentModGenerator(
|
||||
public HashSet<string> GetModPoolForSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
|
||||
{
|
||||
// Mod is flagged as being default only, try and find it in globals
|
||||
if (request.ModSpawnResult == ModSpawn.DEFAULT_MOD)
|
||||
{
|
||||
return GetModPoolForDefaultSlot(request, weaponTemplate);
|
||||
}
|
||||
if (request.ModSpawnResult == ModSpawn.DEFAULT_MOD) return GetModPoolForDefaultSlot(request, weaponTemplate);
|
||||
|
||||
if (request.IsRandomisableSlot.GetValueOrDefault(false))
|
||||
{
|
||||
return GetDynamicModPool(request.ParentTemplate.Id, request.ModSlot, request.BotEquipBlacklist);
|
||||
}
|
||||
|
||||
// Required mod is not default or randomisable, use existing pool
|
||||
return request.ItemModPool[request.ModSlot];
|
||||
@@ -1290,12 +1179,8 @@ public class BotEquipmentModGenerator(
|
||||
if (matchingModFromPreset is null)
|
||||
{
|
||||
if (request.ItemModPool[request.ModSlot]?.Count > 1)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"{request.BotData.Role} No default: {request.ModSlot} mod found for: {weaponTemplate.Name}, using existing pool");
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find default in globals, use existing mod pool data
|
||||
return request.ItemModPool[request.ModSlot];
|
||||
@@ -1306,10 +1191,8 @@ public class BotEquipmentModGenerator(
|
||||
// You'd have a mod being picked without any sub-mods in its chain, possibly resulting in missing required mods not being added
|
||||
// Mod is in existing mod pool
|
||||
if (request.ItemModPool[request.ModSlot].Contains(matchingModFromPreset.Template))
|
||||
{
|
||||
// Found mod on preset + it already exists in mod pool
|
||||
return [matchingModFromPreset.Template];
|
||||
}
|
||||
|
||||
// Get an array of items that are allowed in slot from parent item
|
||||
// Check the filter of the slot to ensure a chosen mod fits
|
||||
@@ -1325,36 +1208,26 @@ public class BotEquipmentModGenerator(
|
||||
)
|
||||
{
|
||||
// Chosen mod has no conflicts + no children + is in parent compat list
|
||||
if (!request.ConflictingItemTpls.Contains(matchingModFromPreset.Template))
|
||||
{
|
||||
return [matchingModFromPreset.Template];
|
||||
}
|
||||
if (!request.ConflictingItemTpls.Contains(matchingModFromPreset.Template)) return [matchingModFromPreset.Template];
|
||||
|
||||
// Above chosen mod had conflicts with existing weapon mods
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"{request.BotData.Role} Chosen default: {request.ModSlot} mod found for: {weaponTemplate.Name} weapon conflicts with item on weapon, cannot use default"
|
||||
);
|
||||
}
|
||||
|
||||
var existingModPool = request.ItemModPool[request.ModSlot];
|
||||
if (existingModPool.Count == 1)
|
||||
{
|
||||
// The only item in pool isn't compatible
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"{request.BotData.Role} {request.ModSlot} Mod pool for: {weaponTemplate.Name} weapon has only incompatible items, using parent list instead"
|
||||
);
|
||||
}
|
||||
|
||||
// Last ditch, use full pool of items minus conflicts
|
||||
var newListOfModsForSlot = parentSlotCompatibleItems.Where((tpl) => !request.ConflictingItemTpls.Contains(tpl));
|
||||
if (newListOfModsForSlot.Count() > 0)
|
||||
{
|
||||
return newListOfModsForSlot.ToHashSet();
|
||||
}
|
||||
if (newListOfModsForSlot.Count() > 0) return newListOfModsForSlot.ToHashSet();
|
||||
}
|
||||
|
||||
// Return full mod pool
|
||||
@@ -1381,17 +1254,11 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
// Edge case - using mp5sd reciever means default mp5 handguard doesn't fit
|
||||
var isMp5sd = parentItemTpl == "5926f2e086f7745aae644231";
|
||||
if (isMp5sd)
|
||||
{
|
||||
return _presetHelper.GetPreset("59411abb86f77478f702b5d2");
|
||||
}
|
||||
if (isMp5sd) return _presetHelper.GetPreset("59411abb86f77478f702b5d2");
|
||||
|
||||
// Edge case - dvl 500mm is the silenced barrel and has specific muzzle mods
|
||||
var isDvl500mmSilencedBarrel = parentItemTpl == "5888945a2459774bf43ba385";
|
||||
if (isDvl500mmSilencedBarrel)
|
||||
{
|
||||
return _presetHelper.GetPreset("59e8d2b386f77445830dd299");
|
||||
}
|
||||
if (isDvl500mmSilencedBarrel) return _presetHelper.GetPreset("59e8d2b386f77445830dd299");
|
||||
|
||||
return _presetHelper.GetDefaultPreset(weaponTemplate.Id);
|
||||
}
|
||||
@@ -1405,10 +1272,7 @@ public class BotEquipmentModGenerator(
|
||||
public bool WeaponModComboIsIncompatible(List<Item> weapon, string modTpl)
|
||||
{
|
||||
// STM-9 + AR-15 Lone Star Ion Lite handguard
|
||||
if (weapon[0].Template == "60339954d62c9b14ed777c06" && modTpl == "5d4405f0a4b9361e6a4e6bd9")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (weapon[0].Template == "60339954d62c9b14ed777c06" && modTpl == "5d4405f0a4b9361e6a4e6bd9") return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1464,10 +1328,7 @@ public class BotEquipmentModGenerator(
|
||||
while (exhaustableModPool.HasValues())
|
||||
{
|
||||
tmpModTpl = exhaustableModPool.GetRandomValue();
|
||||
if (!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(items, tmpModTpl, modSlot).Incompatible.GetValueOrDefault(false))
|
||||
{
|
||||
return tmpModTpl;
|
||||
}
|
||||
if (!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(items, tmpModTpl, modSlot).Incompatible.GetValueOrDefault(false)) return tmpModTpl;
|
||||
}
|
||||
|
||||
// No mod found
|
||||
@@ -1497,14 +1358,11 @@ public class BotEquipmentModGenerator(
|
||||
new
|
||||
{
|
||||
modId = modBeingAddedDbTemplate.Value?.Id ?? "UNKNOWN",
|
||||
modSlot = modSlot,
|
||||
modSlot = modSlot
|
||||
}
|
||||
)
|
||||
);
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Item -> {parentTemplate?.Id}; Slot -> {modSlot}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Item -> {parentTemplate?.Id}; Slot -> {modSlot}");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1514,7 +1372,6 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
// Parent slot must be filled but db object is invalid, show warning and return false
|
||||
if (slotAddedToTemplate.Required ?? false)
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService.GetText(
|
||||
"bot-unable_to_add_mod_item_invalid",
|
||||
@@ -1523,11 +1380,10 @@ public class BotEquipmentModGenerator(
|
||||
itemName = modBeingAddedDbTemplate.Value?.Name ?? "UNKNOWN",
|
||||
iodSlot = modSlot,
|
||||
parentItemName = parentTemplate.Name,
|
||||
botRole = botRole,
|
||||
botRole = botRole
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1548,21 +1404,14 @@ public class BotEquipmentModGenerator(
|
||||
EquipmentFilterDetails botEquipBlacklist)
|
||||
{
|
||||
var desiredSlotObject = modTemplate.Properties.Slots?.FirstOrDefault((slot) => slot.Name.Contains(desiredSlotName));
|
||||
if (desiredSlotObject is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (desiredSlotObject is null) return;
|
||||
|
||||
var supportedSubMods = desiredSlotObject.Props.Filters[0].Filter;
|
||||
if (supportedSubMods is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (supportedSubMods is null) return;
|
||||
|
||||
// Filter mods
|
||||
var filteredMods = FilterModsByBlacklist(supportedSubMods.ToHashSet(), botEquipBlacklist, desiredSlotName);
|
||||
if (!filteredMods.Any())
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService
|
||||
.GetText(
|
||||
@@ -1570,11 +1419,10 @@ public class BotEquipmentModGenerator(
|
||||
new
|
||||
{
|
||||
slotName = desiredSlotObject.Name,
|
||||
itemName = modTemplate.Name,
|
||||
itemName = modTemplate.Name
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
modPool.TryAdd(modTemplate.Id, new Dictionary<string, HashSet<string>>());
|
||||
|
||||
@@ -1615,10 +1463,7 @@ public class BotEquipmentModGenerator(
|
||||
public HashSet<string> FilterModsByBlacklist(HashSet<string> allowedMods, EquipmentFilterDetails? botEquipBlacklist, string modSlot)
|
||||
{
|
||||
// No blacklist, nothing to filter out
|
||||
if (botEquipBlacklist is null)
|
||||
{
|
||||
return allowedMods;
|
||||
}
|
||||
if (botEquipBlacklist is null) return allowedMods;
|
||||
|
||||
var result = new HashSet<string>();
|
||||
|
||||
@@ -1651,24 +1496,21 @@ public class BotEquipmentModGenerator(
|
||||
new
|
||||
{
|
||||
weaponId = cylinderMagTemplate.Id,
|
||||
weaponName = cylinderMagTemplate.Name,
|
||||
weaponName = cylinderMagTemplate.Name
|
||||
}
|
||||
)
|
||||
);
|
||||
var camoraSlots = cylinderMagTemplate.Properties.Slots.Where((slot) => slot.Name.StartsWith("camora"));
|
||||
|
||||
// Attempt to generate camora slots for item
|
||||
modPool[cylinderMagTemplate.Id] = new();
|
||||
foreach (var camora in camoraSlots)
|
||||
{
|
||||
modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters?[0].Filter.ToHashSet();
|
||||
}
|
||||
modPool[cylinderMagTemplate.Id] = new Dictionary<string, HashSet<string>>();
|
||||
foreach (var camora in camoraSlots) modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters?[0].Filter.ToHashSet();
|
||||
|
||||
itemModPool = modPool[cylinderMagTemplate.Id];
|
||||
}
|
||||
|
||||
ExhaustableArray<string> exhaustableModPool = null;
|
||||
string modSlot = "cartridges";
|
||||
var modSlot = "cartridges";
|
||||
const string camoraFirstSlot = "camora_000";
|
||||
if (itemModPool.TryGetValue(modSlot, out var value))
|
||||
{
|
||||
@@ -1709,13 +1551,15 @@ public class BotEquipmentModGenerator(
|
||||
{
|
||||
var modSlotId = slot.Name;
|
||||
var modId = _hashUtil.Generate();
|
||||
items.Add(new()
|
||||
{
|
||||
Id = modId,
|
||||
Template = modTpl,
|
||||
ParentId = cylinderMagParentId,
|
||||
SlotId = modSlotId
|
||||
});
|
||||
items.Add(
|
||||
new Item
|
||||
{
|
||||
Id = modId,
|
||||
Template = modTpl,
|
||||
ParentId = cylinderMagParentId,
|
||||
SlotId = modSlotId
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1749,11 +1593,9 @@ public class BotEquipmentModGenerator(
|
||||
if (!botWeaponSightWhitelist.TryGetValue(weaponDetails.Value.Parent, out var whitelistedSightTypes))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Unable to find whitelist for weapon type: {weaponDetails.Value.Parent} {weaponDetails.Value.Name}, skipping sight filtering"
|
||||
);
|
||||
}
|
||||
|
||||
return scopes;
|
||||
}
|
||||
@@ -1794,10 +1636,8 @@ public class BotEquipmentModGenerator(
|
||||
)
|
||||
) ??
|
||||
false)
|
||||
{
|
||||
// Add mod to allowed list
|
||||
filteredScopesAndMods.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,9 +1645,7 @@ public class BotEquipmentModGenerator(
|
||||
if (filteredScopesAndMods is null || filteredScopesAndMods.Count() == 0)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Scope whitelist too restrictive for: {weapon.Template} {weaponDetails.Value.Name}, skipping filter");
|
||||
}
|
||||
|
||||
return scopes;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class BotGenerator(
|
||||
BotRelativeLevelDeltaMin = 0,
|
||||
BotCountToGenerate = 1,
|
||||
BotDifficulty = difficulty,
|
||||
IsPlayerScav = true,
|
||||
IsPlayerScav = true
|
||||
};
|
||||
|
||||
bot = GenerateBot(sessionId, bot, botTemplate, botGenDetails);
|
||||
@@ -128,10 +128,7 @@ public class BotGenerator(
|
||||
? 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 null)
|
||||
{
|
||||
_logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
|
||||
}
|
||||
if (botJsonTemplateClone is null) _logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
|
||||
|
||||
return GenerateBot(sessionId, preparedBotBase, botJsonTemplateClone, botGenerationDetails);
|
||||
}
|
||||
@@ -185,14 +182,12 @@ public class BotGenerator(
|
||||
|
||||
// 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,
|
||||
@@ -214,24 +209,17 @@ public class BotGenerator(
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (!(botGenerationDetails.IsPmc.GetValueOrDefault(false) || botGenerationDetails.IsPlayerScav.GetValueOrDefault(false))) bot.Hideout = null;
|
||||
|
||||
bot.Info.Experience = botLevel.Exp;
|
||||
bot.Info.Level = botLevel.Level;
|
||||
@@ -260,10 +248,7 @@ public class BotGenerator(
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (bot.Info.GameVersion == GameEditions.UNHEARD) AddAdditionalPocketLootWeightsForUnheardBot(botJsonTemplate);
|
||||
}
|
||||
|
||||
// Add drip
|
||||
@@ -281,10 +266,7 @@ public class BotGenerator(
|
||||
bot.Info.GameVersion
|
||||
);
|
||||
|
||||
if (_botConfig.BotRolesWithDogTags.Contains(botRoleLowercase))
|
||||
{
|
||||
AddDogtagToBot(bot);
|
||||
}
|
||||
if (_botConfig.BotRolesWithDogTags.Contains(botRoleLowercase)) AddDogtagToBot(bot);
|
||||
|
||||
// Generate new bot ID
|
||||
AddIdsToBot(bot, botGenerationDetails);
|
||||
@@ -293,10 +275,7 @@ public class BotGenerator(
|
||||
GenerateInventoryId(bot);
|
||||
|
||||
// Set role back to originally requested now its been generated
|
||||
if (botGenerationDetails.EventRole is not null)
|
||||
{
|
||||
bot.Info.Settings.Role = botGenerationDetails.EventRole;
|
||||
}
|
||||
if (botGenerationDetails.EventRole is not null) bot.Info.Settings.Role = botGenerationDetails.EventRole;
|
||||
|
||||
return bot;
|
||||
}
|
||||
@@ -320,13 +299,9 @@ public class BotGenerator(
|
||||
/// <returns>Experience for kill</returns>
|
||||
public double GetExperienceRewardForKillByDifficulty(Dictionary<string, MinMax> experiences, string botDifficulty, string role)
|
||||
{
|
||||
|
||||
if (!experiences.TryGetValue(botDifficulty.ToLower(), out var result))
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Unable to find experience: {botDifficulty} for {role} bot, falling back to `normal`");
|
||||
|
||||
return _randomUtil.GetDouble(experiences["normal"].Min.Value, experiences["normal"].Max.Value);
|
||||
}
|
||||
@@ -385,20 +360,16 @@ public class BotGenerator(
|
||||
);
|
||||
|
||||
if (blacklist?.Gear is null)
|
||||
{
|
||||
// Nothing to filter by
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (equipmentSlot, blacklistedTpls) in blacklist.Gear)
|
||||
{
|
||||
var equipmentDict = botJsonTemplate.BotInventory.Equipment[equipmentSlot];
|
||||
|
||||
foreach (var blacklistedTpl in blacklistedTpls)
|
||||
{
|
||||
// Set weighting to 0, will never be picked
|
||||
equipmentDict[blacklistedTpl] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,24 +401,14 @@ public class BotGenerator(
|
||||
var propValue = (Dictionary<string, double>)prop.GetValue(botInventory.Items);
|
||||
|
||||
// No container, skip
|
||||
if (propValue?.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (propValue?.Count == 0) continue;
|
||||
|
||||
List<string> tplsToRemove = [];
|
||||
foreach (var (key, _) in propValue)
|
||||
{
|
||||
if (_itemFilterService.IsLootableItemBlacklisted(key))
|
||||
{
|
||||
tplsToRemove.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var blacklistedTplToRemove in tplsToRemove)
|
||||
{
|
||||
propValue.Remove(blacklistedTplToRemove);
|
||||
}
|
||||
foreach (var blacklistedTplToRemove in tplsToRemove) propValue.Remove(blacklistedTplToRemove);
|
||||
|
||||
prop.SetValue(botInventory.Items, propValue);
|
||||
}
|
||||
@@ -483,10 +444,7 @@ public class BotGenerator(
|
||||
public void LogPmcGeneratedCount(List<BotBase> output)
|
||||
{
|
||||
var pmcCount = output.Aggregate(0, (acc, cur) => { return cur.Info.Side is "Bear" or "Usec" ? acc + 1 : acc; });
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generated {output.Count} total bots. Replaced {pmcCount} with PMCs");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Generated {output.Count} total bots. Replaced {pmcCount} with PMCs");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -503,17 +461,17 @@ public class BotGenerator(
|
||||
|
||||
BotBaseHealth health = new()
|
||||
{
|
||||
Hydration = new()
|
||||
Hydration = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)healthObj.Hydration.Min, (int)healthObj.Hydration.Max),
|
||||
Maximum = healthObj.Hydration.Max
|
||||
},
|
||||
Energy = new()
|
||||
Energy = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)healthObj.Energy.Min, (int)healthObj.Energy.Max),
|
||||
Maximum = healthObj.Energy.Max
|
||||
},
|
||||
Temperature = new()
|
||||
Temperature = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)healthObj.Temperature.Min, (int)healthObj.Temperature.Max),
|
||||
Maximum = healthObj.Temperature.Max
|
||||
@@ -523,7 +481,7 @@ public class BotGenerator(
|
||||
{
|
||||
"Head", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.Head.Min, (int)bodyParts.Head.Max),
|
||||
Maximum = Math.Round(bodyParts.Head.Max ?? 0)
|
||||
@@ -533,7 +491,7 @@ public class BotGenerator(
|
||||
{
|
||||
"Chest", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.Chest.Min, (int)bodyParts.Chest.Max),
|
||||
Maximum = Math.Round(bodyParts.Chest.Max ?? 0)
|
||||
@@ -543,7 +501,7 @@ public class BotGenerator(
|
||||
{
|
||||
"Stomach", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.Stomach.Min, (int)bodyParts.Stomach.Max),
|
||||
Maximum = Math.Round(bodyParts.Stomach.Max ?? 0)
|
||||
@@ -553,7 +511,7 @@ public class BotGenerator(
|
||||
{
|
||||
"LeftArm", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.LeftArm.Min, (int)bodyParts.LeftArm.Max),
|
||||
Maximum = Math.Round(bodyParts.LeftArm.Max ?? 0)
|
||||
@@ -563,7 +521,7 @@ public class BotGenerator(
|
||||
{
|
||||
"RightArm", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.RightArm.Min, (int)bodyParts.RightArm.Max),
|
||||
Maximum = Math.Round(bodyParts.RightArm.Max ?? 0)
|
||||
@@ -573,7 +531,7 @@ public class BotGenerator(
|
||||
{
|
||||
"LeftLeg", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.LeftLeg.Min, (int)bodyParts.LeftLeg.Max),
|
||||
Maximum = Math.Round(bodyParts.LeftLeg.Max ?? 0)
|
||||
@@ -583,7 +541,7 @@ public class BotGenerator(
|
||||
{
|
||||
"RightLeg", new BodyPartHealth
|
||||
{
|
||||
Health = new()
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = _randomUtil.GetInt((int)bodyParts.RightLeg.Min, (int)bodyParts.RightLeg.Max),
|
||||
Maximum = Math.Round(bodyParts.RightLeg.Max ?? 0)
|
||||
@@ -665,10 +623,7 @@ public class BotGenerator(
|
||||
{
|
||||
// Get skill from dict, skip if not found
|
||||
var skill = kvp.Value;
|
||||
if (skill == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (skill == null) return null;
|
||||
|
||||
// All skills have id and progress props
|
||||
var skillToAdd = new BaseSkill
|
||||
@@ -726,16 +681,10 @@ public class BotGenerator(
|
||||
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (item.ParentId == profile.Inventory.Equipment) item.ParentId = newInventoryItemId;
|
||||
}
|
||||
|
||||
// Update inventory equipment id to new one we generated
|
||||
@@ -797,10 +746,10 @@ public class BotGenerator(
|
||||
Template = GetDogtagTplByGameVersionAndSide(bot.Info.Side, bot.Info.GameVersion),
|
||||
ParentId = bot.Inventory.Equipment,
|
||||
SlotId = "Dogtag",
|
||||
Upd = new()
|
||||
Upd = new Upd
|
||||
{
|
||||
SpawnedInSession = true,
|
||||
},
|
||||
SpawnedInSession = true
|
||||
}
|
||||
};
|
||||
|
||||
bot.Inventory.Items.Add(inventoryItem);
|
||||
@@ -815,7 +764,6 @@ public class BotGenerator(
|
||||
public string GetDogtagTplByGameVersionAndSide(string side, string gameVersion)
|
||||
{
|
||||
if (side.ToLower() == "usec")
|
||||
{
|
||||
switch (gameVersion)
|
||||
{
|
||||
case GameEditions.EDGE_OF_DARKNESS:
|
||||
@@ -825,7 +773,6 @@ public class BotGenerator(
|
||||
default:
|
||||
return ItemTpl.BARTER_DOGTAG_USEC;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gameVersion)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ public class BotInventoryGenerator(
|
||||
private BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
|
||||
// Slots handled individually inside `GenerateAndAddEquipmentToBot`
|
||||
List<EquipmentSlots> _excludedEquipmentSlots =
|
||||
private List<EquipmentSlots> _excludedEquipmentSlots =
|
||||
[
|
||||
EquipmentSlots.Pockets,
|
||||
EquipmentSlots.FirstPrimaryWeapon,
|
||||
@@ -123,12 +123,12 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
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 },
|
||||
new() { Id = hideoutCustomizationStashId, Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION }
|
||||
new Item { Id = equipmentId, Template = ItemTpl.INVENTORY_DEFAULT },
|
||||
new Item { Id = stashId, Template = ItemTpl.STASH_STANDARD_STASH_10X30 },
|
||||
new Item { Id = questRaidItemsId, Template = ItemTpl.STASH_QUESTRAID },
|
||||
new Item { Id = questStashItemsId, Template = ItemTpl.STASH_QUESTOFFLINE },
|
||||
new Item { Id = sortingTableId, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE },
|
||||
new Item { Id = hideoutCustomizationStashId, Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION }
|
||||
],
|
||||
Equipment = equipmentId,
|
||||
Stash = stashId,
|
||||
@@ -138,7 +138,7 @@ public class BotInventoryGenerator(
|
||||
HideoutAreaStashes = new Dictionary<string, string>(),
|
||||
FastPanel = new Dictionary<string, string>(),
|
||||
FavoriteItems = [],
|
||||
HideoutCustomizationStashId = hideoutCustomizationStashId,
|
||||
HideoutCustomizationStashId = hideoutCustomizationStashId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -165,16 +165,12 @@ public class BotInventoryGenerator(
|
||||
raidConfig is not null &&
|
||||
_weatherHelper.IsNightTime(raidConfig.TimeVariant)
|
||||
)
|
||||
{
|
||||
foreach (var equipmentSlotKvP in (randomistionDetails.NighttimeChanges.EquipmentModsModifiers))
|
||||
{
|
||||
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);
|
||||
@@ -188,10 +184,7 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
// Skip some slots as they need to be done in a specific order + with specific parameter values
|
||||
// e.g. Weapons
|
||||
if (_excludedEquipmentSlots.Contains(equipmentSlot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_excludedEquipmentSlots.Contains(equipmentSlot)) continue;
|
||||
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
@@ -223,7 +216,7 @@ public class BotInventoryGenerator(
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE, ItemTpl.POCKETS_LARGE],
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
|
||||
@@ -238,7 +231,7 @@ public class BotInventoryGenerator(
|
||||
Inventory = botInventory,
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
|
||||
@@ -253,7 +246,7 @@ public class BotInventoryGenerator(
|
||||
Inventory = botInventory,
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
|
||||
@@ -268,7 +261,7 @@ public class BotInventoryGenerator(
|
||||
Inventory = botInventory,
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
|
||||
@@ -283,29 +276,22 @@ public class BotInventoryGenerator(
|
||||
Inventory = botInventory,
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (botEquipConfig.ForceRigWhenNoVest.GetValueOrDefault(false) && !hasArmorVest) wornItemChances.EquipmentChances["TacticalVest"] = 100;
|
||||
|
||||
GenerateEquipment(
|
||||
new GenerateEquipmentProperties
|
||||
@@ -318,7 +304,7 @@ public class BotInventoryGenerator(
|
||||
Inventory = botInventory,
|
||||
BotEquipmentConfig = botEquipConfig,
|
||||
RandomisationDetails = randomistionDetails,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1,
|
||||
GeneratingPlayerLevel = pmcProfile?.Info?.Level ?? 1
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -343,10 +329,7 @@ public class BotInventoryGenerator(
|
||||
|
||||
if (!tacVestsWithArmor.Any())
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to filter to only armored rigs as bot: {botRole} has none in pool");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Unable to filter to only armored rigs as bot: {botRole} has none in pool");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -369,10 +352,7 @@ public class BotInventoryGenerator(
|
||||
|
||||
if (!allowEmptyResult && !tacVestsWithoutArmor.Any())
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -416,10 +396,7 @@ public class BotInventoryGenerator(
|
||||
var attempts = 0;
|
||||
while (!found)
|
||||
{
|
||||
if (!settings.RootEquipmentPool.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!settings.RootEquipmentPool.Any()) return false;
|
||||
|
||||
var chosenItemTpl = _weightedRandomHelper.GetWeightedValue(settings.RootEquipmentPool);
|
||||
var dbResult = _itemHelper.GetItem(chosenItemTpl);
|
||||
@@ -427,10 +404,7 @@ public class BotInventoryGenerator(
|
||||
if (!dbResult.Key)
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("bot-missing_item_template", chosenItemTpl));
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"EquipmentSlot-> {settings.RootEquipmentSlot}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"EquipmentSlot-> {settings.RootEquipmentSlot}");
|
||||
|
||||
// Remove picked item
|
||||
settings.RootEquipmentPool.Remove(chosenItemTpl);
|
||||
@@ -449,10 +423,7 @@ public class BotInventoryGenerator(
|
||||
if (compatibilityResult.Incompatible ?? false)
|
||||
{
|
||||
// Tried x different items that failed, stop
|
||||
if (attempts > maxAttempts)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (attempts > maxAttempts) return false;
|
||||
|
||||
// Remove picked item from pool
|
||||
settings.RootEquipmentPool.Remove(chosenItemTpl);
|
||||
@@ -488,17 +459,14 @@ public class BotInventoryGenerator(
|
||||
if (_botConfig.Equipment.ContainsKey(settings.BotData.EquipmentRole) &&
|
||||
settings.RandomisationDetails?.RandomisedArmorSlots != null &&
|
||||
settings.RandomisationDetails.RandomisedArmorSlots.Contains(settings.RootEquipmentSlot.ToString()))
|
||||
{
|
||||
// Filter out mods from relevant blacklist
|
||||
settings.ModPool[pickedItemDb.Id] = GetFilteredDynamicModsForItem(
|
||||
pickedItemDb.Id,
|
||||
botEquipBlacklist.Equipment
|
||||
);
|
||||
}
|
||||
var itemIsOnGenerateModBlacklist = settings.GenerateModsBlacklist != null && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id);
|
||||
// Does item have slots for sub-mods to be inserted into
|
||||
if (pickedItemDb.Properties?.Slots?.Count > 0
|
||||
&& !itemIsOnGenerateModBlacklist)
|
||||
if (pickedItemDb.Properties?.Slots?.Count > 0 && !itemIsOnGenerateModBlacklist)
|
||||
{
|
||||
var childItemsToAdd = _botEquipmentModGenerator.GenerateModsForEquipment(
|
||||
[item],
|
||||
@@ -533,13 +501,11 @@ public class BotInventoryGenerator(
|
||||
foreach (var modSlot in modPool)
|
||||
{
|
||||
// Get blacklist
|
||||
if (!equipmentBlacklist.TryGetValue(modSlot.Key, out var blacklistedMods))
|
||||
{
|
||||
blacklistedMods = [];
|
||||
};
|
||||
if (!equipmentBlacklist.TryGetValue(modSlot.Key, out var blacklistedMods)) blacklistedMods = [];
|
||||
;
|
||||
|
||||
// Get mods not on blacklist
|
||||
var filteredMods = modPool[modSlot.Key].Where((slotName) => !(blacklistedMods).Contains(slotName));
|
||||
var filteredMods = modPool[modSlot.Key].Where((slotName) => !blacklistedMods.Contains(slotName));
|
||||
if (!filteredMods.Any())
|
||||
{
|
||||
_logger.Warning($"Filtering {modSlot.Key} pool resulting in 0 items, skipping filter");
|
||||
@@ -568,10 +534,8 @@ public class BotInventoryGenerator(
|
||||
{
|
||||
var weaponSlotsToFill = GetDesiredWeaponsForBot(equipmentChances);
|
||||
foreach (var desiredWeapons in weaponSlotsToFill)
|
||||
{
|
||||
// Add weapon to bot if true and bot json has something to put into the slot
|
||||
if (desiredWeapons.ShouldSpawn && templateInventory.Equipment[desiredWeapons.Slot].Any())
|
||||
{
|
||||
AddWeaponAndMagazinesToInventory(
|
||||
sessionId,
|
||||
desiredWeapons,
|
||||
@@ -583,8 +547,6 @@ public class BotInventoryGenerator(
|
||||
itemGenerationLimitsMinMax,
|
||||
botLevel
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -597,16 +559,16 @@ public class BotInventoryGenerator(
|
||||
var shouldSpawnPrimary = _randomUtil.GetChance100(equipmentChances.EquipmentChances["FirstPrimaryWeapon"]);
|
||||
return
|
||||
[
|
||||
new()
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.FirstPrimaryWeapon, ShouldSpawn = shouldSpawnPrimary
|
||||
},
|
||||
new()
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.SecondPrimaryWeapon,
|
||||
ShouldSpawn = shouldSpawnPrimary && _randomUtil.GetChance100(equipmentChances.EquipmentChances["SecondPrimaryWeapon"])
|
||||
},
|
||||
new()
|
||||
new DesiredWeapons
|
||||
{
|
||||
Slot = EquipmentSlots.Holster,
|
||||
ShouldSpawn = !shouldSpawnPrimary || _randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]) // No primary = force pistol
|
||||
|
||||
@@ -27,30 +27,21 @@ public class BotLevelGenerator(
|
||||
/// <returns>IRandomisedBotLevelResult object</returns>
|
||||
public RandomisedBotLevelResult GenerateBotLevel(MinMax levelDetails, BotGenerationDetails botGenerationDetails, BotBase bot)
|
||||
{
|
||||
if (!botGenerationDetails.IsPmc.GetValueOrDefault(false))
|
||||
{
|
||||
return new RandomisedBotLevelResult { Exp = 0, Level = 1 };
|
||||
}
|
||||
if (!botGenerationDetails.IsPmc.GetValueOrDefault(false)) return new RandomisedBotLevelResult { Exp = 0, Level = 1 };
|
||||
|
||||
var expTable = _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable;
|
||||
var botLevelRange = GetRelativePmcBotLevelRange(botGenerationDetails, levelDetails, expTable.Length);
|
||||
|
||||
// Get random level based on the exp table.
|
||||
int exp = 0;
|
||||
var 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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (level < expTable.Length - 1) exp += _randomUtil.GetInt(0, expTable[level].Experience.Value - 1);
|
||||
|
||||
return new RandomisedBotLevelResult { Level = level, Exp = exp };
|
||||
}
|
||||
|
||||
@@ -47,10 +47,7 @@ public class BotLootGenerator(
|
||||
// Clone limits and set all values to 0 to use as a running total
|
||||
var limitsForBotDict = _cloner.Clone(limits);
|
||||
// Init current count of items we want to limit
|
||||
foreach (var limit in limitsForBotDict)
|
||||
{
|
||||
limitsForBotDict[limit.Key] = 0;
|
||||
}
|
||||
foreach (var limit in limitsForBotDict) limitsForBotDict[limit.Key] = 0;
|
||||
|
||||
return new ItemSpawnLimitSettings
|
||||
{
|
||||
@@ -113,10 +110,7 @@ public class BotLootGenerator(
|
||||
}
|
||||
|
||||
// Forced pmc healing loot into secure container
|
||||
if (isPmc && _pmcConfig.ForceHealingItemsIntoSecure)
|
||||
{
|
||||
AddForcedMedicalItemsToPmcSecure(botInventory, botRole);
|
||||
}
|
||||
if (isPmc && _pmcConfig.ForceHealingItemsIntoSecure) AddForcedMedicalItemsToPmcSecure(botInventory, botRole);
|
||||
|
||||
var botItemLimits = GetItemSpawnLimitsForBot(botRole);
|
||||
|
||||
@@ -235,7 +229,6 @@ public class BotLootGenerator(
|
||||
{
|
||||
// Add randomly generated weapon to PMC backpacks
|
||||
if (isPmc && _randomUtil.GetChance100(_pmcConfig.LooseWeaponInBackpackChancePercent))
|
||||
{
|
||||
AddLooseWeaponsToInventorySlot(
|
||||
sessionId,
|
||||
botInventory,
|
||||
@@ -247,7 +240,6 @@ public class BotLootGenerator(
|
||||
botLevel,
|
||||
filledContainerIds
|
||||
);
|
||||
}
|
||||
|
||||
var backpackLootRoubleTotal = GetBackpackRoubleTotalByLevel(botLevel, isPmc);
|
||||
AddLootFromPool(
|
||||
@@ -271,7 +263,6 @@ public class BotLootGenerator(
|
||||
|
||||
// TacticalVest - generate loot if they have one
|
||||
if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest))
|
||||
{
|
||||
// Vest
|
||||
AddLootFromPool(
|
||||
_botLootCacheService.GetLootFromCache(
|
||||
@@ -290,7 +281,6 @@ public class BotLootGenerator(
|
||||
isPmc,
|
||||
filledContainerIds
|
||||
);
|
||||
}
|
||||
|
||||
// Pockets
|
||||
AddLootFromPool(
|
||||
@@ -315,7 +305,6 @@ public class BotLootGenerator(
|
||||
|
||||
// only add if not a pmc or is pmc and flag is true
|
||||
if (!isPmc || (isPmc && _pmcConfig.AddSecureContainerLootFromBotConfig))
|
||||
{
|
||||
AddLootFromPool(
|
||||
_botLootCacheService.GetLootFromCache(botRole, isPmc, LootCacheType.Secure, botJsonTemplate),
|
||||
[EquipmentSlots.SecuredContainer],
|
||||
@@ -327,7 +316,6 @@ public class BotLootGenerator(
|
||||
isPmc,
|
||||
filledContainerIds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private MinMaxLootItemValue? GetSingleItemLootPriceLimits(int botLevel, bool isPmc)
|
||||
@@ -368,15 +356,9 @@ public class BotLootGenerator(
|
||||
{
|
||||
List<EquipmentSlots> result = [EquipmentSlots.Pockets];
|
||||
|
||||
if ((botInventory.Items ?? []).Any((item) => item.SlotId == EquipmentSlots.TacticalVest.ToString()))
|
||||
{
|
||||
result.Add(EquipmentSlots.TacticalVest);
|
||||
}
|
||||
if ((botInventory.Items ?? []).Any((item) => item.SlotId == EquipmentSlots.TacticalVest.ToString())) result.Add(EquipmentSlots.TacticalVest);
|
||||
|
||||
if ((botInventory.Items ?? []).Any((item) => item.SlotId == EquipmentSlots.Backpack.ToString()))
|
||||
{
|
||||
result.Add(EquipmentSlots.Backpack);
|
||||
}
|
||||
if ((botInventory.Items ?? []).Any((item) => item.SlotId == EquipmentSlots.Backpack.ToString())) result.Add(EquipmentSlots.Backpack);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -449,10 +431,7 @@ public class BotLootGenerator(
|
||||
for (var i = 0; i < totalItemCount; i++)
|
||||
{
|
||||
// Pool can become empty if item spawn limits keep removing items
|
||||
if (pool.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (pool.Count == 0) return;
|
||||
|
||||
var weightedItemTpl = _weightedRandomHelper.GetWeightedValue(pool);
|
||||
var (key, itemToAddTemplate) = _itemHelper.GetItem(weightedItemTpl);
|
||||
@@ -481,7 +460,7 @@ public class BotLootGenerator(
|
||||
Id = newRootItemId,
|
||||
Template = itemToAddTemplate?.Id ?? string.Empty,
|
||||
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(itemToAddTemplate, botRole)
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
// Is Simple-Wallet / WZ wallet
|
||||
@@ -505,14 +484,12 @@ public class BotLootGenerator(
|
||||
{
|
||||
// Add each currency to wallet
|
||||
foreach (var itemToAdd in itemsToAdd)
|
||||
{
|
||||
_inventoryHelper.PlaceItemInContainer(
|
||||
containerGrid,
|
||||
itemToAdd,
|
||||
itemWithChildrenToAdd[0].Id,
|
||||
"main"
|
||||
);
|
||||
}
|
||||
|
||||
itemWithChildrenToAdd.AddRange(itemsToAdd.SelectMany(x => x));
|
||||
}
|
||||
@@ -538,24 +515,20 @@ public class BotLootGenerator(
|
||||
if (itemAddedResult == ItemAddedResult.NO_CONTAINERS)
|
||||
{
|
||||
// Bot has no container to put item in, exit
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Unable to add: {totalItemCount} items to bot as it lacks a container to include them");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fitItemIntoContainerAttempts++;
|
||||
if (fitItemIntoContainerAttempts >= 4)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Failed placing item: {itemToAddTemplate.Id} - {itemToAddTemplate.Name}: {i} of: {totalItemCount} items into: {botRole} " +
|
||||
$"containers: {string.Join(",", equipmentSlots)}. Tried: {fitItemIntoContainerAttempts} " +
|
||||
$"times, reason: {itemAddedResult}, skipping"
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -571,10 +544,7 @@ public class BotLootGenerator(
|
||||
if (totalValueLimitRub > 0)
|
||||
{
|
||||
currentTotalRub += _handbookHelper.GetTemplatePrice(itemToAddTemplate.Id);
|
||||
if (currentTotalRub > totalValueLimitRub)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (currentTotalRub > totalValueLimitRub) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,12 +569,12 @@ public class BotLootGenerator(
|
||||
var chosenStackCount = _weightedRandomHelper.GetWeightedValue<string>(_botConfig.WalletLoot.StackSizeWeight);
|
||||
List<Item> items =
|
||||
[
|
||||
new Item
|
||||
new()
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = _weightedRandomHelper.GetWeightedValue<string>(_botConfig.WalletLoot.CurrencyWeight),
|
||||
ParentId = walletId,
|
||||
Upd = new()
|
||||
Upd = new Upd
|
||||
{
|
||||
StackObjectsCount = int.Parse(chosenStackCount)
|
||||
}
|
||||
@@ -627,24 +597,15 @@ public class BotLootGenerator(
|
||||
{
|
||||
// Fill ammo box
|
||||
if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
_itemHelper.AddCartridgesToAmmoBox(itemToAddChildrenTo, itemToAddTemplate);
|
||||
}
|
||||
// Make money a stack
|
||||
else if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.MONEY))
|
||||
{
|
||||
RandomiseMoneyStackSize(botRole, itemToAddTemplate, itemToAddChildrenTo[0]);
|
||||
}
|
||||
// Make ammo a stack
|
||||
else if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO))
|
||||
{
|
||||
RandomiseAmmoStackSize(isPmc, itemToAddTemplate, itemToAddChildrenTo[0]);
|
||||
}
|
||||
// Must add soft inserts/plates
|
||||
else if (_itemHelper.ItemRequiresSoftInserts(itemToAddTemplate.Id))
|
||||
{
|
||||
_itemHelper.AddChildSlotItems(itemToAddChildrenTo, itemToAddTemplate, null, false);
|
||||
}
|
||||
else if (_itemHelper.ItemRequiresSoftInserts(itemToAddTemplate.Id)) _itemHelper.AddChildSlotItems(itemToAddChildrenTo, itemToAddTemplate, null, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -682,10 +643,7 @@ public class BotLootGenerator(
|
||||
(int)_pmcConfig.LooseWeaponInBackpackLootMinMax.Max
|
||||
);
|
||||
|
||||
if (randomisedWeaponCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (randomisedWeaponCount <= 0) return;
|
||||
|
||||
for (var i = 0; i < randomisedWeaponCount; i++)
|
||||
{
|
||||
@@ -709,12 +667,8 @@ public class BotLootGenerator(
|
||||
);
|
||||
|
||||
if (result != ItemAddedResult.SUCCESS)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Failed to add additional weapon {generatedWeapon.Weapon[0].Id} to bot backpack, reason: {result.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,33 +683,24 @@ public class BotLootGenerator(
|
||||
{
|
||||
// PMCs and scavs have different sections of bot config for spawn limits
|
||||
if (itemSpawnLimits is not null && itemSpawnLimits.GlobalLimits?.Count == 0)
|
||||
{
|
||||
// No items found in spawn limit, drop out
|
||||
return false;
|
||||
}
|
||||
|
||||
// No spawn limits, skipping
|
||||
if (itemSpawnLimits is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (itemSpawnLimits is null) return false;
|
||||
|
||||
var idToCheckFor = GetMatchingIdFromSpawnLimits(itemTemplate, itemSpawnLimits.GlobalLimits);
|
||||
if (idToCheckFor is null)
|
||||
{
|
||||
// ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Use tryAdd to see if it exists, and automatically add 1
|
||||
if (!itemSpawnLimits.CurrentLimits.TryAdd(idToCheckFor, 1))
|
||||
{
|
||||
// if it does exist, come in here and increment
|
||||
// Increment item count with this bot type
|
||||
itemSpawnLimits.CurrentLimits[idToCheckFor]++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check if over limit
|
||||
var currentLimitCount = itemSpawnLimits.CurrentLimits[idToCheckFor];
|
||||
@@ -764,8 +709,7 @@ public class BotLootGenerator(
|
||||
// Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
|
||||
if (currentLimitCount > currentLimitCount * 10)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
_localisationService.GetText(
|
||||
"bot-item_spawn_limit_reached_skipping_item",
|
||||
@@ -777,7 +721,6 @@ public class BotLootGenerator(
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -797,10 +740,7 @@ public class BotLootGenerator(
|
||||
public void RandomiseMoneyStackSize(string botRole, TemplateItem itemTemplate, Item moneyItem)
|
||||
{
|
||||
// Get all currency weights for this bot type
|
||||
if (!_botConfig.CurrencyStackSize.TryGetValue(botRole, out var currencyWeights))
|
||||
{
|
||||
currencyWeights = _botConfig.CurrencyStackSize["default"];
|
||||
}
|
||||
if (!_botConfig.CurrencyStackSize.TryGetValue(botRole, out var currencyWeights)) currencyWeights = _botConfig.CurrencyStackSize["default"];
|
||||
|
||||
var currencyWeight = currencyWeights[moneyItem.Template];
|
||||
|
||||
@@ -831,19 +771,13 @@ public class BotLootGenerator(
|
||||
/// <returns>Dictionary of tplIds and limit</returns>
|
||||
public Dictionary<string, double> GetItemSpawnLimitsForBotType(string botRole)
|
||||
{
|
||||
if (_botHelper.IsBotPmc(botRole))
|
||||
{
|
||||
return _botConfig.ItemSpawnLimits["pmc"];
|
||||
}
|
||||
if (_botHelper.IsBotPmc(botRole)) return _botConfig.ItemSpawnLimits["pmc"];
|
||||
|
||||
if (_botConfig.ItemSpawnLimits.ContainsKey(botRole.ToLower()))
|
||||
{
|
||||
return _botConfig.ItemSpawnLimits[botRole.ToLower()];
|
||||
}
|
||||
if (_botConfig.ItemSpawnLimits.ContainsKey(botRole.ToLower())) return _botConfig.ItemSpawnLimits[botRole.ToLower()];
|
||||
|
||||
_logger.Warning(_localisationService.GetText("bot-unable_to_find_spawn_limits_fallback_to_defaults", botRole));
|
||||
|
||||
return new();
|
||||
return new Dictionary<string, double>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -854,16 +788,10 @@ public class BotLootGenerator(
|
||||
/// <returns>id as string, otherwise undefined</returns>
|
||||
public string? GetMatchingIdFromSpawnLimits(TemplateItem itemTemplate, Dictionary<string, double> spawnLimits)
|
||||
{
|
||||
if (spawnLimits.ContainsKey(itemTemplate.Id))
|
||||
{
|
||||
return itemTemplate.Id;
|
||||
}
|
||||
if (spawnLimits.ContainsKey(itemTemplate.Id)) return itemTemplate.Id;
|
||||
|
||||
// tplId not found in spawnLimits, check if parentId is
|
||||
if (spawnLimits.ContainsKey(itemTemplate.Parent))
|
||||
{
|
||||
return itemTemplate.Parent;
|
||||
}
|
||||
if (spawnLimits.ContainsKey(itemTemplate.Parent)) return itemTemplate.Parent;
|
||||
|
||||
// parentId and tplId not found
|
||||
return null;
|
||||
|
||||
@@ -34,11 +34,11 @@ public class BotWeaponGenerator(
|
||||
IEnumerable<IInventoryMagGen> inventoryMagGenComponents
|
||||
)
|
||||
{
|
||||
protected IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(inventoryMagGenComponents);
|
||||
protected const string _modMagazineSlotId = "mod_magazine";
|
||||
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
protected IEnumerable<IInventoryMagGen> _inventoryMagGenComponents = MagGenSetUp(inventoryMagGenComponents);
|
||||
protected PmcConfig _pmcConfig = _configServer.GetConfig<PmcConfig>();
|
||||
protected RepairConfig _repairConfig = _configServer.GetConfig<RepairConfig>();
|
||||
protected const string _modMagazineSlotId = "mod_magazine";
|
||||
|
||||
private static List<IInventoryMagGen> MagGenSetUp(IEnumerable<IInventoryMagGen> components)
|
||||
{
|
||||
@@ -139,10 +139,8 @@ public class BotWeaponGenerator(
|
||||
|
||||
// Chance to add randomised weapon enhancement
|
||||
if (isPmc && _randomUtil.GetChance100(_pmcConfig.WeaponHasEnhancementChancePercent))
|
||||
{
|
||||
// Add buff to weapon root
|
||||
_repairService.AddBuff(_repairConfig.RepairKit.Weapon, weaponWithModsArray[0]);
|
||||
}
|
||||
|
||||
// Add mods to weapon base
|
||||
if (modPool.Keys.Contains(weaponTpl))
|
||||
@@ -161,10 +159,10 @@ public class BotWeaponGenerator(
|
||||
ParentTemplate = weaponItemTemplate,
|
||||
ModSpawnChances = modChances,
|
||||
AmmoTpl = ammoTpl,
|
||||
BotData = new() { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
||||
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
|
||||
ModLimits = modLimits,
|
||||
WeaponStats = new(),
|
||||
ConflictingItemTpls = new(),
|
||||
WeaponStats = new WeaponStats(),
|
||||
ConflictingItemTpls = new HashSet<string>()
|
||||
};
|
||||
weaponWithModsArray = _botEquipmentModGenerator.GenerateModsForWeapon(
|
||||
sessionId,
|
||||
@@ -174,7 +172,6 @@ public class BotWeaponGenerator(
|
||||
|
||||
// Use weapon preset from globals.json if weapon isn't valid
|
||||
if (!IsWeaponValid(weaponWithModsArray, botRole))
|
||||
{
|
||||
// Weapon is bad, fall back to weapons preset
|
||||
weaponWithModsArray = GetPresetWeaponMods(
|
||||
weaponTpl,
|
||||
@@ -183,19 +180,15 @@ public class BotWeaponGenerator(
|
||||
weaponItemTemplate,
|
||||
botRole
|
||||
);
|
||||
}
|
||||
|
||||
var tempList = _cloner.Clone(weaponWithModsArray.Where((item) => item.SlotId == _modMagazineSlotId));
|
||||
// Fill existing magazines to full and sync ammo type
|
||||
foreach (var magazine in tempList)
|
||||
{
|
||||
FillExistingMagazines(weaponWithModsArray, magazine, ammoTpl);
|
||||
}
|
||||
foreach (var magazine in tempList) FillExistingMagazines(weaponWithModsArray, magazine, ammoTpl);
|
||||
|
||||
// Add cartridge(s) to gun chamber(s)
|
||||
if (weaponItemTemplate.Properties.Chambers?.Any() ??
|
||||
false &&
|
||||
weaponItemTemplate.Properties.Chambers[0].Props.Filters[0].Filter.Contains(ammoTpl))
|
||||
(false &&
|
||||
weaponItemTemplate.Properties.Chambers[0].Props.Filters[0].Filter.Contains(ammoTpl)))
|
||||
{
|
||||
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
|
||||
var chamberSlotNames = weaponItemTemplate.Properties.Chambers.Select((chamberSlot) => chamberSlot.Name);
|
||||
@@ -212,13 +205,13 @@ public class BotWeaponGenerator(
|
||||
FillUbgl(weaponWithModsArray, ubglMod, ubglAmmoTpl);
|
||||
}
|
||||
|
||||
return new()
|
||||
return new GenerateWeaponResult
|
||||
{
|
||||
Weapon = weaponWithModsArray,
|
||||
ChosenAmmoTemplate = ammoTpl,
|
||||
ChosenUbglAmmoTemplate = ubglAmmoTpl,
|
||||
WeaponMods = modPool,
|
||||
WeaponTemplate = weaponItemTemplate,
|
||||
WeaponTemplate = weaponItemTemplate
|
||||
};
|
||||
}
|
||||
|
||||
@@ -238,13 +231,13 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
// Not found, add new slot to weapon
|
||||
weaponWithModsList.Add(
|
||||
new()
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = ammoTemplate,
|
||||
ParentId = weaponWithModsList[0].Id,
|
||||
SlotId = slotId,
|
||||
Upd = new() { StackObjectsCount = 1 },
|
||||
Upd = new Upd { StackObjectsCount = 1 }
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -252,7 +245,7 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
// Already exists, update values
|
||||
existingItemWithSlot.Template = ammoTemplate;
|
||||
existingItemWithSlot.Upd = new() { StackObjectsCount = 1 };
|
||||
existingItemWithSlot.Upd = new Upd { StackObjectsCount = 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +265,7 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
return
|
||||
[
|
||||
new()
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = weaponTemplate,
|
||||
@@ -301,14 +294,12 @@ public class BotWeaponGenerator(
|
||||
// TODO: Preset weapons trigger a lot of warnings regarding missing ammo in magazines & such
|
||||
Preset preset = null;
|
||||
foreach (var (_, itemPreset) in _databaseService.GetGlobals().ItemPresets)
|
||||
{
|
||||
if (itemPreset.Items[0].Template == weaponTemplate)
|
||||
{
|
||||
preset = _cloner.Clone(itemPreset);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (preset is not null)
|
||||
{
|
||||
@@ -338,10 +329,7 @@ public class BotWeaponGenerator(
|
||||
foreach (var mod in weaponItemList)
|
||||
{
|
||||
var modTemplate = _itemHelper.GetItem(mod.Template).Value;
|
||||
if (!modTemplate.Properties.Slots?.Any() ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!modTemplate.Properties.Slots?.Any() ?? false) continue;
|
||||
|
||||
// Iterate over required slots in db item, check mod exists for that slot
|
||||
foreach (var modSlotTemplate in modTemplate.Properties.Slots?.Where((slot) => slot.Required.GetValueOrDefault(false)) ?? [])
|
||||
@@ -360,7 +348,7 @@ public class BotWeaponGenerator(
|
||||
modSlot = modSlotTemplate.Name,
|
||||
modName = modTemplate.Name,
|
||||
slotId = mod.SlotId,
|
||||
botRole = botRole,
|
||||
botRole = botRole
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -394,6 +382,7 @@ public class BotWeaponGenerator(
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//var isInternalMag = magTemplate.Properties.ReloadMagType == ReloadMode.InternalMagazine;
|
||||
var ammoTemplate = _itemHelper.GetItem(generatedWeaponResult.ChosenAmmoTemplate);
|
||||
if (!ammoTemplate.Key)
|
||||
@@ -406,10 +395,7 @@ public class BotWeaponGenerator(
|
||||
}
|
||||
|
||||
// Has an UBGL
|
||||
if (generatedWeaponResult.ChosenUbglAmmoTemplate is not null)
|
||||
{
|
||||
AddUbglGrenadesToBotInventory(weaponAndMods, generatedWeaponResult, inventory);
|
||||
}
|
||||
if (generatedWeaponResult.ChosenUbglAmmoTemplate is not null) AddUbglGrenadesToBotInventory(weaponAndMods, generatedWeaponResult, inventory);
|
||||
|
||||
var inventoryMagGenModel = new InventoryMagGen(
|
||||
magWeights,
|
||||
@@ -446,8 +432,8 @@ public class BotWeaponGenerator(
|
||||
// Define min/max of how many grenades bot will have
|
||||
GenerationData ubglMinMax = new()
|
||||
{
|
||||
Weights = new() { { 1, 1 }, { 2, 1 } },
|
||||
Whitelist = new(),
|
||||
Weights = new Dictionary<double, double> { { 1, 1 }, { 2, 1 } },
|
||||
Whitelist = new Dictionary<string, double>()
|
||||
};
|
||||
|
||||
// get ammo template from db
|
||||
@@ -482,12 +468,12 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
var id = _hashUtil.Generate();
|
||||
_botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
|
||||
new() { EquipmentSlots.SecuredContainer },
|
||||
new List<EquipmentSlots> { EquipmentSlots.SecuredContainer },
|
||||
id,
|
||||
ammoTemplate,
|
||||
new()
|
||||
new List<Item>
|
||||
{
|
||||
new() { Id = id, Template = ammoTemplate, Upd = new() { StackObjectsCount = stackSize } }
|
||||
new() { Id = id, Template = ammoTemplate, Upd = new Upd { StackObjectsCount = stackSize } }
|
||||
},
|
||||
inventory
|
||||
);
|
||||
@@ -508,14 +494,10 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
// Edge case - magazineless chamber loaded weapons dont have magazines, e.g. mp18
|
||||
// return default mag tpl
|
||||
if (weaponTemplate.Properties.ReloadMode == ReloadMode.OnlyBarrel)
|
||||
{
|
||||
return _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weaponTemplate);
|
||||
}
|
||||
if (weaponTemplate.Properties.ReloadMode == ReloadMode.OnlyBarrel) return _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weaponTemplate);
|
||||
|
||||
// log error if no magazine AND not a chamber loaded weapon (e.g. shotgun revolver)
|
||||
if (!weaponTemplate.Properties.IsChamberLoad ?? false)
|
||||
{
|
||||
// Shouldn't happen
|
||||
_logger.Warning(
|
||||
_localisationService.GetText(
|
||||
@@ -523,19 +505,16 @@ public class BotWeaponGenerator(
|
||||
new
|
||||
{
|
||||
weaponId = weaponTemplate.Id,
|
||||
botRole = botRole,
|
||||
botRole = botRole
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var defaultMagTplId = _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weaponTemplate);
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"[{botRole}] Unable to find magazine for weapon: {weaponTemplate.Id} {weaponTemplate.Name}, using mag template default: {defaultMagTplId}."
|
||||
);
|
||||
}
|
||||
|
||||
return defaultMagTplId;
|
||||
}
|
||||
@@ -554,8 +533,7 @@ public class BotWeaponGenerator(
|
||||
var desiredCaliber = GetWeaponCaliber(weaponTemplate);
|
||||
if (!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon) || cartridgePoolForWeapon?.Keys.Count == 0)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
_localisationService.GetText(
|
||||
"bot-no_caliber_data_for_weapon_falling_back_to_default",
|
||||
@@ -563,11 +541,10 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
weaponId = weaponTemplate.Id,
|
||||
weaponName = weaponTemplate.Name,
|
||||
defaultAmmo = weaponTemplate.Properties.DefAmmo,
|
||||
defaultAmmo = weaponTemplate.Properties.DefAmmo
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Immediately returns, default ammo is guaranteed to be compatible
|
||||
return weaponTemplate.Properties.DefAmmo;
|
||||
@@ -576,26 +553,18 @@ public class BotWeaponGenerator(
|
||||
// Get cartridges the weapons first chamber allow
|
||||
var compatibleCartridgesInTemplate = GetCompatibleCartridgesFromWeaponTemplate(weaponTemplate);
|
||||
if (compatibleCartridgesInTemplate is null)
|
||||
{
|
||||
// No chamber data found in weapon, send default
|
||||
return weaponTemplate.Properties.DefAmmo;
|
||||
}
|
||||
|
||||
// Inner join the weapons allowed + passed in cartridge pool to get compatible cartridges
|
||||
Dictionary<string, double> compatibleCartridges = new();
|
||||
foreach (var cartridge in cartridgePoolForWeapon)
|
||||
{
|
||||
if (compatibleCartridgesInTemplate.Contains(cartridge.Key))
|
||||
{
|
||||
compatibleCartridges[cartridge.Key] = cartridgePoolForWeapon[cartridge.Key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!compatibleCartridges.Any())
|
||||
{
|
||||
// No compatible cartridges, use default
|
||||
return weaponTemplate.Properties.DefAmmo;
|
||||
}
|
||||
|
||||
return _weightedRandomHelper.GetWeightedValue<string>(compatibleCartridges);
|
||||
}
|
||||
@@ -608,10 +577,7 @@ public class BotWeaponGenerator(
|
||||
protected List<string>? GetCompatibleCartridgesFromWeaponTemplate(TemplateItem weaponTemplate)
|
||||
{
|
||||
var cartridges = weaponTemplate.Properties?.Chambers.FirstOrDefault()?.Props?.Filters?[0].Filter;
|
||||
if (cartridges is not null)
|
||||
{
|
||||
return cartridges;
|
||||
}
|
||||
if (cartridges is not null) return cartridges;
|
||||
// Fallback to the magazine if possible, e.g. for revolvers
|
||||
// Grab the magazines template
|
||||
var firstMagazine = weaponTemplate.Properties.Slots.FirstOrDefault(slot => slot.Name == "mod_magazine");
|
||||
@@ -621,11 +587,9 @@ public class BotWeaponGenerator(
|
||||
// Get the first slots array of cartridges
|
||||
cartridges = magProperties.Slots.FirstOrDefault()?.Props.Filters?[0].Filter;
|
||||
if (cartridges is null)
|
||||
{
|
||||
// Normal magazines
|
||||
// None found, try the cartridges array
|
||||
cartridges = magProperties.Cartridges.FirstOrDefault()?.Props.Filters[0].Filter;
|
||||
}
|
||||
|
||||
return cartridges;
|
||||
}
|
||||
@@ -637,28 +601,20 @@ public class BotWeaponGenerator(
|
||||
/// <returns>Caliber as string</returns>
|
||||
protected string? GetWeaponCaliber(TemplateItem weaponTemplate)
|
||||
{
|
||||
if (weaponTemplate.Properties.Caliber is not null)
|
||||
{
|
||||
return weaponTemplate.Properties.Caliber;
|
||||
}
|
||||
if (weaponTemplate.Properties.Caliber is not null) return weaponTemplate.Properties.Caliber;
|
||||
|
||||
if (weaponTemplate.Properties.AmmoCaliber is not null)
|
||||
{
|
||||
// 9x18pmm has a typo, should be Caliber9x18PM
|
||||
return weaponTemplate.Properties.AmmoCaliber == "Caliber9x18PMM"
|
||||
? "Caliber9x18PM"
|
||||
: weaponTemplate.Properties.AmmoCaliber;
|
||||
}
|
||||
|
||||
if (weaponTemplate.Properties.LinkedWeapon is not null)
|
||||
{
|
||||
var ammoInChamber = _itemHelper.GetItem(
|
||||
weaponTemplate.Properties.Chambers[0].Props.Filters[0].Filter[0]
|
||||
);
|
||||
if (!ammoInChamber.Key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!ammoInChamber.Key) return null;
|
||||
|
||||
return ammoInChamber.Value.Properties.Caliber;
|
||||
}
|
||||
@@ -689,13 +645,9 @@ public class BotWeaponGenerator(
|
||||
// Exchange of the camora ammo is not necessary we could also just check for stackSize > 0 here
|
||||
// and remove the else
|
||||
if (_botWeaponGeneratorHelper.MagazineIsCylinderRelated(parentItem.Name))
|
||||
{
|
||||
FillCamorasWithAmmo(weaponMods, magazine.Id, cartridgeTemplate);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddOrUpdateMagazinesChildWithAmmo(weaponMods, magazine, cartridgeTemplate, magazineTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -707,13 +659,13 @@ public class BotWeaponGenerator(
|
||||
protected void FillUbgl(List<Item> weaponMods, Item ubglMod, string ubglAmmoTpl)
|
||||
{
|
||||
weaponMods.Add(
|
||||
new()
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = ubglAmmoTpl,
|
||||
ParentId = ubglMod.Id,
|
||||
SlotId = "patron_in_weapon",
|
||||
Upd = new() { StackObjectsCount = 1 },
|
||||
Upd = new Upd { StackObjectsCount = 1 }
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -731,10 +683,8 @@ public class BotWeaponGenerator(
|
||||
(m) => m.ParentId == magazine.Id && m.SlotId == "cartridges"
|
||||
);
|
||||
if (magazineCartridgeChildItem is not null)
|
||||
{
|
||||
// Delete the existing cartridge object and create fresh below
|
||||
weaponWithMods.Remove(magazineCartridgeChildItem);
|
||||
}
|
||||
|
||||
// Create array with just magazine
|
||||
List<Item> magazineWithCartridges = [magazine];
|
||||
@@ -764,13 +714,9 @@ public class BotWeaponGenerator(
|
||||
{
|
||||
camora.Template = ammoTpl;
|
||||
if (camora.Upd is not null)
|
||||
{
|
||||
camora.Upd.StackObjectsCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
camora.Upd = new() { StackObjectsCount = 1 };
|
||||
}
|
||||
camora.Upd = new Upd { StackObjectsCount = 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,47 +40,29 @@ public class FenceBaseAssortGenerator(
|
||||
foreach (var rootItemDb in itemHelper.GetItems().Where(IsValidFenceItem))
|
||||
{
|
||||
// Skip blacklisted items
|
||||
if (itemFilterService.IsItemBlacklisted(rootItemDb.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (itemFilterService.IsItemBlacklisted(rootItemDb.Id)) continue;
|
||||
|
||||
// Skip reward item blacklist
|
||||
if (itemFilterService.IsItemRewardBlacklisted(rootItemDb.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (itemFilterService.IsItemRewardBlacklisted(rootItemDb.Id)) continue;
|
||||
|
||||
// Invalid
|
||||
if (!itemHelper.IsValidItem(rootItemDb.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!itemHelper.IsValidItem(rootItemDb.Id)) continue;
|
||||
|
||||
// Item base type blacklisted
|
||||
if (traderConfig.Fence.Blacklist.Count > 0)
|
||||
{
|
||||
if (traderConfig.Fence.Blacklist.Contains(rootItemDb.Id) ||
|
||||
itemHelper.IsOfBaseclasses(rootItemDb.Id, traderConfig.Fence.Blacklist)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Only allow rigs with no slots (carrier rigs)
|
||||
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.VEST) &&
|
||||
(rootItemDb.Properties?.Slots?.Count ?? 0) > 0
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip seasonal event items when not in seasonal event
|
||||
if (traderConfig.Fence.BlacklistSeasonalItems && blockedSeasonalItems.Contains(rootItemDb.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (traderConfig.Fence.BlacklistSeasonalItems && blockedSeasonalItems.Contains(rootItemDb.Id)) continue;
|
||||
|
||||
// Create item object in array
|
||||
var itemWithChildrenToAdd = new List<Item>
|
||||
@@ -91,27 +73,19 @@ public class FenceBaseAssortGenerator(
|
||||
Template = rootItemDb.Id,
|
||||
ParentId = "hideout",
|
||||
SlotId = "hideout",
|
||||
Upd = new Upd { StackObjectsCount = 9999999 },
|
||||
Upd = new Upd { StackObjectsCount = 9999999 }
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure ammo is not above penetration limit value
|
||||
if (itemHelper.IsOfBaseclasses(rootItemDb.Id, [BaseClasses.AMMO_BOX, BaseClasses.AMMO]))
|
||||
{
|
||||
if (IsAmmoAbovePenetrationLimit(rootItemDb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
// Only add cartridges to box if box has no children
|
||||
if (itemWithChildrenToAdd.Count == 1)
|
||||
{
|
||||
itemHelper.AddCartridgesToAmmoBox(itemWithChildrenToAdd, rootItemDb);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure IDs are unique
|
||||
itemHelper.RemapRootItemId(itemWithChildrenToAdd);
|
||||
@@ -143,10 +117,7 @@ public class FenceBaseAssortGenerator(
|
||||
foreach (var defaultPreset in defaultPresets)
|
||||
{
|
||||
// Skip presets we've already added
|
||||
if (baseFenceAssort.Items.Any((item) => item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (baseFenceAssort.Items.Any((item) => item.Upd != null && item.Upd.SptPresetId == defaultPreset.Id)) continue;
|
||||
|
||||
// Construct preset + mods
|
||||
var itemAndChildren = itemHelper.ReplaceIDs(_cloner.Clone(defaultPreset.Items));
|
||||
@@ -165,7 +136,7 @@ public class FenceBaseAssortGenerator(
|
||||
{
|
||||
StackObjectsCount = 1,
|
||||
SptPresetId =
|
||||
defaultPreset.Id, // Store preset id here so we can check it later to prevent preset dupes
|
||||
defaultPreset.Id // Store preset id here so we can check it later to prevent preset dupes
|
||||
};
|
||||
|
||||
// Updated root item, exit loop
|
||||
@@ -181,14 +152,14 @@ public class FenceBaseAssortGenerator(
|
||||
var itemQualityModifier = itemHelper.GetItemQualityModifierForItems(itemAndChildren);
|
||||
|
||||
// Multiply weapon+mods rouble price by quality modifier
|
||||
baseFenceAssort.BarterScheme[itemAndChildren[0].Id] = new()
|
||||
baseFenceAssort.BarterScheme[itemAndChildren[0].Id] = new List<List<BarterScheme>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
new BarterScheme
|
||||
{
|
||||
Template = Money.ROUBLES,
|
||||
Count = Math.Round(price * itemQualityModifier),
|
||||
Count = Math.Round(price * itemQualityModifier)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -238,10 +209,7 @@ public class FenceBaseAssortGenerator(
|
||||
}
|
||||
|
||||
// Plain old ammo, get its pen property
|
||||
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO))
|
||||
{
|
||||
return rootItemDb.Properties.PenetrationPower;
|
||||
}
|
||||
if (itemHelper.IsOfBaseclass(rootItemDb.Id, BaseClasses.AMMO)) return rootItemDb.Properties.PenetrationPower;
|
||||
|
||||
// Not an ammobox or ammo
|
||||
return null;
|
||||
@@ -256,26 +224,20 @@ public class FenceBaseAssortGenerator(
|
||||
{
|
||||
// Armor has no mods, make no additions
|
||||
var hasMods = itemDbDetails.Properties.Slots.Count > 0;
|
||||
if (!hasMods)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!hasMods) return;
|
||||
|
||||
// Check for and add required soft inserts to armors
|
||||
var requiredSlots = itemDbDetails.Properties.Slots.Where((slot) => slot.Required ?? false).ToList();
|
||||
var hasRequiredSlots = requiredSlots.Count > 0;
|
||||
if (hasRequiredSlots)
|
||||
{
|
||||
foreach (var requiredSlot in requiredSlots)
|
||||
{
|
||||
var modItemDbDetails = itemHelper.GetItem(requiredSlot.Props.Filters[0].Plate).Value;
|
||||
var plateTpl =
|
||||
requiredSlot.Props.Filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
|
||||
if (string.IsNullOrEmpty(plateTpl))
|
||||
{
|
||||
// Some bsg plate properties are empty, skip mod
|
||||
continue;
|
||||
}
|
||||
|
||||
var mod = new Item
|
||||
{
|
||||
@@ -283,33 +245,29 @@ public class FenceBaseAssortGenerator(
|
||||
Template = plateTpl,
|
||||
ParentId = armor[0].Id,
|
||||
SlotId = requiredSlot.Name,
|
||||
Upd = new()
|
||||
Upd = new Upd
|
||||
{
|
||||
Repairable = new()
|
||||
Repairable = new UpdRepairable
|
||||
{
|
||||
Durability = modItemDbDetails.Properties.MaxDurability,
|
||||
MaxDurability = modItemDbDetails.Properties.MaxDurability,
|
||||
},
|
||||
},
|
||||
MaxDurability = modItemDbDetails.Properties.MaxDurability
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
armor.Add(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for and add plate items
|
||||
var plateSlots = itemDbDetails.Properties.Slots.Where((slot) => itemHelper.IsRemovablePlateSlot(slot.Name))
|
||||
.ToList();
|
||||
if (plateSlots.Count > 0)
|
||||
{
|
||||
foreach (var plateSlot in plateSlots)
|
||||
{
|
||||
var plateTpl = plateSlot.Props.Filters[0].Plate;
|
||||
if (string.IsNullOrEmpty(plateTpl))
|
||||
{
|
||||
// Bsg data lacks a default plate, skip adding mod
|
||||
continue;
|
||||
}
|
||||
|
||||
var modItemDbDetails = itemHelper.GetItem(plateTpl).Value;
|
||||
armor.Add(
|
||||
@@ -319,18 +277,17 @@ public class FenceBaseAssortGenerator(
|
||||
Template = plateSlot.Props.Filters[0].Plate, // `Plate` property appears to be the 'default' item for slot
|
||||
ParentId = armor[0].Id,
|
||||
SlotId = plateSlot.Name,
|
||||
Upd = new()
|
||||
Upd = new Upd
|
||||
{
|
||||
Repairable = new()
|
||||
Repairable = new UpdRepairable
|
||||
{
|
||||
Durability = modItemDbDetails.Properties.MaxDurability,
|
||||
MaxDurability = modItemDbDetails.Properties.MaxDurability,
|
||||
},
|
||||
},
|
||||
MaxDurability = modItemDbDetails.Properties.MaxDurability
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,43 +51,35 @@ public class LocationLootGenerator(
|
||||
|
||||
var staticWeaponsOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticWeapons);
|
||||
if (staticWeaponsOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_localisationService.GetText("location-unable_to_find_static_weapon_for_map", locationBase.Name)
|
||||
);
|
||||
}
|
||||
|
||||
// Add mounted weapons to output loot
|
||||
result.AddRange(staticWeaponsOnMapClone);
|
||||
|
||||
var allStaticContainersOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticContainers);
|
||||
if (allStaticContainersOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_localisationService.GetText("location-unable_to_find_static_container_for_map", locationBase.Name)
|
||||
);
|
||||
}
|
||||
|
||||
// Containers that MUST be added to map (e.g. quest containers)
|
||||
var staticForcedOnMapClone = _cloner.Clone(mapData.StaticContainers.Value.StaticForced);
|
||||
if (staticForcedOnMapClone is null)
|
||||
{
|
||||
_logger.Error(
|
||||
_localisationService.GetText(
|
||||
"location-unable_to_find_forced_static_data_for_map",
|
||||
locationBase.Name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Remove christmas items from loot data
|
||||
if (!_seasonalEventService.ChristmasEventEnabled())
|
||||
{
|
||||
allStaticContainersOnMapClone = allStaticContainersOnMapClone.Where(
|
||||
item => !_seasonalEventConfig.ChristmasContainerIds.Contains(item.Template.Id)
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var staticRandomisableContainersOnMap = GetRandomisableContainersOnMap(allStaticContainersOnMapClone);
|
||||
|
||||
@@ -114,10 +106,7 @@ public class LocationLootGenerator(
|
||||
staticLootItemCount += containerWithLoot.Template.Items.Count;
|
||||
}
|
||||
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Added {guaranteedContainers.Count} guaranteed containers");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Added {guaranteedContainers.Count} guaranteed containers");
|
||||
|
||||
// Randomisation is turned off globally or just turned off for this map
|
||||
if (
|
||||
@@ -127,12 +116,10 @@ public class LocationLootGenerator(
|
||||
)
|
||||
)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Container randomisation disabled, Adding {staticRandomisableContainersOnMap.Count} containers to {locationBase.Name}"
|
||||
);
|
||||
}
|
||||
foreach (var container in staticRandomisableContainersOnMap)
|
||||
{
|
||||
var containerWithLoot = AddLootToContainer(
|
||||
@@ -166,17 +153,11 @@ public class LocationLootGenerator(
|
||||
foreach (var (key, data) in mapping)
|
||||
{
|
||||
// Count chosen was 0, skip
|
||||
if (data.ChosenCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (data.ChosenCount == 0) continue;
|
||||
|
||||
if (data.ContainerIdsWithProbability.Count == 0)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Group: {key} has no containers with < 100 % spawn chance to choose from, skipping");
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -189,18 +170,13 @@ public class LocationLootGenerator(
|
||||
data.ContainerIdsWithProbability = new Dictionary<string, double>();
|
||||
foreach (var containerId in containerIdsCopy)
|
||||
if (_randomUtil.GetChance100(containerIdsCopy[containerId.Key] * 100))
|
||||
{
|
||||
data.ContainerIdsWithProbability[containerId.Key] = containerIdsCopy[containerId.Key];
|
||||
}
|
||||
|
||||
// Set desired count to size of array (we want all containers chosen)
|
||||
data.ChosenCount = data.ContainerIdsWithProbability.Count;
|
||||
|
||||
// EDGE CASE: chosen container count could be 0
|
||||
if (data.ChosenCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (data.ChosenCount == 0) continue;
|
||||
}
|
||||
|
||||
// Pass possible containers into function to choose some
|
||||
@@ -213,12 +189,10 @@ public class LocationLootGenerator(
|
||||
);
|
||||
if (containerObject is null)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Container: {chosenContainerId} not found in staticRandomisableContainersOnMap, this is bad"
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -296,12 +270,10 @@ public class LocationLootGenerator(
|
||||
var containerIds = containerData.ContainerIdsWithProbability.Keys.ToList();
|
||||
if (containerData.ChosenCount > containerIds.Count)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Group: {groupId} wants {containerData.ChosenCount} containers but pool only has {containerIds.Count}, add what's available"
|
||||
);
|
||||
}
|
||||
return containerIds;
|
||||
}
|
||||
|
||||
@@ -331,9 +303,7 @@ public class LocationLootGenerator(
|
||||
// Create dictionary of all group ids and choose a count of containers the map will spawn of that group
|
||||
var mapping = new Dictionary<string, ContainerGroupCount>();
|
||||
foreach (var groupKvP in staticContainerGroupData.ContainersGroups)
|
||||
{
|
||||
if (staticContainerGroupData.ContainersGroups.TryGetValue(groupKvP.Key, out var groupData))
|
||||
{
|
||||
mapping[groupKvP.Key] = new ContainerGroupCount
|
||||
{
|
||||
ContainerIdsWithProbability = new Dictionary<string, double>(),
|
||||
@@ -348,8 +318,6 @@ public class LocationLootGenerator(
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add an empty group for containers without a group id but still have a < 100% chance to spawn
|
||||
// Likely bad BSG data, will be fixed...eventually, example of the groupids: `NEED_TO_BE_FIXED1`,`NEED_TO_BE_FIXED_SE02`, `NEED_TO_BE_FIXED_NW_01`
|
||||
@@ -373,21 +341,22 @@ public class LocationLootGenerator(
|
||||
|
||||
if (container.Probability >= 1)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Container {container.Template.Id} with group: {groupData.GroupId} had 100 % chance to spawn was picked as random container, skipping"
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.TryAdd(groupData.GroupId, new ContainerGroupCount
|
||||
{
|
||||
ChosenCount = 0d,
|
||||
ContainerIdsWithProbability = new Dictionary<string, double>()
|
||||
});
|
||||
mapping.TryAdd(
|
||||
groupData.GroupId,
|
||||
new ContainerGroupCount
|
||||
{
|
||||
ChosenCount = 0d,
|
||||
ContainerIdsWithProbability = new Dictionary<string, double>()
|
||||
}
|
||||
);
|
||||
mapping[groupData.GroupId].ContainerIdsWithProbability.TryAdd(container.Template.Id, container.Probability.Value);
|
||||
}
|
||||
|
||||
@@ -447,10 +416,7 @@ public class LocationLootGenerator(
|
||||
foreach (var tplToAdd in tplsToAddToContainer)
|
||||
{
|
||||
var chosenItemWithChildren = CreateStaticLootItem(tplToAdd, staticAmmoDist, parentId);
|
||||
if (chosenItemWithChildren is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (chosenItemWithChildren is null) continue;
|
||||
|
||||
var items = _locationConfig.TplsToStripChildItemsFrom.Contains(tplToAdd)
|
||||
? [chosenItemWithChildren.Items[0]] // Strip children from parent
|
||||
@@ -464,17 +430,15 @@ public class LocationLootGenerator(
|
||||
if (!result.Success.GetValueOrDefault(false))
|
||||
{
|
||||
if (failedToFitCount > _locationConfig.FitLootIntoContainerAttempts)
|
||||
{
|
||||
// x attempts to fit an item, container is probably full, stop trying to add more
|
||||
break;
|
||||
}
|
||||
|
||||
// Can't fit item, skip
|
||||
failedToFitCount++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
_containerHelper.FillContainerMapWithItem(
|
||||
containerMap,
|
||||
result.X.Value,
|
||||
@@ -485,9 +449,9 @@ public class LocationLootGenerator(
|
||||
);
|
||||
|
||||
var rotation = result.Rotation.GetValueOrDefault(false) ? 1 : 0;
|
||||
|
||||
|
||||
items[0].SlotId = "main";
|
||||
items[0].Location = new ItemLocation{ X = result.X, Y = result.Y, R = rotation };
|
||||
items[0].Location = new ItemLocation { X = result.X, Y = result.Y, R = rotation };
|
||||
|
||||
// Add loot to container before returning
|
||||
containerClone.Template.Items.AddRange(items);
|
||||
@@ -517,7 +481,7 @@ public class LocationLootGenerator(
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Multi-mod-item, use helper to get size of item + attached mods
|
||||
var result = _inventoryHelper.GetItemSize(rootItem.Template, rootItem.Id, items);
|
||||
return new ItemSize
|
||||
@@ -525,7 +489,6 @@ public class LocationLootGenerator(
|
||||
Width = result[0],
|
||||
Height = result[1]
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -614,16 +577,11 @@ public class LocationLootGenerator(
|
||||
foreach (var icd in itemContainerDistribution)
|
||||
{
|
||||
if (!seasonalEventActive && seasonalItemTplBlacklist.Contains(icd.Tpl))
|
||||
{
|
||||
// Skip seasonal event items if they're not enabled
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure no blacklisted lootable items are in pool
|
||||
if (_itemFilterService.IsLootableItemBlacklisted(icd.Tpl))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_itemFilterService.IsLootableItemBlacklisted(icd.Tpl)) continue;
|
||||
|
||||
itemDistribution.Add(new ProbabilityObject<string, float?>(icd.Tpl, icd.RelativeProbability.Value, null));
|
||||
}
|
||||
@@ -697,18 +655,12 @@ public class LocationLootGenerator(
|
||||
// Point is blacklisted, skip
|
||||
if (blacklistedSpawnpoints?.Contains(spawnpoint.Template.Id) ?? false)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Ignoring loose loot location: {spawnpoint.Template.Id}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Ignoring loose loot location: {spawnpoint.Template.Id}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// We've handled IsAlwaysSpawn above, so skip them
|
||||
if (spawnpoint.Template.IsAlwaysSpawn ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (spawnpoint.Template.IsAlwaysSpawn ?? false) continue;
|
||||
|
||||
// 100%, add it to guaranteed
|
||||
if (spawnpoint.Probability == 1)
|
||||
@@ -728,13 +680,9 @@ public class LocationLootGenerator(
|
||||
var randomSpawnpointCount = desiredSpawnpointCount - chosenSpawnpoints.Count;
|
||||
// Only draw random spawn points if needed
|
||||
if (randomSpawnpointCount > 0 && spawnpointArray.Count > 0)
|
||||
{
|
||||
// Add randomly chosen spawn points
|
||||
foreach (var si in spawnpointArray.Draw((int)randomSpawnpointCount, false))
|
||||
{
|
||||
chosenSpawnpoints.Add(spawnpointArray.Data(si));
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out duplicate locationIds // prob can be done better
|
||||
chosenSpawnpoints = chosenSpawnpoints.GroupBy(spawnpoint => spawnpoint.LocationId).Select(group => group.First()).ToList();
|
||||
@@ -742,9 +690,7 @@ public class LocationLootGenerator(
|
||||
// Do we have enough items in pool to fulfill requirement
|
||||
var tooManySpawnPointsRequested = desiredSpawnpointCount - chosenSpawnpoints.Count > 0;
|
||||
if (tooManySpawnPointsRequested)
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
_localisationService.GetText(
|
||||
"location-spawn_point_count_requested_vs_found",
|
||||
@@ -752,12 +698,10 @@ public class LocationLootGenerator(
|
||||
{
|
||||
requested = desiredSpawnpointCount + guaranteedLoosePoints.Count,
|
||||
found = chosenSpawnpoints.Count,
|
||||
mapName = locationName,
|
||||
mapName = locationName
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over spawnpoints
|
||||
var seasonalEventActive = _seasonalEventService.SeasonalEventEnabled();
|
||||
@@ -782,22 +726,18 @@ public class LocationLootGenerator(
|
||||
|
||||
// Ensure no seasonal items are in pool if not in-season
|
||||
if (!seasonalEventActive)
|
||||
{
|
||||
spawnPoint.Template.Items = spawnPoint.Template.Items.Where(
|
||||
(item) => !seasonalItemTplBlacklist.Contains(item.Template)
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// Spawn point has no items after filtering, skip
|
||||
if (spawnPoint.Template.Items is null || spawnPoint.Template.Items.Count == 0)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
_localisationService.GetText("location-spawnpoint_missing_items", spawnPoint.Template.Id)
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -809,10 +749,7 @@ public class LocationLootGenerator(
|
||||
var itemArray = new ProbabilityObjectArray<string, double?>(_mathUtil, _cloner);
|
||||
foreach (var itemDist in spawnPoint.ItemDistribution)
|
||||
{
|
||||
if (!validItemIds.Contains(itemDist.ComposedKey.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!validItemIds.Contains(itemDist.ComposedKey.Key)) continue;
|
||||
|
||||
itemArray.Add(new ProbabilityObject<string, double?>(itemDist.ComposedKey.Key, itemDist.RelativeProbability ?? 0, null));
|
||||
}
|
||||
@@ -858,7 +795,6 @@ public class LocationLootGenerator(
|
||||
{
|
||||
var lootToForceSingleAmountOnMap = _locationConfig.ForcedLootSingleSpawnById.GetValueOrDefault(locationName);
|
||||
if (lootToForceSingleAmountOnMap is not null)
|
||||
{
|
||||
// Process loot items defined as requiring only 1 spawn position as they appear in multiple positions on the map
|
||||
foreach (var itemTpl in lootToForceSingleAmountOnMap)
|
||||
{
|
||||
@@ -868,20 +804,16 @@ public class LocationLootGenerator(
|
||||
);
|
||||
if (items is null || !items.Any())
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug($"Unable to adjust loot item {itemTpl} as it does not exist inside {locationName} forced loot.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create probability array of all spawn positions for this spawn id
|
||||
var spawnpointArray = new ProbabilityObjectArray<string, Spawnpoint>(_mathUtil, _cloner);
|
||||
foreach (var si in items)
|
||||
{
|
||||
// use locationId as template.Id is the same across all items
|
||||
spawnpointArray.Add(new ProbabilityObject<string, Spawnpoint>(si.LocationId, si.Probability ?? 0, si));
|
||||
}
|
||||
|
||||
// Choose 1 out of all found spawn positions for spawn id and add to loot array
|
||||
foreach (var spawnPointLocationId in spawnpointArray.Draw(1, false))
|
||||
@@ -906,7 +838,6 @@ public class LocationLootGenerator(
|
||||
lootLocationTemplates.Add(lootItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var seasonalEventActive = _seasonalEventService.SeasonalEventEnabled();
|
||||
var seasonalItemTplBlacklist = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
@@ -917,16 +848,10 @@ public class LocationLootGenerator(
|
||||
var firstLootItemTpl = forcedLootLocation.Template.Items.FirstOrDefault().Template;
|
||||
|
||||
// Skip spawn positions processed already
|
||||
if (lootToForceSingleAmountOnMap?.Contains(firstLootItemTpl) ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (lootToForceSingleAmountOnMap?.Contains(firstLootItemTpl) ?? false) continue;
|
||||
|
||||
// Skip adding seasonal items when seasonal event is not active
|
||||
if (!seasonalEventActive && seasonalItemTplBlacklist.Contains(firstLootItemTpl))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!seasonalEventActive && seasonalItemTplBlacklist.Contains(firstLootItemTpl)) continue;
|
||||
|
||||
var locationTemplateToAdd = forcedLootLocation.Template;
|
||||
var createItemResult = CreateDynamicLootItem(
|
||||
@@ -949,12 +874,10 @@ public class LocationLootGenerator(
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"Attempted to add a forced loot location with Id: {locationTemplateToAdd.Id} to map {locationName} that already has that id in use, skipping"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -963,39 +886,43 @@ public class LocationLootGenerator(
|
||||
{
|
||||
var chosenItem = items.FirstOrDefault((item) => item.Id == chosenComposedKey);
|
||||
var chosenTpl = chosenItem?.Template;
|
||||
if (chosenTpl is null) {
|
||||
throw new Exception($"Item for tpl {chosenComposedKey} was not found in the spawn point");
|
||||
}
|
||||
if (chosenTpl is null) throw new Exception($"Item for tpl {chosenComposedKey} was not found in the spawn point");
|
||||
var itemTemplate = _itemHelper.GetItem(chosenTpl).Value;
|
||||
if (itemTemplate is null) {
|
||||
_logger.Error($"Item tpl: {chosenTpl} cannot be found in database");
|
||||
}
|
||||
if (itemTemplate is null) _logger.Error($"Item tpl: {chosenTpl} cannot be found in database");
|
||||
|
||||
// Item array to return
|
||||
List<Item> itemWithMods = [];
|
||||
|
||||
// Money/Ammo - don't rely on items in spawnPoint.template.Items so we can randomise it ourselves
|
||||
if (_itemHelper.IsOfBaseclasses(chosenTpl, [BaseClasses.MONEY, BaseClasses.AMMO])) {
|
||||
if (_itemHelper.IsOfBaseclasses(chosenTpl, [BaseClasses.MONEY, BaseClasses.AMMO]))
|
||||
{
|
||||
var stackCount =
|
||||
itemTemplate.Properties.StackMaxSize == 1
|
||||
? 1
|
||||
: _randomUtil.GetInt((int)itemTemplate.Properties.StackMinRandom, (int)itemTemplate.Properties.StackMaxRandom);
|
||||
|
||||
itemWithMods.Add(new Item {
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = chosenTpl,
|
||||
Upd = new Upd { StackObjectsCount = stackCount }
|
||||
});
|
||||
} else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX)) {
|
||||
itemWithMods.Add(
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = chosenTpl,
|
||||
Upd = new Upd { StackObjectsCount = stackCount }
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
// Fill with cartridges
|
||||
List<Item> ammoBoxItem = [ new Item { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
List<Item> ammoBoxItem = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
_itemHelper.AddCartridgesToAmmoBox(ammoBoxItem, itemTemplate);
|
||||
itemWithMods.AddRange(ammoBoxItem);
|
||||
} else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MAGAZINE)) {
|
||||
}
|
||||
else if (_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MAGAZINE))
|
||||
{
|
||||
// Create array with just magazine
|
||||
List<Item> magazineItem = [new Item { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
List<Item> magazineItem = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
|
||||
if (_randomUtil.GetChance100(_locationConfig.StaticMagazineLootHasAmmoChancePercent)) {
|
||||
if (_randomUtil.GetChance100(_locationConfig.StaticMagazineLootHasAmmoChancePercent))
|
||||
// Add randomised amount of cartridges
|
||||
_itemHelper.FillMagazineWithRandomCartridge(
|
||||
magazineItem,
|
||||
@@ -1004,10 +931,11 @@ public class LocationLootGenerator(
|
||||
null,
|
||||
_locationConfig.MinFillLooseMagazinePercent / 100
|
||||
);
|
||||
}
|
||||
|
||||
itemWithMods.AddRange(magazineItem);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Also used by armors to get child mods
|
||||
// Get item + children and add into array we return
|
||||
var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(items, chosenItem.Id);
|
||||
@@ -1015,10 +943,9 @@ public class LocationLootGenerator(
|
||||
// Ensure all IDs are unique
|
||||
itemWithChildren = _itemHelper.ReplaceIDs(_cloner.Clone(itemWithChildren));
|
||||
|
||||
if (_locationConfig.TplsToStripChildItemsFrom.Contains(chosenItem.Template)) {
|
||||
if (_locationConfig.TplsToStripChildItemsFrom.Contains(chosenItem.Template))
|
||||
// Strip children from parent before adding
|
||||
itemWithChildren = [itemWithChildren[0]];
|
||||
}
|
||||
|
||||
itemWithMods.AddRange(itemWithChildren);
|
||||
}
|
||||
@@ -1033,8 +960,9 @@ public class LocationLootGenerator(
|
||||
{
|
||||
return _locationConfig.LooseLootMultiplier[location];
|
||||
}
|
||||
|
||||
protected double getStaticLootMultiplerForLocation(string location) {
|
||||
|
||||
protected double getStaticLootMultiplerForLocation(string location)
|
||||
{
|
||||
return _locationConfig.StaticLootMultiplier[location];
|
||||
}
|
||||
|
||||
@@ -1055,14 +983,11 @@ public class LocationLootGenerator(
|
||||
|
||||
var width = itemTemplate.Properties.Width;
|
||||
var height = itemTemplate.Properties.Height;
|
||||
List<Item> items = [new Item { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
List<Item> items = [new() { Id = _hashUtil.Generate(), Template = chosenTpl }];
|
||||
var rootItem = items.FirstOrDefault();
|
||||
|
||||
// Use passed in parentId as override for new item
|
||||
if (!string.IsNullOrEmpty(parentId))
|
||||
{
|
||||
rootItem.ParentId = parentId;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(parentId)) rootItem.ParentId = parentId;
|
||||
|
||||
if (
|
||||
_itemHelper.IsOfBaseclass(chosenTpl, BaseClasses.MONEY) ||
|
||||
@@ -1072,7 +997,7 @@ public class LocationLootGenerator(
|
||||
// Edge case - some ammos e.g. flares or M406 grenades shouldn't be stacked
|
||||
var stackCount = itemTemplate.Properties.StackMaxSize == 1
|
||||
? 1
|
||||
: _randomUtil.GetInt((int)(itemTemplate.Properties.StackMinRandom), (int)(itemTemplate.Properties.StackMaxRandom));
|
||||
: _randomUtil.GetInt((int)itemTemplate.Properties.StackMinRandom, (int)itemTemplate.Properties.StackMaxRandom);
|
||||
|
||||
rootItem.Upd = new Upd { StackObjectsCount = stackCount };
|
||||
}
|
||||
@@ -1100,7 +1025,7 @@ public class LocationLootGenerator(
|
||||
tpl = chosenTpl,
|
||||
defaultId = defaultPreset.Id,
|
||||
defaultName = defaultPreset.Name,
|
||||
parentId,
|
||||
parentId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -1111,10 +1036,7 @@ public class LocationLootGenerator(
|
||||
else
|
||||
{
|
||||
// RSP30 (62178be9d0050232da3485d9/624c0b3340357b5f566e8766/6217726288ed9f0845317459) doesn't have any default presets and kills this code below as it has no chidren to reparent
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"createStaticLootItem() No preset found for weapon: {chosenTpl}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"createStaticLootItem() No preset found for weapon: {chosenTpl}");
|
||||
}
|
||||
|
||||
rootItem = items[0];
|
||||
@@ -1126,7 +1048,7 @@ public class LocationLootGenerator(
|
||||
new
|
||||
{
|
||||
tpl = chosenTpl,
|
||||
parentId,
|
||||
parentId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -1136,10 +1058,7 @@ public class LocationLootGenerator(
|
||||
|
||||
try
|
||||
{
|
||||
if (children?.Count > 0)
|
||||
{
|
||||
items = _itemHelper.ReparentItemAndChildren(rootItem, children);
|
||||
}
|
||||
if (children?.Count > 0) items = _itemHelper.ReparentItemAndChildren(rootItem, children);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1149,7 +1068,7 @@ public class LocationLootGenerator(
|
||||
new
|
||||
{
|
||||
tpl = chosenTpl,
|
||||
parentId = parentId,
|
||||
parentId = parentId
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -1230,13 +1149,11 @@ public class LocationLootGenerator(
|
||||
{
|
||||
// We make base item above, at start of function, no need to do it here
|
||||
if ((itemTemplate.Properties.Slots?.Count ?? 0) > 0)
|
||||
{
|
||||
items = _itemHelper.AddChildSlotItems(
|
||||
items,
|
||||
itemTemplate,
|
||||
_locationConfig.EquipmentLootSettings.ModSpawnChancePercent
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,8 @@ public class LootGenerator(
|
||||
WeightedRandomHelper _weightedRandomHelper,
|
||||
RagfairLinkedItemService _ragfairLinkedItemService,
|
||||
ICloner _cloner
|
||||
)
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of items based on configuration options parameter
|
||||
/// </summary>
|
||||
@@ -44,24 +43,33 @@ public class LootGenerator(
|
||||
// Handle sealed weapon containers
|
||||
var sealedWeaponCrateCount = _randomUtil.GetDouble(
|
||||
options.WeaponCrateCount.Min.Value,
|
||||
options.WeaponCrateCount.Max.Value);
|
||||
if (sealedWeaponCrateCount > 0) {
|
||||
options.WeaponCrateCount.Max.Value
|
||||
);
|
||||
if (sealedWeaponCrateCount > 0)
|
||||
{
|
||||
// Get list of all sealed containers from db - they're all the same, just for flavor
|
||||
var itemsDb = _itemHelper.GetItems();
|
||||
var sealedWeaponContainerPool = (itemsDb).Where((item) =>
|
||||
item.Name.Contains("event_container_airdrop"));
|
||||
var sealedWeaponContainerPool = itemsDb.Where(
|
||||
(item) =>
|
||||
item.Name.Contains("event_container_airdrop")
|
||||
);
|
||||
|
||||
for (var index = 0; index < sealedWeaponCrateCount; index++) {
|
||||
for (var index = 0; index < sealedWeaponCrateCount; index++)
|
||||
{
|
||||
// Choose one at random + add to results array
|
||||
var chosenSealedContainer = _randomUtil.GetArrayValue(sealedWeaponContainerPool);
|
||||
result.Add( new Item{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = chosenSealedContainer.Id,
|
||||
Upd = new Upd{
|
||||
StackObjectsCount = 1,
|
||||
SpawnedInSession = true
|
||||
},
|
||||
});
|
||||
result.Add(
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = chosenSealedContainer.Id,
|
||||
Upd = new Upd
|
||||
{
|
||||
StackObjectsCount = 1,
|
||||
SpawnedInSession = true
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,17 +78,17 @@ public class LootGenerator(
|
||||
options.ItemBlacklist,
|
||||
options.ItemTypeWhitelist,
|
||||
options.UseRewardItemBlacklist.GetValueOrDefault(false),
|
||||
options.AllowBossItems.GetValueOrDefault(false));
|
||||
options.AllowBossItems.GetValueOrDefault(false)
|
||||
);
|
||||
|
||||
// Pool has items we could add as loot, proceed
|
||||
if (rewardPoolResults.ItemPool.Count > 0) {
|
||||
if (rewardPoolResults.ItemPool.Count > 0)
|
||||
{
|
||||
var randomisedItemCount = _randomUtil.GetDouble(options.ItemCount.Min.Value, options.ItemCount.Max.Value);
|
||||
for (var index = 0; index < randomisedItemCount; index++) {
|
||||
if (!FindAndAddRandomItemToLoot(rewardPoolResults.ItemPool, itemTypeCounts, options, result)) {
|
||||
for (var index = 0; index < randomisedItemCount; index++)
|
||||
if (!FindAndAddRandomItemToLoot(rewardPoolResults.ItemPool, itemTypeCounts, options, result))
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var globalDefaultPresets = _presetHelper.GetDefaultPresets().Values;
|
||||
@@ -88,52 +96,60 @@ public class LootGenerator(
|
||||
// Filter default presets to just weapons
|
||||
var randomisedWeaponPresetCount = _randomUtil.GetDouble(
|
||||
options.WeaponPresetCount.Min.Value,
|
||||
options.WeaponPresetCount.Max.Value);
|
||||
if (randomisedWeaponPresetCount > 0) {
|
||||
var weaponDefaultPresets = globalDefaultPresets.Where((preset) =>
|
||||
_itemHelper.IsOfBaseclass(preset.Encyclopedia, BaseClasses.WEAPON)).ToList();
|
||||
options.WeaponPresetCount.Max.Value
|
||||
);
|
||||
if (randomisedWeaponPresetCount > 0)
|
||||
{
|
||||
var weaponDefaultPresets = globalDefaultPresets.Where(
|
||||
(preset) =>
|
||||
_itemHelper.IsOfBaseclass(preset.Encyclopedia, BaseClasses.WEAPON)
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if (weaponDefaultPresets.Any()) {
|
||||
for (var index = 0; index < randomisedWeaponPresetCount; index++) {
|
||||
if (weaponDefaultPresets.Any())
|
||||
for (var index = 0; index < randomisedWeaponPresetCount; index++)
|
||||
if (
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
weaponDefaultPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result)
|
||||
) {
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
weaponDefaultPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result
|
||||
)
|
||||
)
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter default presets to just armors and then filter again by protection level
|
||||
var randomisedArmorPresetCount = _randomUtil.GetDouble(
|
||||
options.ArmorPresetCount.Min.Value,
|
||||
options.ArmorPresetCount.Max.Value);
|
||||
if (randomisedArmorPresetCount > 0) {
|
||||
var armorDefaultPresets = globalDefaultPresets.Where((preset) =>
|
||||
_itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia));
|
||||
var levelFilteredArmorPresets = armorDefaultPresets.Where((armor) =>
|
||||
IsArmorOfDesiredProtectionLevel(armor, options)).ToList();
|
||||
options.ArmorPresetCount.Max.Value
|
||||
);
|
||||
if (randomisedArmorPresetCount > 0)
|
||||
{
|
||||
var armorDefaultPresets = globalDefaultPresets.Where(
|
||||
(preset) =>
|
||||
_itemHelper.ArmorItemCanHoldMods(preset.Encyclopedia)
|
||||
);
|
||||
var levelFilteredArmorPresets = armorDefaultPresets.Where(
|
||||
(armor) =>
|
||||
IsArmorOfDesiredProtectionLevel(armor, options)
|
||||
)
|
||||
.ToList();
|
||||
|
||||
// Add some armors to rewards
|
||||
if (levelFilteredArmorPresets.Any()) {
|
||||
for (var index = 0; index < randomisedArmorPresetCount; index++) {
|
||||
if (levelFilteredArmorPresets.Any())
|
||||
for (var index = 0; index < randomisedArmorPresetCount; index++)
|
||||
if (
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
levelFilteredArmorPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result)
|
||||
) {
|
||||
!FindAndAddRandomPresetToLoot(
|
||||
levelFilteredArmorPresets,
|
||||
itemTypeCounts,
|
||||
rewardPoolResults.Blacklist,
|
||||
result
|
||||
)
|
||||
)
|
||||
// Failed to add, reduce index so we get another attempt
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -151,18 +167,21 @@ public class LootGenerator(
|
||||
|
||||
var forcedItems = forcedLootDict;
|
||||
|
||||
foreach (var forcedItemKvP in forcedItems) {
|
||||
foreach (var forcedItemKvP in forcedItems)
|
||||
{
|
||||
var details = forcedLootDict[forcedItemKvP.Key];
|
||||
var randomisedItemCount = _randomUtil.GetDouble(details.Min.Value, details.Max.Value);
|
||||
|
||||
// Add forced loot item to result
|
||||
var newLootItem = new Item{
|
||||
var newLootItem = new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = forcedItemKvP.Key,
|
||||
Upd = new Upd{
|
||||
Upd = new Upd
|
||||
{
|
||||
StackObjectsCount = randomisedItemCount,
|
||||
SpawnedInSession = true,
|
||||
},
|
||||
SpawnedInSession = true
|
||||
}
|
||||
};
|
||||
|
||||
var splitResults = _itemHelper.SplitStack(newLootItem);
|
||||
@@ -195,7 +214,7 @@ public class LootGenerator(
|
||||
|
||||
// Get all items that match the blacklisted types and fold into item blacklist
|
||||
var itemTypeBlacklist = _itemFilterService.GetItemRewardBaseTypeBlacklist();
|
||||
var itemsMatchingTypeBlacklist = (itemsDb)
|
||||
var itemsMatchingTypeBlacklist = itemsDb
|
||||
.Where((templateItem) => _itemHelper.IsOfBaseclasses(templateItem.Parent, itemTypeBlacklist))
|
||||
.Select((templateItem) => templateItem.Id);
|
||||
|
||||
@@ -207,26 +226,19 @@ public class LootGenerator(
|
||||
}
|
||||
|
||||
if (!allowBossItems)
|
||||
{
|
||||
foreach (var bossItem in _itemFilterService.GetBossItems()) {
|
||||
foreach (var bossItem in _itemFilterService.GetBossItems())
|
||||
itemBlacklist.Add(bossItem);
|
||||
}
|
||||
}
|
||||
|
||||
var items = itemsDb.Where(
|
||||
(item) =>
|
||||
!itemBlacklist.Contains(item.Id) &&
|
||||
(item) =>
|
||||
!itemBlacklist.Contains(item.Id) &&
|
||||
item.Type.ToLower() == "item" &&
|
||||
!item.Properties.QuestItem.GetValueOrDefault(false) &&
|
||||
itemTypeWhitelist.Contains(item.Parent)).ToList();
|
||||
!item.Properties.QuestItem.GetValueOrDefault(false) &&
|
||||
itemTypeWhitelist.Contains(item.Parent)
|
||||
)
|
||||
.ToList();
|
||||
|
||||
return new ItemRewardPoolResults{ ItemPool = items, Blacklist = itemBlacklist };
|
||||
}
|
||||
|
||||
public record ItemRewardPoolResults
|
||||
{
|
||||
public List<TemplateItem> ItemPool { get; set; }
|
||||
public HashSet<string> Blacklist { get; set; }
|
||||
return new ItemRewardPoolResults { ItemPool = items, Blacklist = itemBlacklist };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -238,12 +250,10 @@ public class LootGenerator(
|
||||
protected bool IsArmorOfDesiredProtectionLevel(Preset armor, LootRequest options)
|
||||
{
|
||||
string[] relevantSlots = ["front_plate", "helmet_top", "soft_armor_front"];
|
||||
foreach (var slotId in relevantSlots) {
|
||||
foreach (var slotId in relevantSlots)
|
||||
{
|
||||
var armorItem = armor.Items.FirstOrDefault((item) => item?.SlotId?.ToLower() == slotId);
|
||||
if (armorItem is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (armorItem is null) continue;
|
||||
|
||||
var armorDetails = _itemHelper.GetItem(armorItem.Template).Value;
|
||||
var armorClass = armorDetails.Properties.ArmorClass;
|
||||
@@ -262,9 +272,7 @@ public class LootGenerator(
|
||||
private Dictionary<string, ItemLimit> InitItemLimitCounter(Dictionary<string, double> limits)
|
||||
{
|
||||
var itemTypeCounts = new Dictionary<string, ItemLimit>();
|
||||
foreach (var itemTypeId in limits) {
|
||||
itemTypeCounts[itemTypeId.Key] = new ItemLimit() { Current = 0, Max = limits[itemTypeId.Key] };
|
||||
}
|
||||
foreach (var itemTypeId in limits) itemTypeCounts[itemTypeId.Key] = new ItemLimit() { Current = 0, Max = limits[itemTypeId.Key] };
|
||||
|
||||
return itemTypeCounts;
|
||||
}
|
||||
@@ -284,36 +292,31 @@ public class LootGenerator(
|
||||
var randomItem = _randomUtil.GetArrayValue(items);
|
||||
|
||||
var itemLimitCount = itemTypeCounts.TryGetValue(randomItem.Parent, out var randomItemLimitCount);
|
||||
if (!itemLimitCount && randomItemLimitCount?.Current > randomItemLimitCount?.Max) {
|
||||
return false;
|
||||
}
|
||||
if (!itemLimitCount && randomItemLimitCount?.Current > randomItemLimitCount?.Max) return false;
|
||||
|
||||
// Skip armors as they need to come from presets
|
||||
if (_itemHelper.ArmorItemCanHoldMods(randomItem.Id)) {
|
||||
return false;
|
||||
}
|
||||
if (_itemHelper.ArmorItemCanHoldMods(randomItem.Id)) return false;
|
||||
|
||||
var newLootItem = new Item {
|
||||
var newLootItem = new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = randomItem.Id,
|
||||
Upd = new Upd {
|
||||
Upd = new Upd
|
||||
{
|
||||
StackObjectsCount = 1,
|
||||
SpawnedInSession = true,
|
||||
},
|
||||
SpawnedInSession = true
|
||||
}
|
||||
};
|
||||
|
||||
// Special case - handle items that need a stackcount > 1
|
||||
if (randomItem.Properties.StackMaxSize > 1) {
|
||||
newLootItem.Upd.StackObjectsCount = GetRandomisedStackCount(randomItem, options);
|
||||
}
|
||||
if (randomItem.Properties.StackMaxSize > 1) newLootItem.Upd.StackObjectsCount = GetRandomisedStackCount(randomItem, options);
|
||||
|
||||
newLootItem.Template = randomItem.Id;
|
||||
result.Add(newLootItem);
|
||||
|
||||
if (randomItemLimitCount is not null) {
|
||||
if (randomItemLimitCount is not null)
|
||||
// Increment item count as it's in limit array
|
||||
randomItemLimitCount.Current++;
|
||||
}
|
||||
|
||||
// Item added okay
|
||||
return true;
|
||||
@@ -330,7 +333,8 @@ public class LootGenerator(
|
||||
var min = item.Properties.StackMinRandom;
|
||||
var max = item.Properties.StackMaxSize;
|
||||
|
||||
if (options.ItemStackLimits.TryGetValue(item.Id, out var itemLimits)) {
|
||||
if (options.ItemStackLimits.TryGetValue(item.Id, out var itemLimits))
|
||||
{
|
||||
min = itemLimits.Min;
|
||||
max = (int?)itemLimits.Max;
|
||||
}
|
||||
@@ -353,40 +357,36 @@ public class LootGenerator(
|
||||
{
|
||||
// Choose random preset and get details from item db using encyclopedia value (encyclopedia === tplId)
|
||||
var chosenPreset = _randomUtil.GetArrayValue(presetPool);
|
||||
if (chosenPreset is null ) {
|
||||
if (chosenPreset is null)
|
||||
{
|
||||
_logger.Warning("Unable to find random preset in given presets, skipping");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// No `_encyclopedia` property, not possible to reliably get root item tpl
|
||||
if (chosenPreset.Encyclopedia is null) {
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Preset with id: {chosenPreset?.Id} lacks encyclopedia property, skipping");
|
||||
}
|
||||
if (chosenPreset.Encyclopedia is null)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Preset with id: {chosenPreset?.Id} lacks encyclopedia property, skipping");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get preset root item db details via its `_encyclopedia` property
|
||||
var itemDbDetails = _itemHelper.GetItem(chosenPreset.Encyclopedia);
|
||||
if (!itemDbDetails.Key) {
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping");
|
||||
}
|
||||
if (!itemDbDetails.Key)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"$Unable to find preset with tpl: {chosenPreset.Encyclopedia}, skipping");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip preset if root item is blacklisted
|
||||
if (itemBlacklist.Contains(chosenPreset.Items[0].Template)) {
|
||||
return false;
|
||||
}
|
||||
if (itemBlacklist.Contains(chosenPreset.Items[0].Template)) return false;
|
||||
|
||||
// Some custom mod items lack a parent property
|
||||
if (itemDbDetails.Value.Parent is null) {
|
||||
if (itemDbDetails.Value.Parent is null)
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("loot-item_missing_parentid", itemDbDetails.Value?.Name));
|
||||
|
||||
return false;
|
||||
@@ -394,21 +394,16 @@ public class LootGenerator(
|
||||
|
||||
// Check chosen preset hasn't exceeded spawn limit
|
||||
var hasItemLimitCount = itemTypeCounts.TryGetValue(itemDbDetails.Value.Parent, out var itemLimitCount);
|
||||
if (!hasItemLimitCount && itemLimitCount?.Current > itemLimitCount?.Max) {
|
||||
return false;
|
||||
}
|
||||
if (!hasItemLimitCount && itemLimitCount?.Current > itemLimitCount?.Max) return false;
|
||||
|
||||
var presetAndMods = _itemHelper.ReplaceIDs(_cloner.Clone(chosenPreset.Items));
|
||||
_itemHelper.RemapRootItemId(presetAndMods);
|
||||
// Add chosen preset tpl to result array
|
||||
foreach (var item in presetAndMods) {
|
||||
result.Add(item);
|
||||
}
|
||||
foreach (var item in presetAndMods) result.Add(item);
|
||||
|
||||
if (itemLimitCount is not null) {
|
||||
if (itemLimitCount is not null)
|
||||
// Increment item count as item has been chosen and its inside itemLimitCount dictionary
|
||||
itemLimitCount.Current++;
|
||||
}
|
||||
|
||||
// Item added okay
|
||||
return true;
|
||||
@@ -430,7 +425,8 @@ public class LootGenerator(
|
||||
|
||||
// Get itemDb details of weapon
|
||||
var weaponDetailsDb = _itemHelper.GetItem(chosenWeaponTpl);
|
||||
if (!weaponDetailsDb.Key) {
|
||||
if (!weaponDetailsDb.Key)
|
||||
{
|
||||
_logger.Error(
|
||||
_localisationService.GetText("loot-non_item_picked_as_sealed_weapon_crate_reward", chosenWeaponTpl)
|
||||
);
|
||||
@@ -444,7 +440,8 @@ public class LootGenerator(
|
||||
: _randomUtil.GetArrayValue(_presetHelper.GetPresets(chosenWeaponTpl));
|
||||
|
||||
// No default preset found for weapon, choose a random one
|
||||
if (chosenWeaponPreset is null) {
|
||||
if (chosenWeaponPreset is null)
|
||||
{
|
||||
_logger.Warning(
|
||||
_localisationService.GetText("loot-default_preset_not_found_using_random", chosenWeaponTpl)
|
||||
);
|
||||
@@ -460,11 +457,12 @@ public class LootGenerator(
|
||||
|
||||
// Get a random collection of weapon mods related to chosen weawpon and add them to result array
|
||||
var linkedItemsToWeapon = _ragfairLinkedItemService.GetLinkedDbItems(chosenWeaponTpl);
|
||||
itemsToReturn.AddRange(GetSealedContainerWeaponModRewards(containerSettings, linkedItemsToWeapon, chosenWeaponPreset)
|
||||
itemsToReturn.AddRange(
|
||||
GetSealedContainerWeaponModRewards(containerSettings, linkedItemsToWeapon, chosenWeaponPreset)
|
||||
);
|
||||
|
||||
// Handle non-weapon mod reward types
|
||||
itemsToReturn.AddRange((GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value)));
|
||||
itemsToReturn.AddRange(GetSealedContainerNonWeaponModRewards(containerSettings, weaponDetailsDb.Value));
|
||||
|
||||
return itemsToReturn;
|
||||
}
|
||||
@@ -480,35 +478,39 @@ public class LootGenerator(
|
||||
{
|
||||
List<List<Item>> rewards = [];
|
||||
|
||||
foreach (var (rewardKey,settings) in containerSettings.RewardTypeLimits) {
|
||||
foreach (var (rewardKey, settings) in containerSettings.RewardTypeLimits)
|
||||
{
|
||||
var rewardCount = _randomUtil.GetDouble(settings.Min.Value, settings.Max.Value);
|
||||
|
||||
if (rewardCount == 0) {
|
||||
continue;
|
||||
}
|
||||
if (rewardCount == 0) continue;
|
||||
|
||||
// Edge case - ammo boxes
|
||||
if (rewardKey == BaseClasses.AMMO_BOX) {
|
||||
if (rewardKey == BaseClasses.AMMO_BOX)
|
||||
{
|
||||
// Get ammoboxes from db
|
||||
var ammoBoxesDetails = containerSettings.AmmoBoxWhitelist.Select((tpl) => {
|
||||
var itemDetails = _itemHelper.GetItem(tpl);
|
||||
return itemDetails.Value;
|
||||
});
|
||||
var ammoBoxesDetails = containerSettings.AmmoBoxWhitelist.Select(
|
||||
(tpl) =>
|
||||
{
|
||||
var itemDetails = _itemHelper.GetItem(tpl);
|
||||
return itemDetails.Value;
|
||||
}
|
||||
);
|
||||
|
||||
// Need to find boxes that matches weapons caliber
|
||||
var weaponCaliber = weaponDetailsDb.Properties.AmmoCaliber;
|
||||
var ammoBoxesMatchingCaliber = ammoBoxesDetails.Where((x) =>
|
||||
x.Properties.AmmoCaliber == weaponCaliber);
|
||||
if (!ammoBoxesMatchingCaliber.Any()) {
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"No ammo box with caliber {weaponCaliber} found, skipping");
|
||||
}
|
||||
var ammoBoxesMatchingCaliber = ammoBoxesDetails.Where(
|
||||
(x) =>
|
||||
x.Properties.AmmoCaliber == weaponCaliber
|
||||
);
|
||||
if (!ammoBoxesMatchingCaliber.Any())
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"No ammo box with caliber {weaponCaliber} found, skipping");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var index = 0; index < rewardCount; index++) {
|
||||
for (var index = 0; index < rewardCount; index++)
|
||||
{
|
||||
var chosenAmmoBox = _randomUtil.GetArrayValue(ammoBoxesMatchingCaliber);
|
||||
var ammoBoxReward = new List<Item> { new() { Id = _hashUtil.Generate(), Template = chosenAmmoBox.Id } };
|
||||
_itemHelper.AddCartridgesToAmmoBox(ammoBoxReward, chosenAmmoBox);
|
||||
@@ -519,25 +521,25 @@ public class LootGenerator(
|
||||
}
|
||||
|
||||
// Get all items of the desired type + not quest items + not globally blacklisted
|
||||
var rewardItemPool = _databaseService.GetItems().Values.Where(
|
||||
(item) =>
|
||||
item.Parent == rewardKey &&
|
||||
item.Type.ToLower() == "item" &&
|
||||
_itemFilterService.IsItemBlacklisted(item.Id) &&
|
||||
!(containerSettings.AllowBossItems || _itemFilterService.IsBossItem(item.Id)) &&
|
||||
item.Properties.QuestItem is null
|
||||
);
|
||||
var rewardItemPool = _databaseService.GetItems()
|
||||
.Values.Where(
|
||||
(item) =>
|
||||
item.Parent == rewardKey &&
|
||||
item.Type.ToLower() == "item" &&
|
||||
_itemFilterService.IsItemBlacklisted(item.Id) &&
|
||||
!(containerSettings.AllowBossItems || _itemFilterService.IsBossItem(item.Id)) &&
|
||||
item.Properties.QuestItem is null
|
||||
);
|
||||
|
||||
if (rewardItemPool.Count() == 0) {
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"No items with base type of {rewardKey} found, skipping");
|
||||
}
|
||||
if (rewardItemPool.Count() == 0)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"No items with base type of {rewardKey} found, skipping");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var index = 0; index < rewardCount; index++) {
|
||||
for (var index = 0; index < rewardCount; index++)
|
||||
{
|
||||
// Choose a random item from pool
|
||||
var chosenRewardItem = _randomUtil.GetArrayValue(rewardItemPool);
|
||||
var rewardItem = new List<Item> { new() { Id = _hashUtil.Generate(), Template = chosenRewardItem.Id } };
|
||||
@@ -560,31 +562,30 @@ public class LootGenerator(
|
||||
Preset chosenWeaponPreset)
|
||||
{
|
||||
List<List<Item>> modRewards = [];
|
||||
|
||||
foreach (var (rewardKey,settings) in containerSettings.WeaponModRewardLimits) {
|
||||
|
||||
foreach (var (rewardKey, settings) in containerSettings.WeaponModRewardLimits)
|
||||
{
|
||||
var rewardCount = _randomUtil.GetDouble(settings.Min.Value, settings.Max.Value);
|
||||
|
||||
|
||||
// Nothing to add, skip reward type
|
||||
if (rewardCount == 0) {
|
||||
continue;
|
||||
}
|
||||
if (rewardCount == 0) continue;
|
||||
|
||||
// Get items that fulfil reward type criteria from items that fit on gun
|
||||
var relatedItems = linkedItemsToWeapon?.Where(
|
||||
(item) => item?.Parent == rewardKey && !_itemFilterService.IsItemBlacklisted(item.Id)
|
||||
);
|
||||
if (relatedItems is null || relatedItems.Count() == 0) {
|
||||
if(_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
if (relatedItems is null || relatedItems.Count() == 0)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
_logger.Debug(
|
||||
$"No items found to fulfil reward type: {rewardKey} for weapon: {chosenWeaponPreset.Name}, skipping type"
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find a random item of the desired type and add as reward
|
||||
for (var index = 0; index < rewardCount; index++) {
|
||||
for (var index = 0; index < rewardCount; index++)
|
||||
{
|
||||
var chosenItem = _randomUtil.DrawRandomFromList(relatedItems.ToList());
|
||||
var reward = new List<Item> { new() { Id = _hashUtil.Generate(), Template = chosenItem[0].Id } };
|
||||
|
||||
@@ -605,11 +606,13 @@ public class LootGenerator(
|
||||
List<List<Item>> itemsToReturn = [];
|
||||
|
||||
// Get random items and add to newItemRequest
|
||||
for (var index = 0; index < rewardContainerDetails.RewardCount; index++) {
|
||||
for (var index = 0; index < rewardContainerDetails.RewardCount; index++)
|
||||
{
|
||||
// Pick random reward from pool, add to request object
|
||||
var chosenRewardItemTpl = PickRewardItem(rewardContainerDetails);
|
||||
|
||||
if (_presetHelper.HasPreset(chosenRewardItemTpl)) {
|
||||
if (_presetHelper.HasPreset(chosenRewardItemTpl))
|
||||
{
|
||||
var preset = _presetHelper.GetDefaultPreset(chosenRewardItemTpl);
|
||||
|
||||
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
|
||||
@@ -621,7 +624,7 @@ public class LootGenerator(
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Item> rewardItem = [new Item { Id = _hashUtil.Generate(), Template = chosenRewardItemTpl }];
|
||||
List<Item> rewardItem = [new() { Id = _hashUtil.Generate(), Template = chosenRewardItemTpl }];
|
||||
itemsToReturn.Add(rewardItem);
|
||||
}
|
||||
|
||||
@@ -635,16 +638,22 @@ public class LootGenerator(
|
||||
/// <returns>Single tpl</returns>
|
||||
protected string PickRewardItem(RewardDetails rewardContainerDetails)
|
||||
{
|
||||
if (rewardContainerDetails.RewardTplPool is not null && rewardContainerDetails.RewardTplPool.Count > 0) {
|
||||
if (rewardContainerDetails.RewardTplPool is not null && rewardContainerDetails.RewardTplPool.Count > 0)
|
||||
return _weightedRandomHelper.GetWeightedValue(rewardContainerDetails.RewardTplPool);
|
||||
}
|
||||
|
||||
return _randomUtil.GetArrayValue(
|
||||
GetItemRewardPool([], rewardContainerDetails.RewardTypePool, true, true).ItemPool.Select(
|
||||
(item) => item.Id
|
||||
)
|
||||
GetItemRewardPool([], rewardContainerDetails.RewardTypePool, true, true)
|
||||
.ItemPool.Select(
|
||||
(item) => item.Id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public record ItemRewardPoolResults
|
||||
{
|
||||
public List<TemplateItem> ItemPool { get; set; }
|
||||
public HashSet<string> Blacklist { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemLimit
|
||||
|
||||
@@ -12,19 +12,19 @@ namespace Core.Generators;
|
||||
[Injectable]
|
||||
public class PMCLootGenerator
|
||||
{
|
||||
private readonly ISptLogger<PMCLootGenerator> _logger;
|
||||
private readonly ConfigServer _configServer;
|
||||
private readonly DatabaseService _databaseService;
|
||||
private readonly ItemHelper _itemHelper;
|
||||
private readonly ItemFilterService _itemFilterService;
|
||||
private readonly ItemHelper _itemHelper;
|
||||
private readonly ISptLogger<PMCLootGenerator> _logger;
|
||||
private readonly PmcConfig _pmcConfig;
|
||||
private readonly RagfairPriceService _ragfairPriceService;
|
||||
private readonly SeasonalEventService _seasonalEventService;
|
||||
private readonly WeightedRandomHelper _weightedRandomHelper;
|
||||
private readonly ConfigServer _configServer;
|
||||
|
||||
private Dictionary<string, double>? _backpackLootPool;
|
||||
private Dictionary<string, double>? _pocketLootPool;
|
||||
private Dictionary<string, double>? _vestLootPool;
|
||||
private readonly PmcConfig _pmcConfig;
|
||||
|
||||
public PMCLootGenerator(
|
||||
ISptLogger<PMCLootGenerator> logger,
|
||||
@@ -35,7 +35,7 @@ public class PMCLootGenerator
|
||||
SeasonalEventService seasonalEventService,
|
||||
WeightedRandomHelper weightedRandomHelper,
|
||||
ConfigServer configServer
|
||||
)
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_databaseService = databaseService;
|
||||
@@ -78,7 +78,6 @@ public class PMCLootGenerator
|
||||
);
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd)
|
||||
{
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
@@ -90,15 +89,12 @@ public class PMCLootGenerator
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_pocketLootPool[tpl] = price ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _pocketLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _pocketLootPool)
|
||||
{
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_pocketLootPool[key] = Math.Round((1 / _pocketLootPool[key]) * highestPrice);
|
||||
}
|
||||
_pocketLootPool[key] = Math.Round(1 / _pocketLootPool[key] * highestPrice);
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_pocketLootPool);
|
||||
}
|
||||
@@ -109,22 +105,10 @@ public class PMCLootGenerator
|
||||
private HashSet<string> GetLootBlacklist()
|
||||
{
|
||||
var blacklist = new HashSet<string>();
|
||||
foreach (var blacklistedItem in _pmcConfig.PocketLoot.Blacklist)
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _pmcConfig.GlobalLootBlacklist)
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _itemFilterService.GetBlacklistedItems())
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _seasonalEventService.GetInactiveSeasonalEventItems())
|
||||
{
|
||||
blacklist.Add(blacklistedItem);
|
||||
}
|
||||
foreach (var blacklistedItem in _pmcConfig.PocketLoot.Blacklist) blacklist.Add(blacklistedItem);
|
||||
foreach (var blacklistedItem in _pmcConfig.GlobalLootBlacklist) blacklist.Add(blacklistedItem);
|
||||
foreach (var blacklistedItem in _itemFilterService.GetBlacklistedItems()) blacklist.Add(blacklistedItem);
|
||||
foreach (var blacklistedItem in _seasonalEventService.GetInactiveSeasonalEventItems()) blacklist.Add(blacklistedItem);
|
||||
|
||||
return blacklist;
|
||||
}
|
||||
@@ -154,10 +138,10 @@ public class PMCLootGenerator
|
||||
_itemHelper.IsValidItem(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent) &&
|
||||
ItemFitsInto2By2Slot(item.Value));
|
||||
ItemFitsInto2By2Slot(item.Value)
|
||||
);
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd)
|
||||
{
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
@@ -169,15 +153,12 @@ public class PMCLootGenerator
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_vestLootPool[tpl] = price ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _vestLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _vestLootPool)
|
||||
{
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_vestLootPool[key] = Math.Round((1 / _vestLootPool[key]) * highestPrice);
|
||||
}
|
||||
_vestLootPool[key] = Math.Round(1 / _vestLootPool[key] * highestPrice);
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_vestLootPool);
|
||||
}
|
||||
@@ -234,10 +215,11 @@ public class PMCLootGenerator
|
||||
(item) =>
|
||||
allowedItemTypeWhitelist.Contains(item.Value.Parent) &&
|
||||
_itemHelper.IsValidItem(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent));
|
||||
!blacklist.Contains(item.Value.Id) &&
|
||||
!blacklist.Contains(item.Value.Parent)
|
||||
);
|
||||
|
||||
foreach (var (tpl, template) in itemsToAdd) {
|
||||
foreach (var (tpl, template) in itemsToAdd)
|
||||
// If pmc has price override, use that. Otherwise, use flea price
|
||||
if (pmcPriceOverrides.ContainsKey(tpl))
|
||||
{
|
||||
@@ -249,14 +231,12 @@ public class PMCLootGenerator
|
||||
var price = _ragfairPriceService.GetDynamicItemPrice(tpl, Money.ROUBLES);
|
||||
_backpackLootPool[tpl] = price ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
var highestPrice = _backpackLootPool.Max(price => price.Value);
|
||||
foreach (var (key, _) in _backpackLootPool) {
|
||||
foreach (var (key, _) in _backpackLootPool)
|
||||
// Invert price so cheapest has a larger weight
|
||||
// Times by highest price so most expensive item has weight of 1
|
||||
_backpackLootPool[key] = Math.Round((1 / _backpackLootPool[key]) * highestPrice);
|
||||
}
|
||||
_backpackLootPool[key] = Math.Round(1 / _backpackLootPool[key] * highestPrice);
|
||||
|
||||
_weightedRandomHelper.ReduceWeightValues(_backpackLootPool);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using SptCommon.Annotations;
|
||||
using Core.Helpers;
|
||||
using Core.Models.Eft.Common;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Eft.Notes;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Models.Utils;
|
||||
@@ -9,6 +10,7 @@ using Core.Servers;
|
||||
using Core.Services;
|
||||
using Core.Utils;
|
||||
using Core.Utils.Cloners;
|
||||
using Core.Utils.Json;
|
||||
using LogLevel = Core.Models.Spt.Logging.LogLevel;
|
||||
|
||||
|
||||
@@ -53,14 +55,9 @@ public class PlayerScavGenerator(
|
||||
|
||||
// use karma level to get correct karmaSettings
|
||||
if (!_playerScavConfig.KarmaLevel.TryGetValue(scavKarmaLevel.ToString(), out var playerScavKarmaSettings))
|
||||
{
|
||||
_logger.Error(_localisationService.GetText("scav-missing_karma_settings", scavKarmaLevel));
|
||||
}
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Generated player scav loadout with karma level {scavKarmaLevel}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Generated player scav loadout with karma level {scavKarmaLevel}");
|
||||
|
||||
// Edit baseBotNode values
|
||||
var baseBotNode = ConstructBotBaseTemplate(playerScavKarmaSettings.BotTypeForLoot);
|
||||
@@ -81,7 +78,7 @@ public class PlayerScavGenerator(
|
||||
scavData.Savage = null;
|
||||
scavData.Aid = pmcDataClone.Aid;
|
||||
scavData.TradersInfo = pmcDataClone.TradersInfo;
|
||||
scavData.Info.Settings = new();
|
||||
scavData.Info.Settings = new BotInfoSettings();
|
||||
scavData.Info.Bans = [];
|
||||
scavData.Info.RegistrationDate = pmcDataClone.Info.RegistrationDate;
|
||||
scavData.Info.GameVersion = pmcDataClone.Info.GameVersion;
|
||||
@@ -98,10 +95,10 @@ public class PlayerScavGenerator(
|
||||
scavData.Info.Level = GetScavLevel(existingScavDataClone);
|
||||
scavData.Info.Experience = GetScavExperience(existingScavDataClone);
|
||||
scavData.Quests = existingScavDataClone.Quests ?? [];
|
||||
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? new();
|
||||
scavData.Notes = existingScavDataClone.Notes ?? new() { DataNotes = new() };
|
||||
scavData.WishList = existingScavDataClone.WishList ?? new(new(), new());
|
||||
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? new();
|
||||
scavData.TaskConditionCounters = existingScavDataClone.TaskConditionCounters ?? new Dictionary<string, TaskConditionCounter>();
|
||||
scavData.Notes = existingScavDataClone.Notes ?? new Notes { DataNotes = new List<Note>() };
|
||||
scavData.WishList = existingScavDataClone.WishList ?? new DictionaryOrList<string, int>(new Dictionary<string, int>(), new List<int>());
|
||||
scavData.Encyclopedia = pmcDataClone.Encyclopedia ?? new Dictionary<string, bool>();
|
||||
|
||||
// Add additional items to player scav as loot
|
||||
AddAdditionalLootToPlayerScavContainers(
|
||||
@@ -168,12 +165,8 @@ public class PlayerScavGenerator(
|
||||
);
|
||||
|
||||
if (result != ItemAddedResult.SUCCESS)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to add keycard to bot. Reason: {result.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,17 +225,12 @@ public class PlayerScavGenerator(
|
||||
foreach (var equipmentKvP in karmaSettings.Modifiers.Equipment)
|
||||
{
|
||||
// Adjustment value zero, nothing to do
|
||||
if (equipmentKvP.Value == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (equipmentKvP.Value == 0) continue;
|
||||
|
||||
// Try add new key with value
|
||||
if (!baseBotNode.BotChances.EquipmentChances.TryAdd(equipmentKvP.Key, equipmentKvP.Value))
|
||||
{
|
||||
// Unable to add new, update existing
|
||||
baseBotNode.BotChances.EquipmentChances[equipmentKvP.Key] += equipmentKvP.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust mod chance values
|
||||
@@ -255,8 +243,9 @@ public class PlayerScavGenerator(
|
||||
{
|
||||
baseBotNode.BotChances.WeaponModsChances.TryAdd(modKvP.Key, 0);
|
||||
baseBotNode.BotChances.WeaponModsChances[modKvP.Key] += value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
// Adjust item spawn quantity values
|
||||
@@ -271,10 +260,7 @@ public class PlayerScavGenerator(
|
||||
foreach (var equipmentBlacklistKvP in karmaSettings.EquipmentBlacklist)
|
||||
{
|
||||
baseBotNode.BotInventory.Equipment.TryGetValue(equipmentBlacklistKvP.Key, out var equipmentDict);
|
||||
foreach (var itemToRemove in equipmentBlacklistKvP.Value)
|
||||
{
|
||||
equipmentDict.Remove(itemToRemove);
|
||||
}
|
||||
foreach (var itemToRemove in equipmentBlacklistKvP.Value) equipmentDict.Remove(itemToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +274,10 @@ public class PlayerScavGenerator(
|
||||
|
||||
protected Skills GetDefaultScavSkills()
|
||||
{
|
||||
return new()
|
||||
return new Skills
|
||||
{
|
||||
Common = new(),
|
||||
Mastering = new(),
|
||||
Common = new List<BaseSkill>(),
|
||||
Mastering = new List<BaseSkill>(),
|
||||
Points = 0
|
||||
};
|
||||
}
|
||||
@@ -337,14 +323,10 @@ public class PlayerScavGenerator(
|
||||
var modifier = 1;
|
||||
|
||||
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 += (int)(bonus?.Value ?? 1) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
var fenceInfo = _fenceService.GetFenceInfo(pmcData);
|
||||
modifier *= (int)(fenceInfo.SavageCooldownModifier ?? 1);
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RagfairAssortGenerator(
|
||||
BaseClasses.INVENTORY,
|
||||
BaseClasses.STATIONARY_CONTAINER,
|
||||
BaseClasses.POCKETS,
|
||||
BaseClasses.BUILT_IN_INSERTS,
|
||||
BaseClasses.BUILT_IN_INSERTS
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -42,10 +42,7 @@ public class RagfairAssortGenerator(
|
||||
*/
|
||||
public List<List<Item>> GetAssortItems()
|
||||
{
|
||||
if (!AssortsAreGenerated())
|
||||
{
|
||||
generatedAssortItems = GenerateRagfairAssortItems();
|
||||
}
|
||||
if (!AssortsAreGenerated()) generatedAssortItems = GenerateRagfairAssortItems();
|
||||
|
||||
return generatedAssortItems;
|
||||
}
|
||||
@@ -95,10 +92,7 @@ public class RagfairAssortGenerator(
|
||||
|
||||
foreach (var item in dbItemsClone)
|
||||
{
|
||||
if (!itemHelper.IsValidItem(item.Id, ragfairItemInvalidBaseTypes))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!itemHelper.IsValidItem(item.Id, ragfairItemInvalidBaseTypes)) continue;
|
||||
|
||||
// Skip seasonal items when not in-season
|
||||
if (
|
||||
@@ -106,15 +100,11 @@ public class RagfairAssortGenerator(
|
||||
!seasonalEventActive &&
|
||||
seasonalItemTplBlacklist.Contains(item.Id)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processedArmorItems.Contains(item.Id))
|
||||
{
|
||||
// Already processed
|
||||
continue;
|
||||
}
|
||||
|
||||
var ragfairAssort = CreateRagfairAssortRootItem(
|
||||
item.Id,
|
||||
|
||||
@@ -43,14 +43,15 @@ public class RagfairOfferGenerator(
|
||||
ICloner cloner
|
||||
)
|
||||
{
|
||||
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
|
||||
protected TraderConfig traderConfig = configServer.GetConfig<TraderConfig>();
|
||||
protected BotConfig botConfig = configServer.GetConfig<BotConfig>();
|
||||
protected List<TplWithFleaPrice>? allowedFleaPriceItemsForBarter;
|
||||
protected BotConfig botConfig = configServer.GetConfig<BotConfig>();
|
||||
|
||||
/** Internal counter to ensure each offer created has a unique value for its intId property */
|
||||
protected int offerCounter = 0;
|
||||
|
||||
|
||||
protected RagfairConfig ragfairConfig = configServer.GetConfig<RagfairConfig>();
|
||||
protected TraderConfig traderConfig = configServer.GetConfig<TraderConfig>();
|
||||
|
||||
/**
|
||||
* Create a flea offer and store it in the Ragfair server offers array
|
||||
* @param userID Owner of the offer
|
||||
@@ -97,21 +98,27 @@ public class RagfairOfferGenerator(
|
||||
{
|
||||
var isTrader = ragfairServerHelper.IsTrader(userID);
|
||||
|
||||
var offerRequirements = barterScheme.Select((barter) => {
|
||||
var offerRequirement = new OfferRequirement{
|
||||
Template = barter.Template,
|
||||
Count = Math.Round((double) barter.Count, 2),
|
||||
OnlyFunctional = barter.OnlyFunctional ?? false,
|
||||
};
|
||||
var offerRequirements = barterScheme.Select(
|
||||
(barter) =>
|
||||
{
|
||||
var offerRequirement = new OfferRequirement
|
||||
{
|
||||
Template = barter.Template,
|
||||
Count = Math.Round((double)barter.Count, 2),
|
||||
OnlyFunctional = barter.OnlyFunctional ?? false
|
||||
};
|
||||
|
||||
// Dogtags define level and side
|
||||
if (barter.Level != null) {
|
||||
offerRequirement.Level = barter.Level;
|
||||
offerRequirement.Side = barter.Side;
|
||||
}
|
||||
// Dogtags define level and side
|
||||
if (barter.Level != null)
|
||||
{
|
||||
offerRequirement.Level = barter.Level;
|
||||
offerRequirement.Side = barter.Side;
|
||||
}
|
||||
|
||||
return offerRequirement;
|
||||
}).ToList();
|
||||
return offerRequirement;
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
|
||||
// Clone to avoid modifying original array
|
||||
var itemsClone = cloner.Clone(items);
|
||||
@@ -119,28 +126,28 @@ public class RagfairOfferGenerator(
|
||||
|
||||
// Hydrate ammo boxes with cartridges + ensure only 1 item is present (ammo box)
|
||||
// On offer refresh don't re-add cartridges to ammo box that already has cartridges
|
||||
if (itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX) && itemsClone.Count == 1) {
|
||||
if (itemHelper.IsOfBaseclass(itemsClone[0].Template, BaseClasses.AMMO_BOX) && itemsClone.Count == 1)
|
||||
itemHelper.AddCartridgesToAmmoBox(itemsClone, itemHelper.GetItem(items[0].Template).Value);
|
||||
}
|
||||
|
||||
var roubleListingPrice = Math.Round((double) ConvertOfferRequirementsIntoRoubles(offerRequirements));
|
||||
var roubleListingPrice = Math.Round((double)ConvertOfferRequirementsIntoRoubles(offerRequirements));
|
||||
var singleItemListingPrice = isPackOffer ? roubleListingPrice / itemStackCount : roubleListingPrice;
|
||||
|
||||
var offer = new RagfairOffer {
|
||||
Id= hashUtil.Generate(),
|
||||
InternalId= offerCounter,
|
||||
User= CreateUserDataForFleaOffer(userID, isTrader),
|
||||
Root= items[0].Id,
|
||||
Items= itemsClone,
|
||||
ItemsCost= Math.Round((double) handbookHelper.GetTemplatePrice(items[0].Template)), // Handbook price
|
||||
Requirements= offerRequirements,
|
||||
RequirementsCost= Math.Round(singleItemListingPrice),
|
||||
SummaryCost= roubleListingPrice,
|
||||
StartTime= time,
|
||||
EndTime= GetOfferEndTime(userID, time),
|
||||
LoyaltyLevel= loyalLevel,
|
||||
SellInOnePiece= isPackOffer,
|
||||
Locked= false,
|
||||
var offer = new RagfairOffer
|
||||
{
|
||||
Id = hashUtil.Generate(),
|
||||
InternalId = offerCounter,
|
||||
User = CreateUserDataForFleaOffer(userID, isTrader),
|
||||
Root = items[0].Id,
|
||||
Items = itemsClone,
|
||||
ItemsCost = Math.Round((double)handbookHelper.GetTemplatePrice(items[0].Template)), // Handbook price
|
||||
Requirements = offerRequirements,
|
||||
RequirementsCost = Math.Round(singleItemListingPrice),
|
||||
SummaryCost = roubleListingPrice,
|
||||
StartTime = time,
|
||||
EndTime = GetOfferEndTime(userID, time),
|
||||
LoyaltyLevel = loyalLevel,
|
||||
SellInOnePiece = isPackOffer,
|
||||
Locked = false
|
||||
};
|
||||
|
||||
offerCounter++;
|
||||
@@ -157,40 +164,43 @@ public class RagfairOfferGenerator(
|
||||
protected RagfairOfferUser CreateUserDataForFleaOffer(string userID, bool isTrader)
|
||||
{
|
||||
// Trader offer
|
||||
if (isTrader) {
|
||||
return new RagfairOfferUser(){
|
||||
if (isTrader)
|
||||
return new RagfairOfferUser()
|
||||
{
|
||||
Id = userID,
|
||||
MemberType = MemberCategory.Trader
|
||||
};
|
||||
}
|
||||
|
||||
var isPlayerOffer = profileHelper.IsPlayer(userID);
|
||||
if (isPlayerOffer) {
|
||||
if (isPlayerOffer)
|
||||
{
|
||||
var playerProfile = profileHelper.GetPmcProfile(userID);
|
||||
return new RagfairOfferUser {
|
||||
Id= playerProfile.Id,
|
||||
MemberType= playerProfile.Info.MemberCategory,
|
||||
SelectedMemberCategory= playerProfile.Info.SelectedMemberCategory,
|
||||
Nickname= playerProfile.Info.Nickname,
|
||||
Rating= playerProfile.RagfairInfo.Rating ?? 0,
|
||||
IsRatingGrowing= playerProfile.RagfairInfo.IsRatingGrowing,
|
||||
Avatar= null,
|
||||
Aid= playerProfile.Aid,
|
||||
return new RagfairOfferUser
|
||||
{
|
||||
Id = playerProfile.Id,
|
||||
MemberType = playerProfile.Info.MemberCategory,
|
||||
SelectedMemberCategory = playerProfile.Info.SelectedMemberCategory,
|
||||
Nickname = playerProfile.Info.Nickname,
|
||||
Rating = playerProfile.RagfairInfo.Rating ?? 0,
|
||||
IsRatingGrowing = playerProfile.RagfairInfo.IsRatingGrowing,
|
||||
Avatar = null,
|
||||
Aid = playerProfile.Aid
|
||||
};
|
||||
}
|
||||
|
||||
// Fake pmc offer
|
||||
return new RagfairOfferUser(){
|
||||
Id= userID,
|
||||
MemberType= MemberCategory.Default,
|
||||
Nickname= botHelper.GetPmcNicknameOfMaxLength(botConfig.BotNameLengthLimit),
|
||||
Rating= randomUtil.GetDouble(
|
||||
(double) ragfairConfig.Dynamic.Rating.Min,
|
||||
(double) ragfairConfig.Dynamic.Rating.Max
|
||||
return new RagfairOfferUser()
|
||||
{
|
||||
Id = userID,
|
||||
MemberType = MemberCategory.Default,
|
||||
Nickname = botHelper.GetPmcNicknameOfMaxLength(botConfig.BotNameLengthLimit),
|
||||
Rating = randomUtil.GetDouble(
|
||||
(double)ragfairConfig.Dynamic.Rating.Min,
|
||||
(double)ragfairConfig.Dynamic.Rating.Max
|
||||
),
|
||||
IsRatingGrowing= randomUtil.GetBool(),
|
||||
Avatar= null,
|
||||
Aid= hashUtil.GenerateAccountId(),
|
||||
IsRatingGrowing = randomUtil.GetBool(),
|
||||
Avatar = null,
|
||||
Aid = hashUtil.GenerateAccountId()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -199,13 +209,13 @@ public class RagfairOfferGenerator(
|
||||
* @param offerRequirements barter requirements for offer
|
||||
* @returns rouble cost of offer
|
||||
*/
|
||||
protected int ConvertOfferRequirementsIntoRoubles(List<OfferRequirement> offerRequirements) {
|
||||
protected int ConvertOfferRequirementsIntoRoubles(List<OfferRequirement> offerRequirements)
|
||||
{
|
||||
var roublePrice = 0;
|
||||
foreach (var requirement in offerRequirements) {
|
||||
roublePrice += (int) (paymentHelper.IsMoneyTpl(requirement.Template)
|
||||
? Math.Round((double) CalculateRoublePrice((int) requirement.Count, requirement.Template))
|
||||
foreach (var requirement in offerRequirements)
|
||||
roublePrice += (int)(paymentHelper.IsMoneyTpl(requirement.Template)
|
||||
? Math.Round((double)CalculateRoublePrice((int)requirement.Count, requirement.Template))
|
||||
: ragfairPriceService.GetFleaPriceForItem(requirement.Template) * requirement.Count); // get flea price for barter offer items
|
||||
}
|
||||
|
||||
return roublePrice;
|
||||
}
|
||||
@@ -218,9 +228,7 @@ public class RagfairOfferGenerator(
|
||||
*/
|
||||
protected string GetAvatarUrl(bool isTrader, string userId)
|
||||
{
|
||||
if (isTrader) {
|
||||
return databaseService.GetTrader(userId).Base.Avatar;
|
||||
}
|
||||
if (isTrader) return databaseService.GetTrader(userId).Base.Avatar;
|
||||
|
||||
return "/files/trader/avatar/unknown.jpg";
|
||||
}
|
||||
@@ -233,9 +241,7 @@ public class RagfairOfferGenerator(
|
||||
*/
|
||||
protected int CalculateRoublePrice(int currencyCount, string currencyType)
|
||||
{
|
||||
if (currencyType == Money.ROUBLES) {
|
||||
return currencyCount;
|
||||
}
|
||||
if (currencyType == Money.ROUBLES) return currencyCount;
|
||||
|
||||
return handbookHelper.InRUB(currencyCount, currencyType);
|
||||
}
|
||||
@@ -247,9 +253,7 @@ public class RagfairOfferGenerator(
|
||||
*/
|
||||
protected string GetTraderId(string userId)
|
||||
{
|
||||
if (profileHelper.IsPlayer(userId)) {
|
||||
return saveServer.GetProfile(userId).CharacterData.PmcData.Id;
|
||||
}
|
||||
if (profileHelper.IsPlayer(userId)) return saveServer.GetProfile(userId).CharacterData.PmcData.Id;
|
||||
|
||||
return userId;
|
||||
}
|
||||
@@ -261,18 +265,16 @@ public class RagfairOfferGenerator(
|
||||
*/
|
||||
protected double? GetRating(string userId)
|
||||
{
|
||||
if (profileHelper.IsPlayer(userId)) {
|
||||
if (profileHelper.IsPlayer(userId))
|
||||
// Player offer
|
||||
return saveServer.GetProfile(userId).CharacterData?.PmcData?.RagfairInfo?.Rating;
|
||||
}
|
||||
|
||||
if (ragfairServerHelper.IsTrader(userId)) {
|
||||
if (ragfairServerHelper.IsTrader(userId))
|
||||
// Trader offer
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Generated pmc offer
|
||||
return randomUtil.GetDouble((double) ragfairConfig.Dynamic.Rating.Min, (double) ragfairConfig.Dynamic.Rating.Max);
|
||||
return randomUtil.GetDouble((double)ragfairConfig.Dynamic.Rating.Min, (double)ragfairConfig.Dynamic.Rating.Max);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,15 +285,12 @@ public class RagfairOfferGenerator(
|
||||
protected bool GetRatingGrowing(string userID)
|
||||
{
|
||||
if (profileHelper.IsPlayer(userID))
|
||||
{
|
||||
// player offer
|
||||
return saveServer.GetProfile(userID).CharacterData?.PmcData?.RagfairInfo?.IsRatingGrowing ?? false;
|
||||
}
|
||||
|
||||
if (ragfairServerHelper.IsTrader(userID)) {
|
||||
if (ragfairServerHelper.IsTrader(userID))
|
||||
// trader offer
|
||||
return true;
|
||||
}
|
||||
|
||||
// generated offer
|
||||
// 50/50 growing/falling
|
||||
@@ -306,19 +305,21 @@ public class RagfairOfferGenerator(
|
||||
*/
|
||||
protected long GetOfferEndTime(string userID, long time)
|
||||
{
|
||||
if (profileHelper.IsPlayer(userID)) {
|
||||
if (profileHelper.IsPlayer(userID))
|
||||
{
|
||||
// Player offer = current time + offerDurationTimeInHour;
|
||||
var offerDurationTimeHours = databaseService.GetGlobals().Configuration.RagFair.OfferDurationTimeInHour;
|
||||
return (long) (timeUtil.GetTimeStamp() + Math.Round((double) offerDurationTimeHours * TimeUtil.OneHourAsSeconds));
|
||||
return (long)(timeUtil.GetTimeStamp() + Math.Round((double)offerDurationTimeHours * TimeUtil.OneHourAsSeconds));
|
||||
}
|
||||
|
||||
if (ragfairServerHelper.IsTrader(userID)) {
|
||||
if (ragfairServerHelper.IsTrader(userID))
|
||||
// Trader offer
|
||||
return (long) databaseService.GetTrader(userID).Base.NextResupply;
|
||||
}
|
||||
return (long)databaseService.GetTrader(userID).Base.NextResupply;
|
||||
|
||||
// Generated fake-player offer
|
||||
return (long) Math.Round((double) (time + randomUtil.GetInt((int) ragfairConfig.Dynamic.EndTimeSeconds.Min, (int) ragfairConfig.Dynamic.EndTimeSeconds.Max)));
|
||||
return (long)Math.Round(
|
||||
(double)(time + randomUtil.GetInt((int)ragfairConfig.Dynamic.EndTimeSeconds.Min, (int)ragfairConfig.Dynamic.EndTimeSeconds.Max))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,26 +336,18 @@ public class RagfairOfferGenerator(
|
||||
? expiredOffers ?? []
|
||||
: ragfairAssortGenerator.GetAssortItems();
|
||||
stopwatch.Stop();
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts");
|
||||
}
|
||||
if (logger.IsLogEnabled(LogLevel.Debug)) logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts");
|
||||
stopwatch.Restart();
|
||||
var tasks = new List<Task>();
|
||||
foreach (var assortItem in assortItemsToProcess)
|
||||
{
|
||||
tasks.Add(
|
||||
Task.Factory.StartNew(
|
||||
() => { CreateOffersFromAssort(assortItem, replacingExpiredOffers, ragfairConfig.Dynamic); }
|
||||
)
|
||||
);
|
||||
}
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
stopwatch.Stop();
|
||||
if (logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to CreateOffersFromAssort");
|
||||
}
|
||||
if (logger.IsLogEnabled(LogLevel.Debug)) logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to CreateOffersFromAssort");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,20 +365,17 @@ public class RagfairOfferGenerator(
|
||||
var isPreset = presetHelper.IsPreset(assortItemWithChildren[0].Upd.SptPresetId);
|
||||
|
||||
// Only perform checks on newly generated items, skip expired items being refreshed
|
||||
if (!(isExpiredOffer || ragfairServerHelper.IsItemValidRagfairItem(itemToSellDetails))) {
|
||||
return;
|
||||
}
|
||||
if (!(isExpiredOffer || ragfairServerHelper.IsItemValidRagfairItem(itemToSellDetails))) return;
|
||||
|
||||
// Armor presets can hold plates above the allowed flea level, remove if necessary
|
||||
if (isPreset && ragfairConfig.Dynamic.Blacklist.EnableBsgList) {
|
||||
if (isPreset && ragfairConfig.Dynamic.Blacklist.EnableBsgList)
|
||||
RemoveBannedPlatesFromPreset(assortItemWithChildren, ragfairConfig.Dynamic.Blacklist.ArmorPlate);
|
||||
}
|
||||
|
||||
// Get number of offers to create
|
||||
// Limit to 1 offer when processing expired - like-for-like replacement
|
||||
var offerCount = isExpiredOffer
|
||||
? 1
|
||||
: randomUtil.GetInt((int) config.OfferItemCount.Min, (int) config.OfferItemCount.Max);
|
||||
: randomUtil.GetInt((int)config.OfferItemCount.Min, (int)config.OfferItemCount.Max);
|
||||
|
||||
/* TODO: ???????
|
||||
if (ProgramStatics.DEBUG && !ProgramStatics.COMPILED) {
|
||||
@@ -393,7 +383,8 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
*/
|
||||
|
||||
for (var index = 0; index < offerCount; index++) {
|
||||
for (var index = 0; index < offerCount; index++)
|
||||
{
|
||||
// Clone the item so we don't have shared references and generate new item IDs
|
||||
var clonedAssort = cloner.Clone(assortItemWithChildren);
|
||||
itemHelper.ReparentItemAndChildren(clonedAssort[0], clonedAssort);
|
||||
@@ -417,26 +408,24 @@ public class RagfairOfferGenerator(
|
||||
ArmorPlateBlacklistSettings plateSettings
|
||||
)
|
||||
{
|
||||
if (!itemHelper.ArmorItemCanHoldMods(presetWithChildren[0].Template)) {
|
||||
if (!itemHelper.ArmorItemCanHoldMods(presetWithChildren[0].Template))
|
||||
// Cant hold armor inserts, skip
|
||||
return false;
|
||||
}
|
||||
|
||||
var plateSlots = presetWithChildren.Where((item) => itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLower())). ToList();
|
||||
if (plateSlots.Count == 0) {
|
||||
var plateSlots = presetWithChildren.Where((item) => itemHelper.GetRemovablePlateSlotIds().Contains(item.SlotId?.ToLower())).ToList();
|
||||
if (plateSlots.Count == 0)
|
||||
// Has no plate slots e.g. "front_plate", exit
|
||||
return false;
|
||||
}
|
||||
|
||||
var removedPlate = false;
|
||||
foreach (var plateSlot in plateSlots) {
|
||||
foreach (var plateSlot in plateSlots)
|
||||
{
|
||||
var plateDetails = itemHelper.GetItem(plateSlot.Template).Value;
|
||||
if (plateSettings.IgnoreSlots.Contains(plateSlot.SlotId.ToLower())) {
|
||||
continue;
|
||||
}
|
||||
if (plateSettings.IgnoreSlots.Contains(plateSlot.SlotId.ToLower())) continue;
|
||||
|
||||
var plateArmorLevel = plateDetails.Properties.ArmorClass ?? 0;
|
||||
if (plateArmorLevel > plateSettings.MaxProtectionLevel) {
|
||||
if (plateArmorLevel > plateSettings.MaxProtectionLevel)
|
||||
{
|
||||
presetWithChildren.Splice(presetWithChildren.IndexOf(plateSlot), 1);
|
||||
removedPlate = true;
|
||||
}
|
||||
@@ -492,15 +481,13 @@ public class RagfairOfferGenerator(
|
||||
// Latest first, to ensure we don't move later items off by 1 each time we remove an item below it
|
||||
var indexesToRemove = offerItemPlatesToRemove.Select(plateItem => itemWithChildren.IndexOf(plateItem))
|
||||
.ToList();
|
||||
foreach (var index in indexesToRemove.OrderByDescending(x => x))
|
||||
{
|
||||
itemWithChildren.RemoveAt(index);
|
||||
}
|
||||
foreach (var index in indexesToRemove.OrderByDescending(x => x)) itemWithChildren.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
List<BarterScheme> barterScheme;
|
||||
if (isPackOffer) {
|
||||
if (isPackOffer)
|
||||
{
|
||||
// Set pack size
|
||||
var stackSize = randomUtil.GetInt(
|
||||
ragfairConfig.Dynamic.Pack.ItemCountMin,
|
||||
@@ -510,14 +497,16 @@ public class RagfairOfferGenerator(
|
||||
|
||||
// Don't randomise pack items
|
||||
barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer, stackSize);
|
||||
} else if (isBarterOffer) {
|
||||
}
|
||||
else if (isBarterOffer)
|
||||
{
|
||||
// Apply randomised properties
|
||||
RandomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails);
|
||||
barterScheme = CreateBarterBarterScheme(itemWithChildren, ragfairConfig.Dynamic.Barter);
|
||||
if (ragfairConfig.Dynamic.Barter.MakeSingleStackOnly) {
|
||||
itemWithChildren[0].Upd.StackObjectsCount = 1;
|
||||
}
|
||||
} else {
|
||||
if (ragfairConfig.Dynamic.Barter.MakeSingleStackOnly) itemWithChildren[0].Upd.StackObjectsCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply randomised properties
|
||||
RandomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails);
|
||||
barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer);
|
||||
@@ -547,7 +536,8 @@ public class RagfairOfferGenerator(
|
||||
var assortsClone = cloner.Clone(trader.Assort);
|
||||
|
||||
// Trader assorts / assort items are missing
|
||||
if (assortsClone?.Items?.Count is null or 0) {
|
||||
if (assortsClone?.Items?.Count is null or 0)
|
||||
{
|
||||
logger.Error(
|
||||
localisationService.GetText(
|
||||
"ragfair-no_trader_assorts_cant_generate_flea_offers",
|
||||
@@ -558,25 +548,25 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
var blacklist = ragfairConfig.Dynamic.Blacklist;
|
||||
foreach (var item in assortsClone.Items) {
|
||||
foreach (var item in assortsClone.Items)
|
||||
{
|
||||
// We only want to process 'base/root' items, no children
|
||||
if (item.SlotId != "hideout") {
|
||||
if (item.SlotId != "hideout")
|
||||
// skip mod items
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run blacklist check on trader offers
|
||||
if (blacklist.TraderItems) {
|
||||
if (blacklist.TraderItems)
|
||||
{
|
||||
var itemDetails = itemHelper.GetItem(item.Template);
|
||||
if (!itemDetails.Key) {
|
||||
if (!itemDetails.Key)
|
||||
{
|
||||
logger.Warning(localisationService.GetText("ragfair-tpl_not_a_valid_item", item.Template));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't include items that BSG has blacklisted from flea
|
||||
if (blacklist.EnableBsgList && !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false)) {
|
||||
continue;
|
||||
}
|
||||
if (blacklist.EnableBsgList && !(itemDetails.Value?.Properties?.CanSellOnRagfair ?? false)) continue;
|
||||
}
|
||||
|
||||
var isPreset = presetHelper.IsPreset(item.Id);
|
||||
@@ -617,17 +607,16 @@ public class RagfairOfferGenerator(
|
||||
// Add any missing properties to first item in array
|
||||
AddMissingConditions(itemWithMods[0]);
|
||||
|
||||
if (!(profileHelper.IsPlayer(userID) || ragfairServerHelper.IsTrader(userID))) {
|
||||
if (!(profileHelper.IsPlayer(userID) || ragfairServerHelper.IsTrader(userID)))
|
||||
{
|
||||
var parentId = GetDynamicConditionIdForTpl(itemDetails.Id);
|
||||
if (string.IsNullOrEmpty(parentId)) {
|
||||
if (string.IsNullOrEmpty(parentId))
|
||||
// No condition details found, don't proceed with modifying item conditions
|
||||
return;
|
||||
}
|
||||
|
||||
// Roll random chance to randomise item condition
|
||||
if (randomUtil.GetChance100(ragfairConfig.Dynamic.Condition[parentId].ConditionChance * 100)) {
|
||||
if (randomUtil.GetChance100(ragfairConfig.Dynamic.Condition[parentId].ConditionChance * 100))
|
||||
RandomiseItemCondition(parentId, itemWithMods, itemDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,11 +629,9 @@ public class RagfairOfferGenerator(
|
||||
{
|
||||
// Get keys from condition config dictionary
|
||||
var configConditions = ragfairConfig.Dynamic.Condition.Keys;
|
||||
foreach (var baseClass in configConditions) {
|
||||
if (itemHelper.IsOfBaseclass(tpl, baseClass)) {
|
||||
foreach (var baseClass in configConditions)
|
||||
if (itemHelper.IsOfBaseclass(tpl, baseClass))
|
||||
return baseClass;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -664,21 +651,23 @@ public class RagfairOfferGenerator(
|
||||
var rootItem = itemWithMods[0];
|
||||
|
||||
var itemConditionValues = ragfairConfig.Dynamic.Condition[conditionSettingsId];
|
||||
var maxMultiplier = randomUtil.GetDouble((double) itemConditionValues.Max.Min, (double) itemConditionValues.Max.Min);
|
||||
var maxMultiplier = randomUtil.GetDouble((double)itemConditionValues.Max.Min, (double)itemConditionValues.Max.Min);
|
||||
var currentMultiplier = randomUtil.GetDouble(
|
||||
(double) itemConditionValues.Current.Min,
|
||||
(double) itemConditionValues.Current.Max
|
||||
(double)itemConditionValues.Current.Min,
|
||||
(double)itemConditionValues.Current.Max
|
||||
);
|
||||
|
||||
// Randomise armor + plates + armor related things
|
||||
if (itemHelper.ArmorItemCanHoldMods(rootItem.Template) ||
|
||||
itemHelper.IsOfBaseclasses(rootItem.Template, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
|
||||
) {
|
||||
)
|
||||
{
|
||||
RandomiseArmorDurabilityValues(itemWithMods, currentMultiplier, maxMultiplier);
|
||||
|
||||
// Add hits to visor
|
||||
var visorMod = itemWithMods.FirstOrDefault((item) => item.ParentId == BaseClasses.ARMORED_EQUIPMENT && item.SlotId == "mod_equipment_000");
|
||||
if (randomUtil.GetChance100(25) && visorMod != null) {
|
||||
if (randomUtil.GetChance100(25) && visorMod != null)
|
||||
{
|
||||
itemHelper.AddUpdObjectToItem(visorMod);
|
||||
|
||||
visorMod.Upd.FaceShield = new UpdFaceShield() { Hits = randomUtil.GetInt(1, 3) };
|
||||
@@ -688,26 +677,30 @@ public class RagfairOfferGenerator(
|
||||
}
|
||||
|
||||
// Randomise Weapons
|
||||
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.WEAPON)) {
|
||||
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.WEAPON))
|
||||
{
|
||||
RandomiseWeaponDurability(itemWithMods[0], itemDetails, maxMultiplier, currentMultiplier);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootItem.Upd?.MedKit != null) {
|
||||
if (rootItem.Upd?.MedKit != null)
|
||||
{
|
||||
// Randomize health
|
||||
var hpResource = Math.Round((double)rootItem.Upd.MedKit.HpResource * maxMultiplier);
|
||||
rootItem.Upd.MedKit.HpResource = hpResource == 0D ? 1D : hpResource;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootItem.Upd?.Key != null && itemDetails.Properties.MaximumNumberOfUsage > 1) {
|
||||
if (rootItem.Upd?.Key != null && itemDetails.Properties.MaximumNumberOfUsage > 1)
|
||||
{
|
||||
// Randomize key uses
|
||||
rootItem.Upd.Key.NumberOfUsages = (int?)Math.Round(itemDetails.Properties.MaximumNumberOfUsage.Value * (1 - maxMultiplier));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootItem.Upd?.FoodDrink != null) {
|
||||
if (rootItem.Upd?.FoodDrink != null)
|
||||
{
|
||||
// randomize food/drink value
|
||||
var hpPercent = Math.Round((double)itemDetails.Properties.MaxResource * maxMultiplier);
|
||||
rootItem.Upd.FoodDrink.HpPercent = hpPercent == 0D ? 1D : hpPercent;
|
||||
@@ -715,7 +708,8 @@ public class RagfairOfferGenerator(
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootItem.Upd?.RepairKit != null) {
|
||||
if (rootItem.Upd?.RepairKit != null)
|
||||
{
|
||||
// randomize repair kit (armor/weapon) uses
|
||||
var resource = Math.Round((double)itemDetails.Properties.MaxRepairResource * maxMultiplier);
|
||||
rootItem.Upd.RepairKit.Resource = resource == 0D ? 1D : resource;
|
||||
@@ -723,9 +717,10 @@ public class RagfairOfferGenerator(
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.FUEL)) {
|
||||
if (itemHelper.IsOfBaseclass(itemDetails.Id, BaseClasses.FUEL))
|
||||
{
|
||||
var totalCapacity = itemDetails.Properties.MaxResource;
|
||||
var remainingFuel = Math.Round((double) totalCapacity * maxMultiplier);
|
||||
var remainingFuel = Math.Round((double)totalCapacity * maxMultiplier);
|
||||
rootItem.Upd.Resource = new UpdResource()
|
||||
{ UnitsConsumed = totalCapacity - remainingFuel, Value = remainingFuel };
|
||||
}
|
||||
@@ -748,7 +743,7 @@ public class RagfairOfferGenerator(
|
||||
// Max
|
||||
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
|
||||
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double) lowestMaxDurability, (double) baseMaxDurability));
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability));
|
||||
|
||||
// Current
|
||||
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
@@ -770,19 +765,22 @@ public class RagfairOfferGenerator(
|
||||
double maxMultiplier
|
||||
)
|
||||
{
|
||||
foreach (var armorItem in armorWithMods) {
|
||||
foreach (var armorItem in armorWithMods)
|
||||
{
|
||||
var itemDbDetails = itemHelper.GetItem(armorItem.Template).Value;
|
||||
if (itemDbDetails.Properties.ArmorClass > 1) {
|
||||
if (itemDbDetails.Properties.ArmorClass > 1)
|
||||
{
|
||||
itemHelper.AddUpdObjectToItem(armorItem);
|
||||
|
||||
var baseMaxDurability = itemDbDetails.Properties.MaxDurability;
|
||||
var lowestMaxDurability = randomUtil.GetDouble(maxMultiplier, 1) * baseMaxDurability;
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double) lowestMaxDurability,(double) baseMaxDurability));
|
||||
var chosenMaxDurability = Math.Round(randomUtil.GetDouble((double)lowestMaxDurability, (double)baseMaxDurability));
|
||||
|
||||
var lowestCurrentDurability = randomUtil.GetDouble(currentMultiplier, 1) * chosenMaxDurability;
|
||||
var chosenCurrentDurability = Math.Round(randomUtil.GetDouble(lowestCurrentDurability, chosenMaxDurability));
|
||||
|
||||
armorItem.Upd.Repairable = new UpdRepairable() {
|
||||
armorItem.Upd.Repairable = new UpdRepairable()
|
||||
{
|
||||
Durability = chosenCurrentDurability == 0D ? 1D : chosenCurrentDurability, // Never var value become 0
|
||||
MaxDurability = chosenMaxDurability
|
||||
};
|
||||
@@ -796,7 +794,8 @@ public class RagfairOfferGenerator(
|
||||
* HpResource for medical items
|
||||
* @param item item to add conditions to
|
||||
*/
|
||||
protected void AddMissingConditions(Item item) {
|
||||
protected void AddMissingConditions(Item item)
|
||||
{
|
||||
var props = itemHelper.GetItem(item.Template).Value.Properties;
|
||||
var isRepairable = props.Durability != null;
|
||||
var isMedkit = props.MaxHpResource != null;
|
||||
@@ -804,7 +803,8 @@ public class RagfairOfferGenerator(
|
||||
var isConsumable = props.MaxResource > 1 && props.FoodUseTime != null;
|
||||
var isRepairKit = props.MaxRepairResource != null;
|
||||
|
||||
if (isRepairable && props.Durability > 0) {
|
||||
if (isRepairable && props.Durability > 0)
|
||||
{
|
||||
item.Upd.Repairable = new UpdRepairable()
|
||||
{ Durability = props.Durability, MaxDurability = props.Durability };
|
||||
|
||||
@@ -818,22 +818,22 @@ public class RagfairOfferGenerator(
|
||||
return;
|
||||
}
|
||||
|
||||
if (isKey) {
|
||||
item.Upd.Key = new UpdKey(){ NumberOfUsages = 0 };
|
||||
if (isKey)
|
||||
{
|
||||
item.Upd.Key = new UpdKey() { NumberOfUsages = 0 };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Food/drink
|
||||
if (isConsumable) {
|
||||
if (isConsumable)
|
||||
{
|
||||
item.Upd.FoodDrink = new UpdFoodDrink() { HpPercent = props.MaxResource };
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRepairKit) {
|
||||
item.Upd.RepairKit = new UpdRepairKit() { Resource = props.MaxRepairResource };
|
||||
}
|
||||
if (isRepairKit) item.Upd.RepairKit = new UpdRepairKit() { Resource = props.MaxRepairResource };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -852,9 +852,7 @@ public class RagfairOfferGenerator(
|
||||
);
|
||||
|
||||
// Dont make items under a designated rouble value into barter offers
|
||||
if (priceOfOfferItem < barterConfig.MinRoubleCostToBecomeBarter) {
|
||||
return CreateCurrencyBarterScheme(offerItems, false);
|
||||
}
|
||||
if (priceOfOfferItem < barterConfig.MinRoubleCostToBecomeBarter) return CreateCurrencyBarterScheme(offerItems, false);
|
||||
|
||||
// Get a randomised number of barter items to list offer for
|
||||
var barterItemCount = randomUtil.GetInt(barterConfig.ItemCountMin, barterConfig.ItemCountMax);
|
||||
@@ -863,23 +861,22 @@ public class RagfairOfferGenerator(
|
||||
var desiredItemCostRouble = Math.Round(priceOfOfferItem / barterItemCount);
|
||||
|
||||
// Rouble amount to go above/below when looking for an item (Wiggle cost of item a little)
|
||||
var offerCostVarianceRoubles = (desiredItemCostRouble * barterConfig.PriceRangeVariancePercent) / 100;
|
||||
var offerCostVarianceRoubles = desiredItemCostRouble * barterConfig.PriceRangeVariancePercent / 100;
|
||||
|
||||
// Dict of items and their flea price (cached on first use)
|
||||
var itemFleaPrices = GetFleaPricesAsArray();
|
||||
|
||||
// Filter possible barters to items that match the price range + not itself
|
||||
var itemsInsidePriceBounds = itemFleaPrices.Where(
|
||||
itemAndPrice =>
|
||||
itemAndPrice.Price >= desiredItemCostRouble - offerCostVarianceRoubles &&
|
||||
itemAndPrice.Price <= desiredItemCostRouble + offerCostVarianceRoubles &&
|
||||
itemAndPrice.Tpl != offerItems[0].Template // Don't allow the item being sold to be chosen
|
||||
).ToList();
|
||||
itemAndPrice =>
|
||||
itemAndPrice.Price >= desiredItemCostRouble - offerCostVarianceRoubles &&
|
||||
itemAndPrice.Price <= desiredItemCostRouble + offerCostVarianceRoubles &&
|
||||
itemAndPrice.Tpl != offerItems[0].Template // Don't allow the item being sold to be chosen
|
||||
)
|
||||
.ToList();
|
||||
|
||||
// No items on flea have a matching price, fall back to currency
|
||||
if (itemsInsidePriceBounds.Count == 0) {
|
||||
return CreateCurrencyBarterScheme(offerItems, false);
|
||||
}
|
||||
if (itemsInsidePriceBounds.Count == 0) return CreateCurrencyBarterScheme(offerItems, false);
|
||||
|
||||
// Choose random item from price-filtered flea items
|
||||
var randomItem = randomUtil.GetArrayValue(itemsInsidePriceBounds);
|
||||
@@ -894,7 +891,8 @@ public class RagfairOfferGenerator(
|
||||
protected List<TplWithFleaPrice> GetFleaPricesAsArray()
|
||||
{
|
||||
// Generate if needed
|
||||
if (allowedFleaPriceItemsForBarter == null) {
|
||||
if (allowedFleaPriceItemsForBarter == null)
|
||||
{
|
||||
var fleaPrices = databaseService.GetPrices();
|
||||
|
||||
// Only get prices for items that also exist in items.json
|
||||
@@ -928,4 +926,3 @@ public class RagfairOfferGenerator(
|
||||
return [new BarterScheme() { Count = price, Template = currency }];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ public class RepeatableQuestGenerator(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
protected QuestConfig _questConfig = _configServer.GetConfig<QuestConfig>();
|
||||
protected int _maxRandomNumberAttempts = 6;
|
||||
protected QuestConfig _questConfig = _configServer.GetConfig<QuestConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by /GetClientRepeatableQuests/ and creates one element of quest type format (see
|
||||
@@ -101,7 +101,8 @@ public class RepeatableQuestGenerator(
|
||||
var locationsConfig = repeatableConfig.Locations;
|
||||
var targetsConfig = new ProbabilityObjectArray<string, BossInfo>(_mathUtil, _cloner, eliminationConfig.Targets);
|
||||
var bodyPartsConfig = new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.BodyParts);
|
||||
var weaponCategoryRequirementConfig = new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.WeaponCategoryRequirements);
|
||||
var weaponCategoryRequirementConfig =
|
||||
new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.WeaponCategoryRequirements);
|
||||
var weaponRequirementConfig = new ProbabilityObjectArray<string, List<string>>(_mathUtil, _cloner, eliminationConfig.WeaponRequirements);
|
||||
|
||||
// the difficulty of the quest varies in difficulty depending on the condition
|
||||
@@ -178,9 +179,7 @@ public class RepeatableQuestGenerator(
|
||||
.ToList();
|
||||
if (questTypePool.Pool.Elimination.Targets.GetByJsonProp<TargetLocation>(targetKey).Locations.Count ==
|
||||
0)
|
||||
{
|
||||
questTypePool.Pool.Elimination.Targets.Remove(targetKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,10 +242,13 @@ public class RepeatableQuestGenerator(
|
||||
if (eliminationConfig.DistanceProbability > rand.NextDouble() && isDistanceRequirementAllowed)
|
||||
{
|
||||
// Random distance with lower values more likely; simple distribution for starters...
|
||||
distance = (int)Math.Floor(Math.Abs(rand.NextDouble() - rand.NextDouble()) *
|
||||
(1 + eliminationConfig.MaxDistance - eliminationConfig.MinDistance) +
|
||||
eliminationConfig.MinDistance ?? 0);
|
||||
|
||||
distance = (int)Math.Floor(
|
||||
Math.Abs(rand.NextDouble() - rand.NextDouble()) *
|
||||
(1 + eliminationConfig.MaxDistance - eliminationConfig.MinDistance) +
|
||||
eliminationConfig.MinDistance ??
|
||||
0
|
||||
);
|
||||
|
||||
distance = (int)Math.Ceiling((decimal)(distance / 5)) * 5;
|
||||
distanceDifficulty = (int)(maxDistDifficulty * distance / eliminationConfig.MaxDistance);
|
||||
}
|
||||
@@ -317,10 +319,7 @@ public class RepeatableQuestGenerator(
|
||||
var quest = GenerateRepeatableTemplate("Elimination", traderId, repeatableConfig.Side, sessionId);
|
||||
|
||||
// ASSUMPTION: All fence quests are for scavs
|
||||
if (traderId == Traders.FENCE)
|
||||
{
|
||||
quest.Side = "Scav";
|
||||
}
|
||||
if (traderId == Traders.FENCE) quest.Side = "Scav";
|
||||
|
||||
var availableForFinishCondition = quest.Conditions.AvailableForFinish[0];
|
||||
availableForFinishCondition.Counter.Id = _hashUtil.Generate();
|
||||
@@ -372,14 +371,10 @@ public class RepeatableQuestGenerator(
|
||||
EliminationConfig eliminationConfig)
|
||||
{
|
||||
if (targetsConfig.Data(targetKey).IsBoss.GetValueOrDefault(false))
|
||||
{
|
||||
return _randomUtil.RandInt(eliminationConfig.MinBossKills.Value, eliminationConfig.MaxBossKills + 1);
|
||||
}
|
||||
|
||||
if (targetsConfig.Data(targetKey).IsPmc.GetValueOrDefault(false))
|
||||
{
|
||||
return _randomUtil.RandInt(eliminationConfig.MinPmcKills.Value, eliminationConfig.MaxPmcKills + 1);
|
||||
}
|
||||
|
||||
return _randomUtil.RandInt(eliminationConfig.MinKills.Value, eliminationConfig.MaxKills + 1);
|
||||
}
|
||||
@@ -448,22 +443,14 @@ public class RepeatableQuestGenerator(
|
||||
}
|
||||
|
||||
// Has specific body part hit condition
|
||||
if (targetedBodyParts is not null)
|
||||
{
|
||||
killConditionProps.BodyPart = targetedBodyParts;
|
||||
}
|
||||
if (targetedBodyParts is not null) killConditionProps.BodyPart = targetedBodyParts;
|
||||
|
||||
// Don't allow distance + melee requirement
|
||||
if (distance is not null && allowedWeaponCategory != "5b5f7a0886f77409407a7f96")
|
||||
{
|
||||
killConditionProps.Distance = new CounterConditionDistance { CompareMethod = ">=", Value = distance.Value };
|
||||
}
|
||||
|
||||
// Has specific weapon requirement
|
||||
if (allowedWeapon is not null)
|
||||
{
|
||||
killConditionProps.Weapon = [allowedWeapon];
|
||||
}
|
||||
if (allowedWeapon is not null) killConditionProps.Weapon = [allowedWeapon];
|
||||
|
||||
// Has specific weapon category requirement
|
||||
if (allowedWeaponCategory?.Length > 0)
|
||||
@@ -637,11 +624,9 @@ public class RepeatableQuestGenerator(
|
||||
var x = (int)Math.Floor(roublesBudget / itemUnitPrice);
|
||||
maxValue = Math.Min(maxValue, x);
|
||||
if (maxValue > minValue)
|
||||
{
|
||||
// If it doesn't blow the budget we have for the request, draw a random amount of the selected
|
||||
// Item type to be requested
|
||||
value = _randomUtil.RandInt(minValue, maxValue + 1);
|
||||
}
|
||||
|
||||
roublesBudget -= value * itemUnitPrice;
|
||||
|
||||
@@ -654,10 +639,7 @@ public class RepeatableQuestGenerator(
|
||||
// Reduce the list possible items to fulfill the new budget constraint
|
||||
itemSelection = itemSelection.Where(dbItem => _itemHelper.GetItemPrice(dbItem.Id) < roublesBudget)
|
||||
.ToList();
|
||||
if (!itemSelection.Any())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!itemSelection.Any()) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -694,15 +676,10 @@ public class RepeatableQuestGenerator(
|
||||
_itemHelper.IsOfBaseclass(itemTpl, BaseClasses.WEAPON) ||
|
||||
_itemHelper.IsOfBaseclass(itemTpl, BaseClasses.ARMOR)
|
||||
)
|
||||
{
|
||||
minDurability = _randomUtil.GetArrayValue([60, 80]);
|
||||
}
|
||||
|
||||
// By default all collected items must be FiR, except dog tags
|
||||
if (_itemHelper.IsDogtag(itemTpl))
|
||||
{
|
||||
onlyFoundInRaid = false;
|
||||
}
|
||||
if (_itemHelper.IsDogtag(itemTpl)) onlyFoundInRaid = false;
|
||||
|
||||
return new QuestCondition
|
||||
{
|
||||
|
||||
@@ -43,12 +43,12 @@ public class RepeatableQuestRewardGenerator(
|
||||
* - Items
|
||||
* - Trader Reputation
|
||||
* - Skill level experience
|
||||
*
|
||||
*
|
||||
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
|
||||
* experience / money / items / trader reputation can be defined in QuestConfig.js
|
||||
*
|
||||
*
|
||||
* There's also a random variation of the reward the spread of which can be also defined in the config
|
||||
*
|
||||
*
|
||||
* Additionally, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
|
||||
* @param pmcLevel Level of player reward is being generated for
|
||||
* @param difficulty Reward scaling factor from 0.2 to 1
|
||||
@@ -130,19 +130,14 @@ public class RepeatableQuestRewardGenerator(
|
||||
var filteredRewardItemPool = inBudgetRewardItemPool.Where(
|
||||
item => !rewardTplBlacklist.Contains(item.Id)
|
||||
);
|
||||
if (filteredRewardItemPool.Count() > 0)
|
||||
{
|
||||
inBudgetRewardItemPool = filteredRewardItemPool.ToList();
|
||||
}
|
||||
if (filteredRewardItemPool.Count() > 0) inBudgetRewardItemPool = filteredRewardItemPool.ToList();
|
||||
}
|
||||
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Generating: {repeatableConfig.Name} quest for: {traderId} with budget: {itemRewardBudget} totalling: {rewardParams.RewardNumItems} items"
|
||||
);
|
||||
}
|
||||
if (inBudgetRewardItemPool.Count > 0)
|
||||
{
|
||||
var itemsToReward = GetRewardableItemsFromPoolWithinBudget(
|
||||
@@ -177,10 +172,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
rewards.Success.Add(reward);
|
||||
rewardIndex++;
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward");
|
||||
}
|
||||
|
||||
// Chance of adding skill reward
|
||||
@@ -200,10 +192,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
};
|
||||
rewards.Success.Add(reward);
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}");
|
||||
}
|
||||
|
||||
return rewards;
|
||||
@@ -223,10 +212,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
var reputationConfig = rewardScaling.Reputation;
|
||||
|
||||
var effectiveDifficulty = difficulty is null ? 1 : difficulty;
|
||||
if (difficulty is null)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("repeatable-difficulty_was_nan"));
|
||||
}
|
||||
if (difficulty is null) _logger.Warning(_localisationService.GetText("repeatable-difficulty_was_nan"));
|
||||
|
||||
return new QuestRewardValues
|
||||
{
|
||||
@@ -325,10 +311,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
// Get a random item
|
||||
var chosenItemFromPool = exhausableItemPool.GetRandomValue();
|
||||
if (!exhausableItemPool.HasValues())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!exhausableItemPool.HasValues()) break;
|
||||
|
||||
// Handle edge case - ammo
|
||||
if (_itemHelper.IsOfBaseclass(chosenItemFromPool.Id, BaseClasses.AMMO))
|
||||
@@ -351,18 +334,13 @@ public class RepeatableQuestRewardGenerator(
|
||||
// 25% chance to double, triple or quadruple reward stack
|
||||
// (Only occurs when item is stackable and not weapon, armor or ammo)
|
||||
if (CanIncreaseRewardItemStackSize(chosenItemFromPool, 70000, 25))
|
||||
{
|
||||
rewardItemStackCount = GetRandomisedRewardItemStackSizeByPrice(chosenItemFromPool);
|
||||
}
|
||||
|
||||
itemsToReturn.Add(chosenItemFromPool, rewardItemStackCount);
|
||||
|
||||
var itemCost = _presetHelper.GetDefaultPresetOrItemPrice(chosenItemFromPool.Id);
|
||||
var calculatedItemRewardBudget = itemRewardBudget - rewardItemStackCount * itemCost;
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}");
|
||||
}
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug)) _logger.Debug($"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}");
|
||||
|
||||
// If we still have budget narrow down possible items
|
||||
if (calculatedItemRewardBudget > 0)
|
||||
@@ -375,12 +353,8 @@ public class RepeatableQuestRewardGenerator(
|
||||
);
|
||||
|
||||
if (!exhausableItemPool.HasValues())
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No budget for more items, end loop
|
||||
@@ -453,10 +427,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
// Find the appropriate price tier and return a random stack size from its options
|
||||
var tier = priceTiers.FirstOrDefault(tier => rewardItemPrice < tier.Item1);
|
||||
if (tier is null)
|
||||
{
|
||||
return 4; // Default to 2 if no tier matches
|
||||
}
|
||||
if (tier is null) return 4; // Default to 2 if no tier matches
|
||||
|
||||
return _randomUtil.GetArrayValue(tier.Item2);
|
||||
}
|
||||
@@ -533,10 +504,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
while (defaultPresetPool.HasValues())
|
||||
{
|
||||
var randomPreset = defaultPresetPool.GetRandomValue();
|
||||
if (randomPreset is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (randomPreset is null) continue;
|
||||
|
||||
// Gather all tpls so we can get prices of them
|
||||
var tpls = randomPreset.Items.Select(item => item.Template).ToList();
|
||||
@@ -560,7 +528,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
/**
|
||||
* Helper to create a reward item structured as required by the client
|
||||
*
|
||||
*
|
||||
* @param {string} tpl ItemId of the rewarded item
|
||||
* @param {integer} count Amount of items to give
|
||||
* @param {integer} index All rewards will be appended to a list, for unknown reasons the client wants the index
|
||||
@@ -587,15 +555,9 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
// Get presets root item
|
||||
var rootItem = preset.FirstOrDefault(item => item.Template == tpl);
|
||||
if (rootItem is null)
|
||||
{
|
||||
_logger.Warning($"Root item of preset: {tpl} not found");
|
||||
}
|
||||
if (rootItem is null) _logger.Warning($"Root item of preset: {tpl} not found");
|
||||
|
||||
if (rootItem.Upd is not null)
|
||||
{
|
||||
rootItem.Upd.SpawnedInSession = foundInRaid;
|
||||
}
|
||||
if (rootItem.Upd is not null) rootItem.Upd.SpawnedInSession = foundInRaid;
|
||||
|
||||
questRewardItem.Items = _itemHelper.ReparentItemAndChildren(rootItem, preset);
|
||||
questRewardItem.Target = rootItem.Id; // Target property and root items id must match
|
||||
@@ -605,7 +567,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
/**
|
||||
* Helper to create a reward item structured as required by the client
|
||||
*
|
||||
*
|
||||
* @param {string} tpl ItemId of the rewarded item
|
||||
* @param {integer} count Amount of items to give
|
||||
* @param {integer} index All rewards will be appended to a list, for unknown reasons the client wants the index
|
||||
@@ -677,15 +639,9 @@ public class RepeatableQuestRewardGenerator(
|
||||
itemTemplate =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate.Parent == "")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (itemTemplate.Parent == "") return false;
|
||||
|
||||
if (seasonalItems.Contains(itemTemplate.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (seasonalItems.Contains(itemTemplate.Id)) return false;
|
||||
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(
|
||||
trader => trader.TraderId == traderId
|
||||
@@ -711,10 +667,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
List<string>? itemBaseWhitelist = null)
|
||||
{
|
||||
// Return early if not valid item to give as reward
|
||||
if (!_itemHelper.IsValidItem(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_itemHelper.IsValidItem(tpl)) return false;
|
||||
|
||||
// Check item is not blacklisted
|
||||
if (
|
||||
@@ -723,27 +676,16 @@ public class RepeatableQuestRewardGenerator(
|
||||
repeatableQuestConfig.RewardBlacklist.Contains(tpl) ||
|
||||
_itemFilterService.IsItemBlacklisted(tpl)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Item has blacklisted base types
|
||||
if (_itemHelper.IsOfBaseclasses(tpl, repeatableQuestConfig.RewardBaseTypeBlacklist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_itemHelper.IsOfBaseclasses(tpl, repeatableQuestConfig.RewardBaseTypeBlacklist)) return false;
|
||||
|
||||
// Skip boss items
|
||||
if (_itemFilterService.IsBossItem(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_itemFilterService.IsBossItem(tpl)) return false;
|
||||
|
||||
// Trader has specific item base types they can give as rewards to player
|
||||
if (itemBaseWhitelist is not null && !_itemHelper.IsOfBaseclasses(tpl, itemBaseWhitelist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (itemBaseWhitelist is not null && !_itemHelper.IsOfBaseclasses(tpl, itemBaseWhitelist)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ public class ScavCaseRewardGenerator(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
protected ScavCaseConfig _scavCaseConfig = _configServer.GetConfig<ScavCaseConfig>();
|
||||
protected List<TemplateItem> _dbItemsCache = [];
|
||||
protected List<TemplateItem> _dbAmmoItemsCache = [];
|
||||
protected List<TemplateItem> _dbItemsCache = [];
|
||||
protected ScavCaseConfig _scavCaseConfig = _configServer.GetConfig<ScavCaseConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Create an array of rewards that will be given to the player upon completing their scav case build
|
||||
@@ -93,26 +93,16 @@ public class ScavCaseRewardGenerator(
|
||||
// Get an array of seasonal items that should not be shown right now as seasonal event is not active
|
||||
var inactiveSeasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
if (!_dbItemsCache.Any())
|
||||
{
|
||||
_dbItemsCache = _databaseService.GetItems()
|
||||
.Values.Where(
|
||||
item =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (item.Parent == "")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Parent == "") return false;
|
||||
|
||||
if (item.Type == "Node")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Type == "Node") return false;
|
||||
|
||||
if (item.Properties.QuestItem ?? false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Properties.QuestItem ?? false) return false;
|
||||
|
||||
// Skip item if item id is on blacklist
|
||||
if (
|
||||
@@ -120,98 +110,58 @@ public class ScavCaseRewardGenerator(
|
||||
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id) ||
|
||||
_itemFilterService.IsItemBlacklisted(item.Id)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Globally reward-blacklisted
|
||||
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_itemFilterService.IsItemRewardBlacklisted(item.Id)) return false;
|
||||
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id)) return false;
|
||||
|
||||
// Skip item if parent id is blacklisted
|
||||
if (_itemHelper.IsOfBaseclasses(item.Id, _scavCaseConfig.RewardItemParentBlacklist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_itemHelper.IsOfBaseclasses(item.Id, _scavCaseConfig.RewardItemParentBlacklist)) return false;
|
||||
|
||||
if (inactiveSeasonalItems.Contains(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (inactiveSeasonalItems.Contains(item.Id)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (!_dbAmmoItemsCache.Any())
|
||||
{
|
||||
_dbAmmoItemsCache = _databaseService.GetItems()
|
||||
.Values.Where(
|
||||
item =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (item.Parent == "")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Parent == "") return false;
|
||||
|
||||
if (item.Type != "Item")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Type != "Item") return false;
|
||||
|
||||
// Not ammo, skip
|
||||
if (!_itemHelper.IsOfBaseclass(item.Id, BaseClasses.AMMO))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_itemHelper.IsOfBaseclass(item.Id, BaseClasses.AMMO)) return false;
|
||||
|
||||
// Skip item if item id is on blacklist
|
||||
if (
|
||||
_scavCaseConfig.RewardItemBlacklist.Contains(item.Id) ||
|
||||
_itemFilterService.IsItemBlacklisted(item.Id)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Globally reward-blacklisted
|
||||
if (_itemFilterService.IsItemRewardBlacklisted(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_itemFilterService.IsItemRewardBlacklisted(item.Id)) return false;
|
||||
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!_scavCaseConfig.AllowBossItemsAsRewards && _itemFilterService.IsBossItem(item.Id)) return false;
|
||||
|
||||
// Skip seasonal items
|
||||
if (inactiveSeasonalItems.Contains(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (inactiveSeasonalItems.Contains(item.Id)) return false;
|
||||
|
||||
// Skip ammo that doesn't stack as high as value in config
|
||||
if (item.Properties.StackMaxSize < _scavCaseConfig.AmmoRewards.MinStackSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (item.Properties.StackMaxSize < _scavCaseConfig.AmmoRewards.MinStackSize) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -231,30 +181,22 @@ public class ScavCaseRewardGenerator(
|
||||
var rewardWasAmmo = false;
|
||||
var randomCount = _randomUtil.GetInt((int)itemFilters.MinCount, (int)itemFilters.MaxCount);
|
||||
for (var i = 0; i < randomCount; i++)
|
||||
{
|
||||
if (RewardShouldBeMoney() && !rewardWasMoney)
|
||||
{
|
||||
// Only allow one reward to be money
|
||||
result.Add(GetRandomMoney());
|
||||
if (!_scavCaseConfig.AllowMultipleMoneyRewardsPerRarity)
|
||||
{
|
||||
rewardWasMoney = true;
|
||||
}
|
||||
if (!_scavCaseConfig.AllowMultipleMoneyRewardsPerRarity) rewardWasMoney = true;
|
||||
}
|
||||
else if (RewardShouldBeAmmo() && !rewardWasAmmo)
|
||||
{
|
||||
// Only allow one reward to be ammo
|
||||
result.Add(GetRandomAmmo(rarity));
|
||||
if (!_scavCaseConfig.AllowMultipleAmmoRewardsPerRarity)
|
||||
{
|
||||
rewardWasAmmo = true;
|
||||
}
|
||||
if (!_scavCaseConfig.AllowMultipleAmmoRewardsPerRarity) rewardWasAmmo = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(_randomUtil.GetArrayValue(items));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -308,18 +250,13 @@ public class ScavCaseRewardGenerator(
|
||||
handbookPrice >= _scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub[rarity].Min &&
|
||||
handbookPrice <= _scavCaseConfig.AmmoRewards.AmmoRewardValueRangeRub[rarity].Max
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
if (!possibleAmmoPool.Any())
|
||||
{
|
||||
_logger.Warning("Unable to get a list of ammo that matches desired criteria for scav case reward");
|
||||
}
|
||||
if (!possibleAmmoPool.Any()) _logger.Warning("Unable to get a list of ammo that matches desired criteria for scav case reward");
|
||||
|
||||
// Get a random ammo and return it
|
||||
return _randomUtil.GetArrayValue(possibleAmmoPool);
|
||||
@@ -337,7 +274,7 @@ public class ScavCaseRewardGenerator(
|
||||
List<List<Item>> result = [];
|
||||
foreach (var rewardItemDb in rewardItems)
|
||||
{
|
||||
List<Item> resultItem = [new Item { Id = _hashUtil.Generate(), Template = rewardItemDb.Id, Upd = null }];
|
||||
List<Item> resultItem = [new() { Id = _hashUtil.Generate(), Template = rewardItemDb.Id, Upd = null }];
|
||||
var rootItem = resultItem.FirstOrDefault();
|
||||
|
||||
if (_itemHelper.IsOfBaseclass(rewardItemDb.Id, BaseClasses.AMMO_BOX))
|
||||
@@ -359,7 +296,7 @@ public class ScavCaseRewardGenerator(
|
||||
}
|
||||
|
||||
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
|
||||
List<Item> presetAndMods = _itemHelper.ReplaceIDs(_cloner.Clone(preset.Items));
|
||||
var presetAndMods = _itemHelper.ReplaceIDs(_cloner.Clone(preset.Items));
|
||||
_itemHelper.RemapRootItemId(presetAndMods);
|
||||
|
||||
resultItem = presetAndMods;
|
||||
@@ -388,10 +325,7 @@ public class ScavCaseRewardGenerator(
|
||||
item =>
|
||||
{
|
||||
var handbookPrice = _ragfairPriceService.GetStaticPriceForItem(item.Id);
|
||||
if (handbookPrice >= itemFilters.MinPriceRub && handbookPrice <= itemFilters.MaxPriceRub)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (handbookPrice >= itemFilters.MinPriceRub && handbookPrice <= itemFilters.MaxPriceRub) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -443,14 +377,11 @@ public class ScavCaseRewardGenerator(
|
||||
{
|
||||
var amountToGive = 1;
|
||||
if (itemToCalculate.Parent == BaseClasses.AMMO)
|
||||
{
|
||||
amountToGive = _randomUtil.GetInt(
|
||||
_scavCaseConfig.AmmoRewards.MinStackSize,
|
||||
itemToCalculate.Properties.StackMaxSize ?? 0
|
||||
);
|
||||
}
|
||||
else if (itemToCalculate.Parent == BaseClasses.MONEY)
|
||||
{
|
||||
amountToGive = itemToCalculate.Id switch
|
||||
{
|
||||
Money.ROUBLES => _randomUtil.GetInt(
|
||||
@@ -471,7 +402,6 @@ public class ScavCaseRewardGenerator(
|
||||
),
|
||||
_ => amountToGive
|
||||
};
|
||||
}
|
||||
|
||||
return amountToGive;
|
||||
}
|
||||
|
||||
@@ -26,17 +26,13 @@ public class BarrelInvetoryMagGen(
|
||||
// Can't be done by _props.ammoType as grenade launcher shoots grenades with ammoType of "buckshot"
|
||||
double? randomisedAmmoStackSize = null;
|
||||
if (inventoryMagGen.GetAmmoTemplate().Properties.StackMaxRandom == 1)
|
||||
{
|
||||
// doesnt stack
|
||||
randomisedAmmoStackSize = _randomUtil.GetInt(3, 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
randomisedAmmoStackSize = _randomUtil.GetInt(
|
||||
(int)inventoryMagGen.GetAmmoTemplate().Properties.StackMinRandom,
|
||||
(int)inventoryMagGen.GetAmmoTemplate().Properties.StackMaxRandom
|
||||
);
|
||||
}
|
||||
|
||||
_botWeaponGeneratorHelper.AddAmmoIntoEquipmentSlots(
|
||||
inventoryMagGen.GetAmmoTemplate().Id,
|
||||
|
||||
@@ -19,7 +19,6 @@ public class ExternalInventoryMagGen(
|
||||
RandomUtil _randomUtil
|
||||
) : InventoryMagGen, IInventoryMagGen
|
||||
{
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
return 99;
|
||||
@@ -61,10 +60,8 @@ public class ExternalInventoryMagGen(
|
||||
);
|
||||
|
||||
if (fitsIntoInventory == ItemAddedResult.NO_CONTAINERS)
|
||||
{
|
||||
// No containers to fit magazines, stop trying
|
||||
break;
|
||||
}
|
||||
|
||||
// No space for magazine and we haven't reached desired magazine count
|
||||
if (fitsIntoInventory == ItemAddedResult.NO_SPACE && i < randomizedMagazineCount)
|
||||
@@ -73,9 +70,7 @@ public class ExternalInventoryMagGen(
|
||||
if (fitAttempts > 5)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Failed {fitAttempts} times to add magazine {magazineTpl} to bot inventory, stopping");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -85,25 +80,19 @@ public class ExternalInventoryMagGen(
|
||||
* Temporary workaround to Killa spawning with no extra mags if he spawns with a drum mag */
|
||||
|
||||
if (magazineTpl == defaultMagazineTpl)
|
||||
{
|
||||
// We were already on default - stop here to prevent infinite loop
|
||||
break;
|
||||
}
|
||||
|
||||
// Add failed magazine tpl to blacklist
|
||||
attemptedMagBlacklist.Add(magazineTpl);
|
||||
|
||||
if (defaultMagazineTpl is null)
|
||||
{
|
||||
// No default to fall back to, stop trying to add mags
|
||||
break;
|
||||
}
|
||||
|
||||
if (defaultMagazineTpl == BaseClasses.MAGAZINE)
|
||||
{
|
||||
// Magazine base type, do not use
|
||||
break;
|
||||
}
|
||||
|
||||
// Set chosen magazine tpl to the weapons default magazine tpl and try to fit into inventory next loop
|
||||
magazineTpl = defaultMagazineTpl;
|
||||
@@ -128,15 +117,12 @@ public class ExternalInventoryMagGen(
|
||||
if (result?.Id is null)
|
||||
{
|
||||
// Highly likely shotgun has no external mags
|
||||
if (isShotgun)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (isShotgun) break;
|
||||
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Unable to add additional magazine into bot inventory: vest/pockets for weapon: {weapon.Name}, attempted: {fitAttempts} times. Reason: {fitsIntoInventory}");
|
||||
}
|
||||
_logger.Debug(
|
||||
$"Unable to add additional magazine into bot inventory: vest/pockets for weapon: {weapon.Name}, attempted: {fitAttempts} times. Reason: {fitsIntoInventory}"
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -151,10 +137,8 @@ public class ExternalInventoryMagGen(
|
||||
}
|
||||
|
||||
if (fitsIntoInventory == ItemAddedResult.SUCCESS)
|
||||
{
|
||||
// Reset fit counter now it succeeded
|
||||
fitAttempts = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +146,7 @@ public class ExternalInventoryMagGen(
|
||||
{
|
||||
// The mag Slot data for the weapon
|
||||
var magSlot = _itemHelper.GetItem(weaponTpl).Value.Properties.Slots.FirstOrDefault((x) => x.Name == "mod_magazine");
|
||||
if (magSlot is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (magSlot is null) return null;
|
||||
|
||||
// All possible mags that fit into the weapon excluding blacklisted
|
||||
var magazinePool = magSlot.Props.Filters[0]
|
||||
@@ -173,17 +154,11 @@ public class ExternalInventoryMagGen(
|
||||
.Select(
|
||||
(x) => _itemHelper.GetItem(x).Value
|
||||
);
|
||||
if (magazinePool is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (magazinePool is null) return null;
|
||||
|
||||
// Non-internal magazines that fit into the weapon
|
||||
var externalMagazineOnlyPool = magazinePool.Where((x) => x.Properties.ReloadMagType != ReloadMode.InternalMagazine);
|
||||
if (externalMagazineOnlyPool is null || externalMagazineOnlyPool?.Count() == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (externalMagazineOnlyPool is null || externalMagazineOnlyPool?.Count() == 0) return null;
|
||||
|
||||
// Randomly chosen external magazine
|
||||
return _randomUtil.GetArrayValue(externalMagazineOnlyPool);
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Core.Generators.WeaponGen.Implementations;
|
||||
[Injectable]
|
||||
public class UbglExternalMagGen(
|
||||
BotWeaponGeneratorHelper _botWeaponGeneratorHelper
|
||||
|
||||
) : InventoryMagGen, IInventoryMagGen
|
||||
{
|
||||
public int GetPriority()
|
||||
|
||||
@@ -6,11 +6,11 @@ namespace Core.Generators.WeaponGen;
|
||||
[Injectable]
|
||||
public class InventoryMagGen()
|
||||
{
|
||||
private GenerationData _magCounts;
|
||||
private TemplateItem _magazineTemplate;
|
||||
private TemplateItem _weaponTemplate;
|
||||
private TemplateItem _ammoTemplate;
|
||||
private TemplateItem _magazineTemplate;
|
||||
private GenerationData _magCounts;
|
||||
private BotBaseInventory _pmcInventory;
|
||||
private TemplateItem _weaponTemplate;
|
||||
|
||||
public InventoryMagGen
|
||||
(
|
||||
|
||||
@@ -104,10 +104,7 @@ public class WeatherGenerator(
|
||||
protected SeasonalValues GetWeatherValuesBySeason(Season currentSeason)
|
||||
{
|
||||
var result = _weatherConfig.Weather.SeasonValues.TryGetValue(currentSeason.ToString(), out var value);
|
||||
if (!result)
|
||||
{
|
||||
return _weatherConfig.Weather.SeasonValues["default"];
|
||||
}
|
||||
if (!result) return _weatherConfig.Weather.SeasonValues["default"];
|
||||
|
||||
return value!;
|
||||
}
|
||||
|
||||
@@ -47,17 +47,11 @@ public class AssortHelper(
|
||||
{
|
||||
// Get quest id that unlocks assort + statuses quest can be in to show assort
|
||||
var unlockValues = GetQuestIdAndStatusThatShowAssort(mergedQuestAssorts, assortId.Key);
|
||||
if (unlockValues is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (unlockValues is null) continue;
|
||||
|
||||
// Remove assort if quest in profile does not have status that unlocks assort
|
||||
var questStatusInProfile = _questHelper.GetQuestStatus(pmcProfile, unlockValues.Value.Key);
|
||||
if (!unlockValues.Value.Value.Contains(questStatusInProfile))
|
||||
{
|
||||
strippedTraderAssorts = RemoveItemFromAssort(traderAssorts, assortId.Key, flea);
|
||||
}
|
||||
if (!unlockValues.Value.Value.Contains(questStatusInProfile)) strippedTraderAssorts = RemoveItemFromAssort(traderAssorts, assortId.Key, flea);
|
||||
}
|
||||
|
||||
return strippedTraderAssorts;
|
||||
@@ -74,29 +68,23 @@ public class AssortHelper(
|
||||
string assortId)
|
||||
{
|
||||
if (mergedQuestAssorts.TryGetValue("started", out var dict1) && dict1.ContainsKey(assortId))
|
||||
{
|
||||
// Assort unlocked by starting quest, assort is visible to player when : started or ready to hand in + handed in
|
||||
return new KeyValuePair<string, List<QuestStatusEnum>>(
|
||||
mergedQuestAssorts["started"][assortId],
|
||||
[QuestStatusEnum.Started, QuestStatusEnum.AvailableForFinish, QuestStatusEnum.Success]
|
||||
);
|
||||
}
|
||||
|
||||
if (mergedQuestAssorts.TryGetValue("success", out var dict2) && dict2.ContainsKey(assortId))
|
||||
{
|
||||
return new KeyValuePair<string, List<QuestStatusEnum>>(
|
||||
mergedQuestAssorts["success"][assortId],
|
||||
[QuestStatusEnum.Success]
|
||||
);
|
||||
}
|
||||
|
||||
if (mergedQuestAssorts.TryGetValue("fail", out var dict3) && dict3.ContainsKey(assortId))
|
||||
{
|
||||
return new KeyValuePair<string, List<QuestStatusEnum>>(
|
||||
mergedQuestAssorts["fail"][assortId],
|
||||
[QuestStatusEnum.Fail]
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -122,12 +110,8 @@ public class AssortHelper(
|
||||
|
||||
// Remove items restricted by loyalty levels above those reached by the player
|
||||
foreach (var item in assort.LoyalLevelItems)
|
||||
{
|
||||
if (pmcProfile.TradersInfo.TryGetValue(traderId, out var info) && assort.LoyalLevelItems[item.Key] > info.LoyaltyLevel)
|
||||
{
|
||||
strippedAssort = RemoveItemFromAssort(assort, item.Key);
|
||||
}
|
||||
}
|
||||
|
||||
return strippedAssort;
|
||||
}
|
||||
@@ -145,12 +129,8 @@ public class AssortHelper(
|
||||
if (assort.BarterScheme.TryGetValue(itemID, out var lisToUse) && lisToUse is not null && flea)
|
||||
{
|
||||
foreach (var barterSchemes in lisToUse)
|
||||
{
|
||||
foreach (var barterScheme in barterSchemes)
|
||||
{
|
||||
barterScheme.SptQuestLocked = true;
|
||||
}
|
||||
}
|
||||
foreach (var barterScheme in barterSchemes)
|
||||
barterScheme.SptQuestLocked = true;
|
||||
|
||||
return assort;
|
||||
}
|
||||
@@ -159,15 +139,9 @@ public class AssortHelper(
|
||||
assort.LoyalLevelItems.Remove(itemID);
|
||||
|
||||
foreach (var i in idsToRemove)
|
||||
{
|
||||
foreach (var a in assort.Items.ToList())
|
||||
{
|
||||
if (a.Id == i)
|
||||
{
|
||||
assort.Items.Remove(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var a in assort.Items.ToList())
|
||||
if (a.Id == i)
|
||||
assort.Items.Remove(a);
|
||||
|
||||
return assort;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class BotDifficultyHelper(
|
||||
)
|
||||
{
|
||||
protected PmcConfig _pmcConfig = _configServer.GetConfig<PmcConfig>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get difficulty settings for desired bot type, if not found use assault bot types
|
||||
/// </summary>
|
||||
@@ -33,7 +33,8 @@ public class BotDifficultyHelper(
|
||||
public DifficultyCategories GetBotDifficultySettings(string type, string desiredDifficulty, Bots botDb)
|
||||
{
|
||||
var desiredType = type.ToLower();
|
||||
if (!botDb.Types.ContainsKey(desiredType)) {
|
||||
if (!botDb.Types.ContainsKey(desiredType))
|
||||
{
|
||||
// No bot found, get fallback difficulty values
|
||||
_logger.Warning(_localisationService.GetText("bot-unable_to_get_bot_fallback_to_assault", type));
|
||||
botDb.Types[desiredType] = _cloner.Clone(botDb.Types["assault"]);
|
||||
@@ -42,13 +43,19 @@ public class BotDifficultyHelper(
|
||||
// Get settings from raw bot json template file
|
||||
var botTemplate = _botHelper.GetBotTemplate(desiredType);
|
||||
botTemplate.BotDifficulty.TryGetValue(desiredDifficulty, out var difficultySettings);
|
||||
if (difficultySettings is null) {
|
||||
if (difficultySettings is null)
|
||||
{
|
||||
// No bot settings found, use 'assault' bot difficulty instead
|
||||
_logger.Warning(
|
||||
_localisationService.GetText("bot-unable_to_get_bot_difficulty_fallback_to_assault", new {
|
||||
botType = desiredType,
|
||||
difficulty = desiredDifficulty,
|
||||
}));
|
||||
_localisationService.GetText(
|
||||
"bot-unable_to_get_bot_difficulty_fallback_to_assault",
|
||||
new
|
||||
{
|
||||
botType = desiredType,
|
||||
difficulty = desiredDifficulty
|
||||
}
|
||||
)
|
||||
);
|
||||
botDb.Types[desiredType].BotDifficulty[desiredDifficulty] = _cloner.Clone(
|
||||
botDb.Types["assault"].BotDifficulty[desiredDifficulty]
|
||||
);
|
||||
@@ -82,7 +89,8 @@ public class BotDifficultyHelper(
|
||||
/// <returns>bot difficulty</returns>
|
||||
public string ConvertBotDifficultyDropdownToBotDifficulty(string dropDownDifficulty)
|
||||
{
|
||||
switch (dropDownDifficulty.ToLower()) {
|
||||
switch (dropDownDifficulty.ToLower())
|
||||
{
|
||||
case "medium":
|
||||
return "normal";
|
||||
case "random":
|
||||
|
||||
@@ -45,10 +45,7 @@ public class BotGeneratorHelper(
|
||||
var raidIsNight = raidSettings?.TimeVariant == DateTimeEnum.PAST;
|
||||
|
||||
RandomisedResourceDetails randomisationSettings = null;
|
||||
if (botRole is not null)
|
||||
{
|
||||
_botConfig.LootItemResourceRandomization.TryGetValue(botRole, out randomisationSettings);
|
||||
}
|
||||
if (botRole is not null) _botConfig.LootItemResourceRandomization.TryGetValue(botRole, out randomisationSettings);
|
||||
|
||||
|
||||
Upd itemProperties = new();
|
||||
@@ -109,7 +106,7 @@ public class BotGeneratorHelper(
|
||||
HpPercent = GetRandomizedResourceValue(
|
||||
itemTemplate.Properties.MaxResource ?? 0,
|
||||
randomisationSettings?.Food
|
||||
),
|
||||
)
|
||||
};
|
||||
hasProperties = true;
|
||||
}
|
||||
@@ -120,7 +117,7 @@ public class BotGeneratorHelper(
|
||||
var lightLaserActiveChance = raidIsNight
|
||||
? GetBotEquipmentSettingFromConfig(botRole, "lightIsActiveNightChancePercent", 50)
|
||||
: GetBotEquipmentSettingFromConfig(botRole, "lightIsActiveDayChancePercent", 25);
|
||||
itemProperties.Light = new UpdLight { IsActive = _randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0, };
|
||||
itemProperties.Light = new UpdLight { IsActive = _randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0 };
|
||||
hasProperties = true;
|
||||
}
|
||||
else if (itemTemplate?.Parent == BaseClasses.TACTICAL_COMBO)
|
||||
@@ -134,7 +131,7 @@ public class BotGeneratorHelper(
|
||||
itemProperties.Light = new UpdLight
|
||||
{
|
||||
IsActive = _randomUtil.GetChance100(lightLaserActiveChance),
|
||||
SelectedMode = 0,
|
||||
SelectedMode = 0
|
||||
};
|
||||
hasProperties = true;
|
||||
}
|
||||
@@ -173,15 +170,9 @@ public class BotGeneratorHelper(
|
||||
/// <returns>Randomized value from maxHpResource</returns>
|
||||
private double GetRandomizedResourceValue(double maxResource, RandomisedResourceValues? randomizationValues)
|
||||
{
|
||||
if (randomizationValues is null)
|
||||
{
|
||||
return maxResource;
|
||||
}
|
||||
if (randomizationValues is null) return maxResource;
|
||||
|
||||
if (_randomUtil.GetChance100(randomizationValues.ChanceMaxResourcePercent))
|
||||
{
|
||||
return maxResource;
|
||||
}
|
||||
if (_randomUtil.GetChance100(randomizationValues.ChanceMaxResourcePercent)) return maxResource;
|
||||
|
||||
return _randomUtil.GetInt(
|
||||
(int)_randomUtil.GetPercentOfValue(randomizationValues.ResourcePercent, maxResource, 0),
|
||||
@@ -198,10 +189,7 @@ public class BotGeneratorHelper(
|
||||
/// <returns>Percent chance to be active</returns>
|
||||
private double? GetBotEquipmentSettingFromConfig(string? botRole, string setting, double defaultValue)
|
||||
{
|
||||
if (botRole is null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
if (botRole is null) return defaultValue;
|
||||
|
||||
var botEquipmentSettings = _botConfig.Equipment[GetBotEquipmentRole(botRole)];
|
||||
if (botEquipmentSettings is null)
|
||||
@@ -299,10 +287,7 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
// Skip slots that have no incompatibilities
|
||||
List<string> slotsToCheck = ["Scabbard", "Backpack", "SecureContainer", "Holster", "ArmBand"];
|
||||
if (slotsToCheck.Contains(equipmentSlot))
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult { Incompatible = false, Found = false, Reason = "" };
|
||||
}
|
||||
if (slotsToCheck.Contains(equipmentSlot)) return new ChooseRandomCompatibleModResult { Incompatible = false, Found = false, Reason = "" };
|
||||
|
||||
// TODO: Can probably be optimized to cache itemTemplates as items are added to inventory
|
||||
var equippedItemsDb = itemsEquipped.Select(equippedItem => _itemHelper.GetItem(equippedItem.Template).Value).ToList();
|
||||
@@ -316,7 +301,7 @@ public class BotGeneratorHelper(
|
||||
new
|
||||
{
|
||||
itemTpl = tplToCheck,
|
||||
slot = equipmentSlot,
|
||||
slot = equipmentSlot
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -333,7 +318,7 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
id = itemToEquip?.Id,
|
||||
name = itemToEquip?.Name,
|
||||
slot = equipmentSlot,
|
||||
slot = equipmentSlot
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -347,20 +332,16 @@ public class BotGeneratorHelper(
|
||||
item => item?.Properties?.GetType().GetProperties().FirstOrDefault(x => x.Name.ToLower() == $"blocks{equipmentSlot}")?.GetValue(item) is not null
|
||||
);
|
||||
if (blockingItem is not null)
|
||||
{
|
||||
// this.logger.warning(`1 incompatibility found between - {itemToEquip[1]._name} and {blockingItem._name} - {equipmentSlot}`);
|
||||
|
||||
return new()
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true, Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} in slot: {equipmentSlot} blocked by: {blockingItem.Id} {blockingItem.Name}", SlotBlocked = true
|
||||
};
|
||||
}
|
||||
|
||||
// Check if any of the current inventory templates have the incoming item defined as incompatible
|
||||
blockingItem = templateItems.FirstOrDefault(x => x?.Properties?.ConflictingItems?.Contains(tplToCheck) ?? false);
|
||||
if (blockingItem is not null)
|
||||
{
|
||||
// this.logger.warning(`2 incompatibility found between - {itemToEquip[1]._name} and {blockingItem._props.Name} - {equipmentSlot}`);
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
@@ -369,14 +350,12 @@ public class BotGeneratorHelper(
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} in slot: {equipmentSlot} blocked by: {blockingItem.Id} {blockingItem.Name}",
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip.Properties.BlocksHeadwear ?? false)
|
||||
{
|
||||
var existingHeadwear = itemsEquipped.FirstOrDefault((x) => x.SlotId == "Headwear");
|
||||
if (existingHeadwear is not null)
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
@@ -384,7 +363,6 @@ public class BotGeneratorHelper(
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingHeadwear.Template} in slot: {existingHeadwear.SlotId}",
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
@@ -392,15 +370,13 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
var existingFaceCover = itemsEquipped.FirstOrDefault((item) => item.SlotId == "FaceCover");
|
||||
if (existingFaceCover is not null)
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingFaceCover.Template} in slot: {existingFaceCover.SlotId}",
|
||||
SlotBlocked = true,
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
@@ -408,15 +384,13 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
var existingEarpiece = itemsEquipped.FirstOrDefault((item) => item.SlotId == "Earpiece");
|
||||
if (existingEarpiece is not null)
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingEarpiece.Template} in slot: {existingEarpiece.SlotId}",
|
||||
SlotBlocked = true,
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Does item being checked get blocked/block existing item
|
||||
@@ -424,29 +398,25 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
var existingArmorVest = itemsEquipped.FirstOrDefault((item) => item.SlotId == "ArmorVest");
|
||||
if (existingArmorVest is not null)
|
||||
{
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} {itemToEquip.Name} is blocked by: {existingArmorVest.Template} in slot: {existingArmorVest.SlotId}",
|
||||
SlotBlocked = true,
|
||||
SlotBlocked = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the incoming item has any inventory items defined as incompatible
|
||||
var blockingInventoryItem = itemsEquipped.FirstOrDefault((x) => itemToEquip.Properties.ConflictingItems?.Contains(x.Template) ?? false);
|
||||
if (blockingInventoryItem is not null)
|
||||
{
|
||||
// this.logger.warning(`3 incompatibility found between - {itemToEquip[1]._name} and {blockingInventoryItem._tpl} - {equipmentSlot}`)
|
||||
return new ChooseRandomCompatibleModResult
|
||||
{
|
||||
Incompatible = true,
|
||||
Found = false,
|
||||
Reason = $"{tplToCheck} blocks existing item {blockingInventoryItem.Template} in slot {blockingInventoryItem.SlotId}",
|
||||
Reason = $"{tplToCheck} blocks existing item {blockingInventoryItem.Template} in slot {blockingInventoryItem.SlotId}"
|
||||
};
|
||||
}
|
||||
|
||||
return new ChooseRandomCompatibleModResult { Incompatible = false, Reason = "" };
|
||||
}
|
||||
@@ -488,13 +458,10 @@ public class BotGeneratorHelper(
|
||||
var missingContainerCount = 0;
|
||||
foreach (var equipmentSlotId in equipmentSlots)
|
||||
{
|
||||
if (containersIdFull?.Contains(equipmentSlotId.ToString()) ?? false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (containersIdFull?.Contains(equipmentSlotId.ToString()) ?? false) continue;
|
||||
|
||||
// Get container to put item into
|
||||
var container = (inventory.Items).FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
|
||||
var container = inventory.Items.FirstOrDefault(item => item.SlotId == equipmentSlotId.ToString());
|
||||
if (container is null)
|
||||
{
|
||||
missingContainerCount++;
|
||||
@@ -502,11 +469,9 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
// Bot doesn't have any containers we want to add item to
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(
|
||||
$"Unable to add item: {itemWithChildren.FirstOrDefault()?.Template} to bot as it lacks the following containers: {string.Join(",", equipmentSlots)}"
|
||||
);
|
||||
}
|
||||
|
||||
return ItemAddedResult.NO_CONTAINERS;
|
||||
}
|
||||
@@ -526,10 +491,8 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
if (value?.Properties?.Grids?.Count == 0)
|
||||
{
|
||||
// Container has no slots to hold items
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get x/y grid size of item
|
||||
var itemSize = _inventoryHelper.GetItemSize(rootItemTplId, rootItemId, itemWithChildren);
|
||||
@@ -543,16 +506,12 @@ public class BotGeneratorHelper(
|
||||
if (slotGrid.Props?.CellsH == 0 ||
|
||||
slotGrid.Props?.CellsV == 0 ||
|
||||
itemSize[0] * itemSize[1] > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can't put item type in grid, skip all grids as we're assuming they have the same rules
|
||||
if (!ItemAllowedInContainer(slotGrid, rootItemTplId))
|
||||
{
|
||||
// Multiple containers, maybe next one allows item, only break out of loop for the containers grids
|
||||
break;
|
||||
}
|
||||
|
||||
// Get all root items in found container
|
||||
var existingContainerItems = (inventory.Items ?? []).Where(
|
||||
@@ -591,7 +550,7 @@ public class BotGeneratorHelper(
|
||||
{
|
||||
X = findSlotResult.X,
|
||||
Y = findSlotResult.Y,
|
||||
R = (findSlotResult.Rotation ?? false) ? 1 : 0,
|
||||
R = findSlotResult.Rotation ?? false ? 1 : 0
|
||||
}
|
||||
;
|
||||
}
|
||||
@@ -603,10 +562,7 @@ public class BotGeneratorHelper(
|
||||
}
|
||||
|
||||
// If we've checked all grids in container and reached this point, there's no space for item
|
||||
if (currentGridCount >= totalSlotGridCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (currentGridCount >= totalSlotGridCount) break;
|
||||
|
||||
currentGridCount++;
|
||||
// No space in this grid, move to next container grid and try again
|
||||
@@ -616,10 +572,7 @@ public class BotGeneratorHelper(
|
||||
if (containersIdFull is null) continue;
|
||||
|
||||
// if the item was a one by one, we know it must be full. Or if the maps cant find a slot for a one by one
|
||||
if (itemSize[0] == 1 && itemSize[1] == 1)
|
||||
{
|
||||
containersIdFull.Add(equipmentSlotId.ToString());
|
||||
}
|
||||
if (itemSize[0] == 1 && itemSize[1] == 1) containersIdFull.Add(equipmentSlotId.ToString());
|
||||
}
|
||||
|
||||
return ItemAddedResult.NO_SPACE;
|
||||
@@ -660,31 +613,20 @@ public class BotGeneratorHelper(
|
||||
var filter = propFilters?.FirstOrDefault()?.Filter ?? [];
|
||||
|
||||
if (propFilters?.Count == 0)
|
||||
{
|
||||
// no filters, item is fine to add
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if item base type is excluded
|
||||
var itemDetails = _itemHelper.GetItem(itemTpl).Value;
|
||||
|
||||
// if item to add is found in exclude filter, not allowed
|
||||
if (excludedFilter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (excludedFilter.Contains(itemDetails?.Parent ?? string.Empty)) return false;
|
||||
|
||||
// If Filter array only contains 1 filter and its for basetype 'item', allow it
|
||||
if (filter.Count == 1 && filter.Contains(BaseClasses.ITEM))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (filter.Count == 1 && filter.Contains(BaseClasses.ITEM)) return true;
|
||||
|
||||
// If allowed filter has something in it + filter doesnt have basetype 'item', not allowed
|
||||
if (filter.Count > 0 && !filter.Contains(itemDetails?.Parent ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (filter.Count > 0 && !filter.Contains(itemDetails?.Parent ?? string.Empty)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,10 +74,7 @@ public class BotHelper(
|
||||
var friendlyBotTypesKey = "FRIENDLY_BOT_TYPES";
|
||||
|
||||
// Null guard
|
||||
if (difficultySettings.Mind[friendlyBotTypesKey] is null)
|
||||
{
|
||||
difficultySettings.Mind[friendlyBotTypesKey] = new List<string>();
|
||||
}
|
||||
if (difficultySettings.Mind[friendlyBotTypesKey] is null) difficultySettings.Mind[friendlyBotTypesKey] = new List<string>();
|
||||
|
||||
((List<string>)difficultySettings.Mind[friendlyBotTypesKey]).Add(typeToAdd);
|
||||
}
|
||||
@@ -92,25 +89,15 @@ public class BotHelper(
|
||||
var revengePropKey = "REVENGE_BOT_TYPES";
|
||||
|
||||
// Nothing to add
|
||||
if (typesToAdd is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (typesToAdd is null) return;
|
||||
|
||||
// Null guard
|
||||
if (difficultySettings.Mind[revengePropKey] is null)
|
||||
{
|
||||
difficultySettings.Mind[revengePropKey] = new List<string>();
|
||||
}
|
||||
if (difficultySettings.Mind[revengePropKey] is null) difficultySettings.Mind[revengePropKey] = new List<string>();
|
||||
|
||||
var revengeArray = (List<string>)difficultySettings.Mind[revengePropKey];
|
||||
foreach (var botTypeToAdd in typesToAdd)
|
||||
{
|
||||
if (!revengeArray.Contains(botTypeToAdd))
|
||||
{
|
||||
revengeArray.Add(botTypeToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool RollChanceToBePmc(MinMax botConvertMinMax)
|
||||
@@ -121,10 +108,7 @@ public class BotHelper(
|
||||
protected Dictionary<string, MinMax> GetPmcConversionValuesForLocation(string location)
|
||||
{
|
||||
var result = _pmcConfig.ConvertIntoPmcChance[location.ToLower()];
|
||||
if (result is null)
|
||||
{
|
||||
_pmcConfig.ConvertIntoPmcChance = new();
|
||||
}
|
||||
if (result is null) _pmcConfig.ConvertIntoPmcChance = new Dictionary<string, Dictionary<string, MinMax>>();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -151,10 +135,7 @@ public class BotHelper(
|
||||
public RandomisationDetails GetBotRandomizationDetails(int botLevel, EquipmentFilters botEquipConfig)
|
||||
{
|
||||
// No randomisation details found, skip
|
||||
if (botEquipConfig is null || botEquipConfig.Randomisation is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (botEquipConfig is null || botEquipConfig.Randomisation is null) return null;
|
||||
|
||||
return botEquipConfig.Randomisation.FirstOrDefault(
|
||||
(randDetails) => botLevel >= randDetails.LevelRange.Min && botLevel <= randDetails.LevelRange.Max
|
||||
@@ -203,7 +184,7 @@ public class BotHelper(
|
||||
/// <returns>name of PMC</returns>
|
||||
public string GetPmcNicknameOfMaxLength(int maxLength, string side = null)
|
||||
{
|
||||
var randomType = (side is not null) ? side : (_randomUtil.GetInt(0, 1) == 0) ? "usec" : "bear";
|
||||
var randomType = side is not null ? side : _randomUtil.GetInt(0, 1) == 0 ? "usec" : "bear";
|
||||
var allNames = _databaseService.GetBots().Types[randomType.ToLower()].FirstNames;
|
||||
var filteredNames = allNames.Where((name) => name.Length <= maxLength);
|
||||
if (filteredNames.Count() == 0)
|
||||
|
||||
@@ -113,11 +113,11 @@ public class BotWeaponGeneratorHelper(
|
||||
equipmentSlotsToAddTo = [EquipmentSlots.TacticalVest, EquipmentSlots.Pockets];
|
||||
|
||||
var ammoItems = _itemHelper.SplitStack(
|
||||
new()
|
||||
new Item
|
||||
{
|
||||
Id = _hashUtil.Generate(),
|
||||
Template = ammoTpl,
|
||||
Upd = new() { StackObjectsCount = cartridgeCount },
|
||||
Upd = new Upd { StackObjectsCount = cartridgeCount }
|
||||
}
|
||||
);
|
||||
|
||||
@@ -136,10 +136,8 @@ public class BotWeaponGeneratorHelper(
|
||||
_logger.Debug($"Unable to add ammo: {ammoItem.Template} to bot inventory, {result.ToString()}");
|
||||
|
||||
if (result == ItemAddedResult.NO_SPACE || result == ItemAddedResult.NO_CONTAINERS)
|
||||
{
|
||||
// If there's no space for 1 stack or no containers to hold item, there's no space for the others
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,29 +23,20 @@ public class ContainerHelper
|
||||
var limitX = containerX - minVolume;
|
||||
|
||||
// Every x+y slot taken up in container, exit
|
||||
if (container2D.All((x) => x.All((y) =>y == 1)))
|
||||
{
|
||||
return new FindSlotResult(false);
|
||||
}
|
||||
if (container2D.All((x) => x.All((y) => y == 1))) return new FindSlotResult(false);
|
||||
|
||||
// Down = y
|
||||
for (var y = 0; y < limitY; y++)
|
||||
{
|
||||
|
||||
if (container2D[y].All((x) => x == 1))
|
||||
{
|
||||
// Every item in row is full, skip row
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try each slot on the row (across = x)
|
||||
for (var x = 0; x < limitX; x++)
|
||||
{
|
||||
var foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight);
|
||||
if (foundSlot)
|
||||
{
|
||||
return new FindSlotResult(true, x, y, rotation);
|
||||
}
|
||||
if (foundSlot) return new FindSlotResult(true, x, y, rotation);
|
||||
|
||||
// Failed to find slot, rotate item and try again
|
||||
if (!foundSlot && ItemBiggerThan1X1(itemWidth, itemHeight))
|
||||
@@ -118,10 +109,7 @@ public class ContainerHelper
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSlot)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!foundSlot) break;
|
||||
}
|
||||
|
||||
return foundSlot;
|
||||
@@ -149,20 +137,12 @@ public class ContainerHelper
|
||||
var itemHeight = rotate ? itemW : itemH;
|
||||
|
||||
for (var tmpY = y; tmpY < y + itemHeight; tmpY++)
|
||||
{
|
||||
for (var tmpX = x; tmpX < x + itemWidth; tmpX++)
|
||||
{
|
||||
if (container2D[tmpY][tmpX] == 0)
|
||||
{
|
||||
// Flag slot as used
|
||||
container2D[tmpY][tmpX] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Slot at({ x }, { y}) is already filled. Cannot fit a { itemW} by { itemH} item");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var tmpX = x; tmpX < x + itemWidth; tmpX++)
|
||||
if (container2D[tmpY][tmpX] == 0)
|
||||
// Flag slot as used
|
||||
container2D[tmpY][tmpX] = 1;
|
||||
else
|
||||
throw new Exception($"Slot at({x}, {y}) is already filled. Cannot fit a {itemW} by {itemH} item");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,13 +167,13 @@ public class FindSlotResult
|
||||
|
||||
[JsonPropertyName("success")]
|
||||
public bool? Success { get; set; }
|
||||
|
||||
|
||||
[JsonPropertyName("x")]
|
||||
public int? X { get; set; }
|
||||
|
||||
|
||||
[JsonPropertyName("y")]
|
||||
public int? Y { get; set; }
|
||||
|
||||
|
||||
[JsonPropertyName("rotation")]
|
||||
public bool? Rotation { get; set; }
|
||||
}
|
||||
|
||||
@@ -30,14 +30,9 @@ public abstract class AbstractDialogChatBot(
|
||||
|
||||
var commandos = _chatCommands.Where(c => c.GetCommandPrefix() == splitCommand.FirstOrDefault());
|
||||
if (commandos.FirstOrDefault()?.GetCommands().Contains(splitCommand[1]) ?? false)
|
||||
{
|
||||
return commandos.FirstOrDefault().Handle(splitCommand[1], GetChatBot(), sessionId, request);
|
||||
}
|
||||
|
||||
if (splitCommand.FirstOrDefault()?.ToLower() == "help")
|
||||
{
|
||||
return SendPlayerHelpMessage(sessionId, request);
|
||||
}
|
||||
if (splitCommand.FirstOrDefault()?.ToLower() == "help") return SendPlayerHelpMessage(sessionId, request);
|
||||
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
@@ -77,7 +72,6 @@ public abstract class AbstractDialogChatBot(
|
||||
() =>
|
||||
{
|
||||
foreach (var subCommand in chatCommand.GetCommands())
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
GetChatBot(),
|
||||
@@ -85,7 +79,6 @@ public abstract class AbstractDialogChatBot(
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
},
|
||||
TimeSpan.FromSeconds(1)
|
||||
);
|
||||
@@ -100,11 +93,9 @@ public abstract class AbstractDialogChatBot(
|
||||
public void RegisterChatCommand(IChatCommand chatCommand)
|
||||
{
|
||||
if (_chatCommands.Any(cc => cc.GetCommandPrefix() == chatCommand.GetCommandPrefix()))
|
||||
{
|
||||
throw new Exception(
|
||||
$"The command \"{chatCommand.GetCommandPrefix()}\" attempting to be registered already exists."
|
||||
);
|
||||
}
|
||||
|
||||
_chatCommands.Add(chatCommand);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ namespace Core.Helpers.Dialog.Commando;
|
||||
[Injectable]
|
||||
public class SptCommandoCommands : IChatCommand
|
||||
{
|
||||
protected List<ISptCommand> _sptCommands;
|
||||
protected LocalisationService _localisationService;
|
||||
protected List<ISptCommand> _sptCommands;
|
||||
|
||||
public SptCommandoCommands(
|
||||
ConfigServer configServer,
|
||||
LocalisationService localisationService,
|
||||
@@ -31,19 +32,6 @@ public class SptCommandoCommands : IChatCommand
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterSptCommandoCommand(ISptCommand command)
|
||||
{
|
||||
if (_sptCommands.Any((c) => c.GetCommand() == command.GetCommand())) {
|
||||
throw new Exception(
|
||||
_localisationService.GetText(
|
||||
"chat-unable_to_register_command_already_registered",
|
||||
command.GetCommand()
|
||||
)
|
||||
);
|
||||
}
|
||||
_sptCommands.Add(command);
|
||||
}
|
||||
|
||||
public string GetCommandPrefix()
|
||||
{
|
||||
return "spt";
|
||||
@@ -65,4 +53,16 @@ public class SptCommandoCommands : IChatCommand
|
||||
.First((c) => c.GetCommand() == command)
|
||||
.PerformAction(commandHandler, sessionId, request);
|
||||
}
|
||||
|
||||
public void RegisterSptCommandoCommand(ISptCommand command)
|
||||
{
|
||||
if (_sptCommands.Any((c) => c.GetCommand() == command.GetCommand()))
|
||||
throw new Exception(
|
||||
_localisationService.GetText(
|
||||
"chat-unable_to_register_command_already_registered",
|
||||
command.GetCommand()
|
||||
)
|
||||
);
|
||||
_sptCommands.Add(command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,8 @@ public class GiveSptCommand(
|
||||
ICloner _cloner
|
||||
) : ISptCommand
|
||||
{
|
||||
protected Dictionary<string, SavedCommand> _savedCommand = new();
|
||||
private static readonly Regex _commandRegex = new(@"^spt give (((([a-z]{2,5}) )?""(.+)""|\w+) )?([0-9]+)$");
|
||||
private const double _acceptableConfidence = 0.9d;
|
||||
private static readonly Regex _commandRegex = new(@"^spt give (((([a-z]{2,5}) )?""(.+)""|\w+) )?([0-9]+)$");
|
||||
|
||||
// Exception for flares
|
||||
protected readonly HashSet<string> _excludedPresetItems =
|
||||
@@ -37,6 +36,8 @@ public class GiveSptCommand(
|
||||
ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_YELLOW
|
||||
];
|
||||
|
||||
protected Dictionary<string, SavedCommand> _savedCommand = new();
|
||||
|
||||
public string GetCommand()
|
||||
{
|
||||
return "give";
|
||||
@@ -104,10 +105,7 @@ public class GiveSptCommand(
|
||||
else
|
||||
{
|
||||
// A new give request was entered, we need to ignore the old saved command
|
||||
if (_savedCommand.ContainsKey(sessionId))
|
||||
{
|
||||
_savedCommand.Remove(sessionId);
|
||||
}
|
||||
if (_savedCommand.ContainsKey(sessionId)) _savedCommand.Remove(sessionId);
|
||||
|
||||
isItemName = result.Groups[5].Value != null;
|
||||
item = result.Groups[5].Value is not null ? result.Groups[5].Value : result.Groups[2].Value;
|
||||
|
||||
@@ -7,7 +7,6 @@ public class SavedCommand
|
||||
{
|
||||
public SavedCommand()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SavedCommand(int quantity, List<string> potentialItemNames, string locale)
|
||||
|
||||
+8
-6
@@ -9,22 +9,25 @@ public static class StringSimilarity
|
||||
*/
|
||||
public static double Match(string str1, string str2, int substringLength = 2, bool caseSensitive = false)
|
||||
{
|
||||
if (!caseSensitive) {
|
||||
if (!caseSensitive)
|
||||
{
|
||||
str1 = str1.ToLower();
|
||||
str2 = str2.ToLower();
|
||||
}
|
||||
|
||||
if (str1.Length < substringLength || str2.Length < substringLength)
|
||||
return 0;
|
||||
|
||||
|
||||
var map = new Dictionary<string, int>();
|
||||
for (var i = 0; i < str1.Length - (substringLength - 1); i++) {
|
||||
for (var i = 0; i < str1.Length - (substringLength - 1); i++)
|
||||
{
|
||||
var substr1 = str1.Substring(i, substringLength);
|
||||
map.Add(substr1, map.TryGetValue(substr1, out var value) ? value + 1 : 1);
|
||||
}
|
||||
|
||||
var match = 0;
|
||||
for (var j = 0; j < str2.Length - (substringLength - 1); j++) {
|
||||
for (var j = 0; j < str2.Length - (substringLength - 1); j++)
|
||||
{
|
||||
var substr2 = str2.Substring(j, substringLength);
|
||||
var count = map.GetValueOrDefault(substr2, 0);
|
||||
if (count > 0)
|
||||
@@ -34,7 +37,6 @@ public static class StringSimilarity
|
||||
}
|
||||
}
|
||||
|
||||
return match * 2d / (str1.Length + str2.Length - ((substringLength - 1d) * 2d));
|
||||
return match * 2d / (str1.Length + str2.Length - (substringLength - 1d) * 2d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,31 +5,31 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SptMessageHandlers
|
||||
namespace Core.Helpers.Dialogue.SptMessageHandlers;
|
||||
|
||||
[Injectable]
|
||||
public class AreYouABotMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class AreYouABotMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
public int GetPriority()
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "are you a bot";
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "are you a bot";
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
|
||||
[], null
|
||||
);
|
||||
}
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-27
@@ -5,38 +5,39 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class ForceChristmasMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
SeasonalEventService _seasonalEventService) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class ForceChristmasMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
SeasonalEventService _seasonalEventService) : IChatMessageHandler
|
||||
public int GetPriority()
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
return 99;
|
||||
}
|
||||
return 99;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "hohoho";
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "hohoho";
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
var enableEventResult = _seasonalEventService.ForceSeasonalEvent(SeasonalEventType.Christmas);
|
||||
if (enableEventResult)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
var enableEventResult = _seasonalEventService.ForceSeasonalEvent(SeasonalEventType.Christmas);
|
||||
if (enableEventResult)
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
_localisationService.GetText("chatbot-forced_event_enabled", SeasonalEventType.Christmas)
|
||||
]), [], null
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+29
-30
@@ -5,40 +5,39 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class ForceHalloweenMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
SeasonalEventService _seasonalEventService) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class ForceHalloweenMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
SeasonalEventService _seasonalEventService) : IChatMessageHandler
|
||||
public int GetPriority()
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
return 99;
|
||||
}
|
||||
return 99;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "veryspooky";
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "veryspooky";
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
var enableEventResult = _seasonalEventService.ForceSeasonalEvent(SeasonalEventType.Halloween);
|
||||
if (enableEventResult)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
var enableEventResult = _seasonalEventService.ForceSeasonalEvent(SeasonalEventType.Halloween);
|
||||
if (enableEventResult)
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
_localisationService.GetText("chatbot-forced_event_enabled", SeasonalEventType.Halloween)
|
||||
]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,38 +7,37 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class ForceSnowMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class ForceSnowMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
private WeatherConfig _weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
private WeatherConfig _weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
return 99;
|
||||
}
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
return 99;
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "itsonlysnowalan";
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "itsonlysnowalan";
|
||||
}
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_weatherConfig.OverrideSeason = Season.WINTER;
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_weatherConfig.OverrideSeason = Season.WINTER;
|
||||
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([_localisationService.GetText("chatbot-snow_enabled")]),
|
||||
_randomUtil.GetArrayValue([_localisationService.GetText("chatbot-snow_enabled")]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,38 +7,37 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class ForceSummerMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class ForceSummerMessageHandler(
|
||||
LocalisationService _localisationService,
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
private WeatherConfig _weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
private WeatherConfig _weatherConfig = _configServer.GetConfig<WeatherConfig>();
|
||||
return 99;
|
||||
}
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
return 99;
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "givemesunshine";
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "givemesunshine";
|
||||
}
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_weatherConfig.OverrideSeason = Season.SUMMER;
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_weatherConfig.OverrideSeason = Season.SUMMER;
|
||||
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([_localisationService.GetText("chatbot-summer_enabled")]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([_localisationService.GetText("chatbot-summer_enabled")]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ public class GiveMeSpaceMessageHandler(
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_localisationService.GetText("chatbot-cannot_accept_any_more_of_gift"), [], null
|
||||
_localisationService.GetText("chatbot-cannot_accept_any_more_of_gift"),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
else
|
||||
@@ -47,9 +49,13 @@ public class GiveMeSpaceMessageHandler(
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
_localisationService.GetText("chatbot-added_stash_rows_please_restart"),
|
||||
]), [], null
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
_localisationService.GetText("chatbot-added_stash_rows_please_restart")
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
_profileHelper.FlagGiftReceivedInProfile(sessionId, stashRowGiftId, maxGiftsToSendCount);
|
||||
|
||||
@@ -6,74 +6,33 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class HelloMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class HelloMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
protected List<string> _listOfMessages = ["hello", "hi", "sup", "yo", "hey", "bonjour"];
|
||||
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
protected List<string> _listOfMessages = ["hello", "hi", "sup", "yo", "hey", "bonjour"];
|
||||
return 100;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return _listOfMessages.Contains(message, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string GetCommand()
|
||||
{
|
||||
return "hello";
|
||||
}
|
||||
|
||||
public string GetAssociatedBotId()
|
||||
{
|
||||
return "6723fd51c5924c57ce0ca01f";
|
||||
}
|
||||
|
||||
public string GetCommandHelp()
|
||||
{
|
||||
return "'hello' replies to the player with a random greeting";
|
||||
}
|
||||
|
||||
public string PerformAction(UserDialogInfo commandHandler, string sessionId, SendMessageRequest request)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
commandHandler,
|
||||
_randomUtil.GetArrayValue([
|
||||
"Howdy",
|
||||
"Hi",
|
||||
"Greetings",
|
||||
"Hello",
|
||||
"Bonjor",
|
||||
"Yo",
|
||||
"Sup",
|
||||
"Heyyyyy",
|
||||
"Hey there",
|
||||
"OH its you"
|
||||
]),
|
||||
[], null
|
||||
);
|
||||
|
||||
return request.DialogId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return _listOfMessages.Contains(message, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
"Howdy",
|
||||
"Hi",
|
||||
"Greetings",
|
||||
@@ -84,10 +43,53 @@ namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
"Heyyyyy",
|
||||
"Hey there",
|
||||
"OH its you",
|
||||
$"Hello {sender?.Info?.Nickname}",
|
||||
]),
|
||||
[], null
|
||||
);
|
||||
}
|
||||
$"Hello {sender?.Info?.Nickname}"
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public string GetCommand()
|
||||
{
|
||||
return "hello";
|
||||
}
|
||||
|
||||
public string GetAssociatedBotId()
|
||||
{
|
||||
return "6723fd51c5924c57ce0ca01f";
|
||||
}
|
||||
|
||||
public string GetCommandHelp()
|
||||
{
|
||||
return "'hello' replies to the player with a random greeting";
|
||||
}
|
||||
|
||||
public string PerformAction(UserDialogInfo commandHandler, string sessionId, SendMessageRequest request)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
commandHandler,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
"Howdy",
|
||||
"Hi",
|
||||
"Greetings",
|
||||
"Hello",
|
||||
"Bonjor",
|
||||
"Yo",
|
||||
"Sup",
|
||||
"Heyyyyy",
|
||||
"Hey there",
|
||||
"OH its you"
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
return request.DialogId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,14 @@ public class LoveYouChatMessageHandler(
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
"That's quite forward but i love you too in a purely chatbot-human way",
|
||||
"I love you too buddy :3!",
|
||||
"uwu",
|
||||
$"love you too {sender?.Info?.Nickname}",
|
||||
]),
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
"That's quite forward but i love you too in a purely chatbot-human way",
|
||||
"I love you too buddy :3!",
|
||||
"uwu",
|
||||
$"love you too {sender?.Info?.Nickname}"
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
@@ -4,36 +4,39 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class NikitaMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class NikitaMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil) : IChatMessageHandler
|
||||
public int GetPriority()
|
||||
{
|
||||
public int GetPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "nikita";
|
||||
}
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return message.ToLower() == "nikita";
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
"I know that guy!",
|
||||
"Cool guy, he made EFT!",
|
||||
"Legend",
|
||||
"The mastermind of my suffering",
|
||||
"Remember when he said webel-webel-webel-webel, classic Nikita moment",
|
||||
]), [], null
|
||||
);
|
||||
}
|
||||
"Remember when he said webel-webel-webel-webel, classic Nikita moment"
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,65 +7,64 @@ using Core.Services;
|
||||
using Core.Utils;
|
||||
using SptCommon.Annotations;
|
||||
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands
|
||||
namespace Core.Helpers.Dialogue.SPTFriend.Commands;
|
||||
|
||||
[Injectable]
|
||||
public class SendGiftMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
GiftService _giftService,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
{
|
||||
[Injectable]
|
||||
public class SendGiftMessageHandler(
|
||||
MailSendService _mailSendService,
|
||||
RandomUtil _randomUtil,
|
||||
GiftService _giftService,
|
||||
ConfigServer _configServer) : IChatMessageHandler
|
||||
private CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
|
||||
private string commandSent = string.Empty;
|
||||
|
||||
public int GetPriority()
|
||||
{
|
||||
private CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
|
||||
private string commandSent = string.Empty;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int GetPriority()
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return _giftService.GiftExists(message.ToLower());
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
// Gifts may be disabled via config
|
||||
if (!_coreConfig.Features.ChatbotFeatures.SptFriendGiftsEnabled) return;
|
||||
|
||||
var giftSent = _giftService.SendGiftToPlayer(sessionId, commandSent);
|
||||
switch (giftSent)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public bool CanHandle(string message)
|
||||
{
|
||||
return _giftService.GiftExists(message.ToLower());
|
||||
}
|
||||
|
||||
public void Process(string sessionId, UserDialogInfo sptFriendUser, PmcData sender)
|
||||
{
|
||||
// Gifts may be disabled via config
|
||||
if (!_coreConfig.Features.ChatbotFeatures.SptFriendGiftsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var giftSent = _giftService.SendGiftToPlayer(sessionId, commandSent);
|
||||
switch (giftSent)
|
||||
{
|
||||
case GiftSentResult.SUCCESS:
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue([
|
||||
case GiftSentResult.SUCCESS:
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(
|
||||
[
|
||||
"Hey! you got the right code!",
|
||||
"A secret code, how exciting!",
|
||||
"You found a gift code!",
|
||||
"A gift code! incredible",
|
||||
"A gift! what could it be!"]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
"A gift! what could it be!"
|
||||
]
|
||||
),
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
return;
|
||||
case GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED:
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(["Looks like you already used that code", "You already have that!!"]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
return;
|
||||
case GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED:
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
_randomUtil.GetArrayValue(["Looks like you already used that code", "You already have that!!"]),
|
||||
[],
|
||||
null
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,8 @@ public class SptDialogueChatBot(
|
||||
{
|
||||
protected IEnumerable<IChatMessageHandler> _chatMessageHandlers = ChatMessageHandlerSetup(chatMessageHandlers);
|
||||
|
||||
private static List<IChatMessageHandler> ChatMessageHandlerSetup(IEnumerable<IChatMessageHandler> components)
|
||||
{
|
||||
var chatMessageHandlers = components.ToList();
|
||||
chatMessageHandlers.Sort((a, b) => a.GetPriority() - b.GetPriority());
|
||||
|
||||
return chatMessageHandlers;
|
||||
}
|
||||
|
||||
protected CoreConfig _coreConfig = _configServer.GetConfig<CoreConfig>();
|
||||
|
||||
|
||||
|
||||
public UserDialogInfo GetChatBot()
|
||||
{
|
||||
@@ -57,12 +49,9 @@ public class SptDialogueChatBot(
|
||||
var sender = _profileHelper.GetPmcProfile(sessionId);
|
||||
var sptFriendUser = GetChatBot();
|
||||
|
||||
if (request.Text?.ToLower() == "help")
|
||||
{
|
||||
return SendPlayerHelpMessage(sessionId, request);
|
||||
}
|
||||
if (request.Text?.ToLower() == "help") return SendPlayerHelpMessage(sessionId, request);
|
||||
|
||||
|
||||
|
||||
var handler = _chatMessageHandlers.FirstOrDefault((v) => v.CanHandle(request.Text));
|
||||
if (handler is not null)
|
||||
{
|
||||
@@ -70,7 +59,7 @@ public class SptDialogueChatBot(
|
||||
|
||||
return request.DialogId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
@@ -83,6 +72,14 @@ public class SptDialogueChatBot(
|
||||
return request.DialogId;
|
||||
}
|
||||
|
||||
private static List<IChatMessageHandler> ChatMessageHandlerSetup(IEnumerable<IChatMessageHandler> components)
|
||||
{
|
||||
var chatMessageHandlers = components.ToList();
|
||||
chatMessageHandlers.Sort((a, b) => a.GetPriority() - b.GetPriority());
|
||||
|
||||
return chatMessageHandlers;
|
||||
}
|
||||
|
||||
private string GetUnrecognizedCommandMessage()
|
||||
{
|
||||
return "Unknown command.";
|
||||
@@ -115,7 +112,6 @@ public class SptDialogueChatBot(
|
||||
() =>
|
||||
{
|
||||
foreach (var subCommand in chatCommand.GetCommands())
|
||||
{
|
||||
_mailSendService.SendUserMessageToPlayer(
|
||||
sessionId,
|
||||
GetChatBot(),
|
||||
@@ -123,7 +119,6 @@ public class SptDialogueChatBot(
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
},
|
||||
TimeSpan.FromSeconds(1)
|
||||
);
|
||||
|
||||
@@ -96,7 +96,7 @@ public class DialogueHelper(
|
||||
public Dictionary<string, Models.Eft.Profile.Dialogue> GetDialogsForProfile(string sessionId)
|
||||
{
|
||||
var profile = _profileHelper.GetFullProfile(sessionId);
|
||||
return profile.DialogueRecords ?? (profile.DialogueRecords = new());
|
||||
return profile.DialogueRecords ?? (profile.DialogueRecords = new Dictionary<string, Models.Eft.Profile.Dialogue>());
|
||||
}
|
||||
|
||||
public Models.Eft.Profile.Dialogue? GetDialogueFromProfile(string profileId, string dialogueId)
|
||||
|
||||
@@ -14,7 +14,6 @@ public class DurabilityLimitsHelper(
|
||||
BotHelper _botHelper,
|
||||
ConfigServer _configServer)
|
||||
{
|
||||
|
||||
private readonly BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
|
||||
|
||||
/// <summary>
|
||||
@@ -39,25 +38,13 @@ public class DurabilityLimitsHelper(
|
||||
public double GetRandomizedMaxArmorDurability(TemplateItem? itemTemplate, string? botRole = null)
|
||||
{
|
||||
var itemMaxDurability = itemTemplate.Properties.MaxDurability.Value;
|
||||
if (botRole is null)
|
||||
{
|
||||
return itemMaxDurability;
|
||||
}
|
||||
if (botRole is null) return itemMaxDurability;
|
||||
|
||||
if (_botHelper.IsBotPmc(botRole))
|
||||
{
|
||||
return GenerateMaxPmcArmorDurability(itemMaxDurability);
|
||||
}
|
||||
if (_botHelper.IsBotPmc(botRole)) return GenerateMaxPmcArmorDurability(itemMaxDurability);
|
||||
|
||||
if (_botHelper.IsBotBoss(botRole))
|
||||
{
|
||||
return itemMaxDurability;
|
||||
}
|
||||
if (_botHelper.IsBotBoss(botRole)) return itemMaxDurability;
|
||||
|
||||
if (_botHelper.IsBotFollower(botRole))
|
||||
{
|
||||
return itemMaxDurability;
|
||||
}
|
||||
if (_botHelper.IsBotFollower(botRole)) return itemMaxDurability;
|
||||
|
||||
return itemMaxDurability;
|
||||
}
|
||||
@@ -83,30 +70,15 @@ public class DurabilityLimitsHelper(
|
||||
/// <returns></returns>
|
||||
private string GetDurabilityRole(string? botRole)
|
||||
{
|
||||
if (botRole is null)
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
if (botRole is null) return "default";
|
||||
|
||||
if (_botHelper.IsBotPmc(botRole))
|
||||
{
|
||||
return "pmc";
|
||||
}
|
||||
if (_botHelper.IsBotPmc(botRole)) return "pmc";
|
||||
|
||||
if (_botHelper.IsBotBoss(botRole))
|
||||
{
|
||||
return "boss";
|
||||
}
|
||||
if (_botHelper.IsBotBoss(botRole)) return "boss";
|
||||
|
||||
if (_botHelper.IsBotFollower(botRole))
|
||||
{
|
||||
return "follower";
|
||||
}
|
||||
if (_botHelper.IsBotFollower(botRole)) return "follower";
|
||||
|
||||
if (_botHelper.IsBotZombie(botRole))
|
||||
{
|
||||
return "zombie";
|
||||
}
|
||||
if (_botHelper.IsBotZombie(botRole)) return "zombie";
|
||||
|
||||
var roleExistsInConfig = _botConfig.Durability.BotDurabilities.ContainsKey(botRole);
|
||||
if (!roleExistsInConfig)
|
||||
@@ -152,15 +124,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetLowestMaxWeaponFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Weapon.LowestMax;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Weapon.LowestMax;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Weapon.LowestMax;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Weapon.LowestMax;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var durability);
|
||||
return durability.Weapon.LowestMax;
|
||||
@@ -168,15 +134,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetHighestMaxWeaponDurabilityFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Weapon.HighestMax;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Weapon.HighestMax;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Weapon.HighestMax;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Weapon.HighestMax;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var durability);
|
||||
return durability.Weapon.HighestMax;
|
||||
@@ -189,7 +149,8 @@ public class DurabilityLimitsHelper(
|
||||
var delta = _randomUtil.GetInt(minDelta, maxDelta);
|
||||
var result = maxDurability - delta;
|
||||
var durabilityValueMinLimit = Math.Round(
|
||||
(GetMinWeaponLimitPercentFromConfig(botRole) / 100) * maxDurability);
|
||||
GetMinWeaponLimitPercentFromConfig(botRole) / 100 * maxDurability
|
||||
);
|
||||
|
||||
// Don't let weapon dura go below the percent defined in config
|
||||
return result >= durabilityValueMinLimit ? result : durabilityValueMinLimit;
|
||||
@@ -202,7 +163,8 @@ public class DurabilityLimitsHelper(
|
||||
var delta = _randomUtil.GetInt(minDelta, maxDelta);
|
||||
var result = maxDurability - delta;
|
||||
var durabilityValueMinLimit = Math.Round(
|
||||
(GetMinArmorLimitPercentFromConfig(botRole) / 100) * maxDurability);
|
||||
GetMinArmorLimitPercentFromConfig(botRole) / 100 * maxDurability
|
||||
);
|
||||
|
||||
// Don't let armor dura go below the percent defined in config
|
||||
return result >= durabilityValueMinLimit ? result : durabilityValueMinLimit;
|
||||
@@ -210,15 +172,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetMinWeaponDeltaFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Weapon.MinDelta;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Weapon.MinDelta;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Weapon.MinDelta;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Weapon.MinDelta;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
@@ -227,15 +183,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetMaxWeaponDeltaFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Weapon.MaxDelta;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Weapon.MaxDelta;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Weapon.MaxDelta;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Weapon.MaxDelta;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
@@ -244,15 +194,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetMinArmorDeltaFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Armor.MinDelta;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Armor.MinDelta;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Armor.MinDelta;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Armor.MinDelta;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
@@ -261,15 +205,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected int GetMaxArmorDeltaFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Armor.MaxDelta;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Armor.MaxDelta;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Armor.MaxDelta;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Armor.MaxDelta;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
@@ -278,15 +216,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected double GetMinArmorLimitPercentFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Armor.MinLimitPercent;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Armor.MinLimitPercent;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Armor.MinLimitPercent;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Armor.MinLimitPercent;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
@@ -295,15 +227,9 @@ public class DurabilityLimitsHelper(
|
||||
|
||||
protected double GetMinWeaponLimitPercentFromConfig(string? botRole = null)
|
||||
{
|
||||
if (botRole is null or "default")
|
||||
{
|
||||
return _botConfig.Durability.Default.Weapon.MinLimitPercent;
|
||||
}
|
||||
if (botRole is null or "default") return _botConfig.Durability.Default.Weapon.MinLimitPercent;
|
||||
|
||||
if (botRole == "pmc")
|
||||
{
|
||||
return _botConfig.Durability.Pmc.Weapon.MinLimitPercent;
|
||||
}
|
||||
if (botRole == "pmc") return _botConfig.Durability.Pmc.Weapon.MinLimitPercent;
|
||||
|
||||
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
|
||||
|
||||
|
||||
@@ -5,5 +5,4 @@ namespace Core.Helpers;
|
||||
[Injectable]
|
||||
public class GameEventHelper
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ public class HandbookHelper(
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
protected LookupCollection _handbookPriceCache = new();
|
||||
protected ItemConfig _itemConfig = _configServer.GetConfig<ItemConfig>();
|
||||
protected bool _lookupCacheGenerated = false;
|
||||
protected LookupCollection _handbookPriceCache = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an in-memory cache of all items with associated handbook price in handbookPriceCache class
|
||||
/// </summary>
|
||||
@@ -26,16 +26,21 @@ public class HandbookHelper(
|
||||
{
|
||||
var handbook = _databaseService.GetHandbook();
|
||||
// Add handbook overrides found in items.json config into db
|
||||
foreach (var itemTplKey in _itemConfig.HandbookPriceOverride) {
|
||||
foreach (var itemTplKey in _itemConfig.HandbookPriceOverride)
|
||||
{
|
||||
var data = _itemConfig.HandbookPriceOverride[itemTplKey.Key];
|
||||
|
||||
var itemToUpdate = handbook.Items.FirstOrDefault(item => item.Id == itemTplKey.Key);
|
||||
if (itemToUpdate is null) {
|
||||
handbook.Items.Add( new HandbookItem {
|
||||
Id = itemTplKey.Key,
|
||||
ParentId = data.ParentId,
|
||||
Price = data.Price,
|
||||
});
|
||||
if (itemToUpdate is null)
|
||||
{
|
||||
handbook.Items.Add(
|
||||
new HandbookItem
|
||||
{
|
||||
Id = itemTplKey.Key,
|
||||
ParentId = data.ParentId,
|
||||
Price = data.Price
|
||||
}
|
||||
);
|
||||
itemToUpdate = handbook.Items.FirstOrDefault(item => item.Id == itemTplKey.Key);
|
||||
}
|
||||
|
||||
@@ -43,22 +48,23 @@ public class HandbookHelper(
|
||||
}
|
||||
|
||||
var handbookDbClone = _cloner.Clone(handbook);
|
||||
foreach (var handbookItem in handbookDbClone.Items) {
|
||||
foreach (var handbookItem in handbookDbClone.Items)
|
||||
{
|
||||
_handbookPriceCache.Items.ById.TryAdd(handbookItem.Id, handbookItem.Price ?? 0);
|
||||
if (!_handbookPriceCache.Items.ByParent.TryGetValue(handbookItem.ParentId, out var _)) {
|
||||
if (!_handbookPriceCache.Items.ByParent.TryGetValue(handbookItem.ParentId, out _))
|
||||
_handbookPriceCache.Items.ByParent.TryAdd(handbookItem.ParentId, []);
|
||||
}
|
||||
|
||||
_handbookPriceCache.Items.ByParent.TryGetValue(handbookItem.ParentId, out var array);
|
||||
array.Add(handbookItem.Id);
|
||||
}
|
||||
|
||||
foreach (var handbookCategory in handbookDbClone.Categories) {
|
||||
foreach (var handbookCategory in handbookDbClone.Categories)
|
||||
{
|
||||
_handbookPriceCache.Categories.ById.TryAdd(handbookCategory.Id, handbookCategory.ParentId);
|
||||
if (handbookCategory.ParentId is not null) {
|
||||
if (!_handbookPriceCache.Categories.ByParent.TryGetValue(handbookCategory.ParentId, out var _)) {
|
||||
if (handbookCategory.ParentId is not null)
|
||||
{
|
||||
if (!_handbookPriceCache.Categories.ByParent.TryGetValue(handbookCategory.ParentId, out _))
|
||||
_handbookPriceCache.Categories.ByParent.TryAdd(handbookCategory.ParentId, []);
|
||||
}
|
||||
|
||||
_handbookPriceCache.Categories.ByParent.TryGetValue(handbookCategory.ParentId, out var array);
|
||||
array.Add(handbookCategory.Id);
|
||||
@@ -80,28 +86,19 @@ public class HandbookHelper(
|
||||
_lookupCacheGenerated = true;
|
||||
}
|
||||
|
||||
if (_handbookPriceCache.Items.ById.TryGetValue(tpl, out var item))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
if (_handbookPriceCache.Items.ById.TryGetValue(tpl, out var item)) return item;
|
||||
|
||||
var handbookItem = _databaseService.GetHandbook().Items?.FirstOrDefault(item => item.Id == tpl);
|
||||
var handbookItem = _databaseService.GetHandbook().Items?.FirstOrDefault(item => item.Id == tpl);
|
||||
if (handbookItem is null)
|
||||
{
|
||||
var newValue = 0;
|
||||
|
||||
if (!_handbookPriceCache.Items.ById.TryAdd(tpl, newValue))
|
||||
{
|
||||
_handbookPriceCache.Items.ById[tpl] = newValue;
|
||||
}
|
||||
if (!_handbookPriceCache.Items.ById.TryAdd(tpl, newValue)) _handbookPriceCache.Items.ById[tpl] = newValue;
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
if (!_handbookPriceCache.Items.ById.TryAdd(tpl, handbookItem.Price ?? 0))
|
||||
{
|
||||
_handbookPriceCache.Items.ById[tpl] = handbookItem.Price ?? 0;
|
||||
}
|
||||
if (!_handbookPriceCache.Items.ById.TryAdd(tpl, handbookItem.Price ?? 0)) _handbookPriceCache.Items.ById[tpl] = handbookItem.Price ?? 0;
|
||||
|
||||
return handbookItem.Price.Value;
|
||||
}
|
||||
@@ -109,9 +106,7 @@ public class HandbookHelper(
|
||||
public double GetTemplatePriceForItems(List<Item> items)
|
||||
{
|
||||
var total = 0D;
|
||||
foreach (var item in items) {
|
||||
total += GetTemplatePrice(item.Template);
|
||||
}
|
||||
foreach (var item in items) total += GetTemplatePrice(item.Template);
|
||||
|
||||
return total;
|
||||
}
|
||||
@@ -124,7 +119,7 @@ public class HandbookHelper(
|
||||
public List<string> TemplatesWithParent(string parentId)
|
||||
{
|
||||
_handbookPriceCache.Items.ByParent.TryGetValue(parentId, out var template);
|
||||
|
||||
|
||||
return template ?? [];
|
||||
}
|
||||
|
||||
@@ -135,7 +130,7 @@ public class HandbookHelper(
|
||||
/// <returns>true if exists in cache</returns>
|
||||
public bool IsCategory(string category)
|
||||
{
|
||||
return _handbookPriceCache.Categories.ById.TryGetValue(category, out var _);
|
||||
return _handbookPriceCache.Categories.ById.TryGetValue(category, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -157,9 +152,9 @@ public class HandbookHelper(
|
||||
/// <returns>Count in roubles</returns>
|
||||
public int InRUB(double nonRoubleCurrencyCount, string currencyTypeFrom)
|
||||
{
|
||||
return (int) (currencyTypeFrom == Money.ROUBLES
|
||||
? nonRoubleCurrencyCount
|
||||
: Math.Round(nonRoubleCurrencyCount * (GetTemplatePrice(currencyTypeFrom))));
|
||||
return (int)(currencyTypeFrom == Money.ROUBLES
|
||||
? nonRoubleCurrencyCount
|
||||
: Math.Round(nonRoubleCurrencyCount * GetTemplatePrice(currencyTypeFrom)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -170,13 +165,11 @@ public class HandbookHelper(
|
||||
/// <returns>currency count in desired type</returns>
|
||||
public int FromRUB(double roubleCurrencyCount, string currencyTypeTo)
|
||||
{
|
||||
if (currencyTypeTo == Money.ROUBLES) {
|
||||
return (int) roubleCurrencyCount;
|
||||
}
|
||||
if (currencyTypeTo == Money.ROUBLES) return (int)roubleCurrencyCount;
|
||||
|
||||
// Get price of currency from handbook
|
||||
var price = GetTemplatePrice(currencyTypeTo);
|
||||
return (int) (price > 0 ? Math.Max(1, Math.Round(roubleCurrencyCount / price)) : 0);
|
||||
return (int)(price > 0 ? Math.Max(1, Math.Round(roubleCurrencyCount / price)) : 0);
|
||||
}
|
||||
|
||||
public HandbookCategory GetCategoryById(string handbookId)
|
||||
@@ -187,24 +180,24 @@ public class HandbookHelper(
|
||||
|
||||
public class LookupItem<T, I>
|
||||
{
|
||||
public Dictionary<string, T> ById { get; set; }
|
||||
public Dictionary<string, List<I>> ByParent { get; set; }
|
||||
|
||||
public LookupItem()
|
||||
{
|
||||
ById = new Dictionary<string, T>();
|
||||
ByParent = new Dictionary<string, List<I>>();
|
||||
}
|
||||
|
||||
public Dictionary<string, T> ById { get; set; }
|
||||
public Dictionary<string, List<I>> ByParent { get; set; }
|
||||
}
|
||||
|
||||
public class LookupCollection
|
||||
{
|
||||
public LookupItem<double, string> Items { get; set; }
|
||||
public LookupItem<string, string> Categories { get; set; }
|
||||
|
||||
public LookupCollection()
|
||||
{
|
||||
Items = new LookupItem<double, string>();
|
||||
Categories = new LookupItem<string, string>();
|
||||
}
|
||||
|
||||
public LookupItem<double, string> Items { get; set; }
|
||||
public LookupItem<string, string> Categories { get; set; }
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class HealthHelper(
|
||||
public void DefaultVitality(Vitality? vitality)
|
||||
{
|
||||
vitality ??= new Vitality { Health = null, Energy = 0, Temperature = 0, Hydration = 0 };
|
||||
|
||||
|
||||
vitality.Health = new Dictionary<string, BodyPartHealth>
|
||||
{
|
||||
{
|
||||
@@ -47,7 +47,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -107,7 +107,7 @@ public class HealthHelper(
|
||||
{
|
||||
Health = new CurrentMinMax
|
||||
{
|
||||
Current = 0,
|
||||
Current = 0
|
||||
},
|
||||
Effects = new Dictionary<string, BodyPartEffectProperties>()
|
||||
}
|
||||
@@ -153,20 +153,15 @@ public class HealthHelper(
|
||||
{
|
||||
// Effects
|
||||
if (postRaidHealth.BodyParts[bodyPart.Key].Effects is not null)
|
||||
{
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Effects = postRaidHealth.BodyParts[bodyPart.Key].Effects;
|
||||
}
|
||||
|
||||
// Limb hp
|
||||
if (!isDead)
|
||||
{
|
||||
// Player alive, not is limb alive
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Health.Current = postRaidHealth.BodyParts[bodyPart.Key].Health.Current ?? 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Health.Current = (pmcData.Health.BodyParts[bodyPart.Key].Health.Maximum * _healthConfig.HealthMultipliers.Death) ?? 0;
|
||||
}
|
||||
fullProfile.VitalityData.Health[bodyPart.Key].Health.Current =
|
||||
pmcData.Health.BodyParts[bodyPart.Key].Health.Maximum * _healthConfig.HealthMultipliers.Death ?? 0;
|
||||
}
|
||||
|
||||
TransferPostRaidLimbEffectsToProfile(postRaidHealth.BodyParts, pmcData);
|
||||
@@ -217,26 +212,19 @@ public class HealthHelper(
|
||||
if (profileBodyPartEffects.TryGetValue(effect.Key, out var dictEffect))
|
||||
{
|
||||
if (effectsToIgnore.Contains(effect.Key))
|
||||
{
|
||||
// Get rid of certain effects we dont want to persist out of raid
|
||||
dictEffect = null;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effectsToIgnore.Contains(effect.Key))
|
||||
{
|
||||
// Do not pass some effects to out of raid profile
|
||||
continue;
|
||||
}
|
||||
|
||||
var effectToAdd = new BodyPartEffectProperties { Time = effectDetails.Time ?? -1 };
|
||||
// Add effect to server profile
|
||||
if (profileBodyPartEffects.TryAdd(effect.Key, effectToAdd))
|
||||
{
|
||||
profileBodyPartEffects[effect.Key] = effectToAdd;
|
||||
}
|
||||
if (profileBodyPartEffects.TryAdd(effect.Key, effectToAdd)) profileBodyPartEffects[effect.Key] = effectToAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,27 +236,16 @@ public class HealthHelper(
|
||||
/// <param name="sessionID">Session id</param>
|
||||
protected void SaveHealth(PmcData pmcData, string sessionID)
|
||||
{
|
||||
if (!_healthConfig.Save.Health) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_healthConfig.Save.Health) return;
|
||||
|
||||
var profileHealth = _saveServer.GetProfile(sessionID).VitalityData;
|
||||
|
||||
if (profileHealth.Hydration > pmcData.Health.Hydration.Maximum)
|
||||
{
|
||||
profileHealth.Hydration = pmcData.Health.Hydration.Maximum;
|
||||
}
|
||||
|
||||
if (profileHealth.Energy > pmcData.Health.Energy.Maximum)
|
||||
{
|
||||
profileHealth.Energy = pmcData.Health.Energy.Maximum;
|
||||
}
|
||||
|
||||
if (profileHealth.Temperature > pmcData.Health.Temperature.Maximum)
|
||||
{
|
||||
profileHealth.Temperature = pmcData.Health.Temperature.Maximum;
|
||||
}
|
||||
|
||||
if (profileHealth.Hydration > pmcData.Health.Hydration.Maximum) profileHealth.Hydration = pmcData.Health.Hydration.Maximum;
|
||||
|
||||
if (profileHealth.Energy > pmcData.Health.Energy.Maximum) profileHealth.Energy = pmcData.Health.Energy.Maximum;
|
||||
|
||||
if (profileHealth.Temperature > pmcData.Health.Temperature.Maximum) profileHealth.Temperature = pmcData.Health.Temperature.Maximum;
|
||||
|
||||
pmcData.Health.Hydration.Current = Math.Round(profileHealth.Hydration ?? 0);
|
||||
pmcData.Health.Energy.Current = Math.Round(profileHealth.Energy ?? 0);
|
||||
pmcData.Health.Temperature.Current = Math.Round(profileHealth.Temperature ?? 0);
|
||||
@@ -276,15 +253,11 @@ public class HealthHelper(
|
||||
foreach (var bodyPart in pmcData.Health.BodyParts)
|
||||
{
|
||||
if (profileHealth.Health[bodyPart.Key].Health.Maximum > bodyPart.Value.Health.Maximum)
|
||||
{
|
||||
profileHealth.Health[bodyPart.Key].Health.Maximum = bodyPart.Value.Health.Maximum;
|
||||
}
|
||||
|
||||
if (profileHealth.Health[bodyPart.Key].Health.Current == 0)
|
||||
{
|
||||
profileHealth.Health[bodyPart.Key].Health.Current = bodyPart.Value.Health.Maximum * _healthConfig.HealthMultipliers.Blacked;
|
||||
}
|
||||
|
||||
|
||||
bodyPart.Value.Health.Current = Math.Round(profileHealth.Health[bodyPart.Key].Health.Current ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -305,29 +278,20 @@ public class HealthHelper(
|
||||
bool deleteExistingEffects = true)
|
||||
{
|
||||
// TODO: this will need to change, typing is all fucked up
|
||||
if (!_healthConfig.Save.Effects)
|
||||
if (!_healthConfig.Save.Effects) return;
|
||||
|
||||
foreach (var bodyPart in bodyPartsWithEffects)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( var bodyPart in bodyPartsWithEffects) {
|
||||
// clear effects from profile bodyPart
|
||||
if (deleteExistingEffects)
|
||||
{
|
||||
pmcData.Health.BodyParts[bodyPart.Key].Effects = new Dictionary<string, BodyPartEffectProperties>();
|
||||
}
|
||||
if (deleteExistingEffects) pmcData.Health.BodyParts[bodyPart.Key].Effects = new Dictionary<string, BodyPartEffectProperties>();
|
||||
|
||||
foreach ( var effectType in bodyPartsWithEffects[bodyPart.Key].Effects) {
|
||||
|
||||
foreach (var effectType in bodyPartsWithEffects[bodyPart.Key].Effects)
|
||||
{
|
||||
var time = effectType.Value.Time;
|
||||
if (time is not null && time > 0)
|
||||
{
|
||||
AddEffect(pmcData, effectType, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddEffect(pmcData, effectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,7 +307,7 @@ public class HealthHelper(
|
||||
{
|
||||
var profileBodyPart = pmcData.Health.BodyParts[effectType.Key];
|
||||
profileBodyPart.Effects ??= new Dictionary<string, BodyPartEffectProperties>();
|
||||
|
||||
|
||||
profileBodyPart.Effects[effectType.Key] = new BodyPartEffectProperties { Time = duration };
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user