diff --git a/Core/Controllers/GameController.cs b/Core/Controllers/GameController.cs
index 23a90972..db7a86ec 100644
--- a/Core/Controllers/GameController.cs
+++ b/Core/Controllers/GameController.cs
@@ -5,6 +5,7 @@ using Core.Models.Eft.Common;
using Core.Models.Eft.Game;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
+using Core.Models.External;
using Core.Models.Spt.Config;
using Core.Servers;
using Core.Services;
@@ -40,6 +41,7 @@ public class GameController
private readonly RaidTimeAdjustmentService _raidTimeAdjustmentService;
private readonly ProfileActivityService _profileActivityService;
private readonly ApplicationContext _applicationContext;
+ //private readonly PreSptModLoader preSptModLoader
private readonly ICloner _cloner;
private readonly CoreConfig _coreConfig;
@@ -356,7 +358,11 @@ public class GameController
/// Player profile
private void WarnOnActiveBotReloadSkill(PmcData pmcProfile)
{
- throw new NotImplementedException();
+ var botReloadSkill = _profileHelper.GetSkillFromProfile(pmcProfile, SkillTypes.BotReload);
+ if (botReloadSkill?.Progress > 0)
+ {
+ _logger.Warning(_localisationService.GetText("server_start_player_active_botreload_skill"));
+ }
}
///
@@ -365,7 +371,98 @@ public class GameController
/// Profile to adjust values for
private void UpdateProfileHealthValues(PmcData pmcProfile)
{
- throw new NotImplementedException();
+ var healthLastUpdated = pmcProfile.Health.UpdateTime;
+ var currentTimeStamp = _timeUtil.GetTimeStamp();
+ var diffSeconds = currentTimeStamp - healthLastUpdated;
+
+ // Last update is in past
+ if (healthLastUpdated < currentTimeStamp)
+ {
+ // Base values
+ double energyRegenPerHour = 60;
+ double hydrationRegenPerHour = 60;
+ double 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));
+ hydrationRegenPerHour += pmcProfile.Bonuses
+ .Where((bonus) => bonus.Type == BonusType.HydrationRegeneration)
+ .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));
+
+ // Player has energy deficit
+ if (pmcProfile.Health.Energy.Current != pmcProfile.Health.Energy.Maximum)
+ {
+ // 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;
+ }
+ }
+
+ // Player has hydration deficit
+ if (pmcProfile.Health.Hydration.Current != pmcProfile.Health.Hydration.Maximum)
+ {
+ 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
+ foreach (var bodyPart in pmcProfile.Health.BodyParts
+ .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 = bodyPart.Health.Maximum;
+ }
+
+
+ if (bodyPart.Effects is null || bodyPart.Effects.Count == 0)
+ {
+ continue;
+ }
+
+ // Look for effects
+ foreach (var effectKvP in bodyPart.Effects) {
+ // remove effects below 1, .e.g. bleeds at -1
+ if (effectKvP.Value.Time < 1)
+ {
+ // More than 30 mins has passed
+ if (diffSeconds > 1800)
+ {
+ bodyPart.Effects.Remove(effectKvP.Key);
+ }
+
+ continue;
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ // Update both values as they've both been updated
+ pmcProfile.Health.UpdateTime = currentTimeStamp;
+ }
+
}
///
@@ -374,7 +471,21 @@ public class GameController
/// Profile to add gifts to
private void SendPraporGiftsToNewProfiles(PmcData pmcProfile)
{
- throw new NotImplementedException();
+ var timeStampProfileCreated = pmcProfile.Info.RegistrationDate;
+ var oneDaySeconds = _timeUtil.GetHoursAsSeconds(24);
+ var currentTimeStamp = _timeUtil.GetTimeStamp();
+
+ // One day post-profile creation
+ if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds)
+ {
+ _giftService.SendPraporStartingGift(pmcProfile.SessionId, 1);
+ }
+
+ // Two day post-profile creation
+ if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2)
+ {
+ _giftService.SendPraporStartingGift(pmcProfile.SessionId, 2);
+ }
}
///
@@ -383,7 +494,36 @@ public class GameController
/// Profile to add mod details to
private void SaveActiveModsToProfile(SptProfile fullProfile)
{
- throw new NotImplementedException();
+ // Add empty mod array if undefined
+ if (fullProfile.SptData.Mods is null)
+ {
+ fullProfile.SptData.Mods = [];
+ }
+
+ // Get active mods
+ //var activeMods = _preSptModLoader.GetImportedModDetails(); //TODO IMPLEMENT _preSptModLoader
+ var activeMods = new Dictionary();
+ foreach (var modKvP in activeMods) {
+ var modDetails = modKvP.Value;
+ if (
+ fullProfile.SptData.Mods.Any(
+ (mod) =>
+ mod.Author == modDetails.Author &&
+ mod.Name == modDetails.Name &&
+ mod.Version == modDetails.Version))
+ {
+ // Exists already, skip
+ continue;
+ }
+
+ fullProfile.SptData.Mods.Add( new ModDetails{
+ Author = modDetails.Author,
+ DateAdded = _timeUtil.GetTimeStamp(),
+ Name = modDetails.Name,
+ Version = modDetails.Version,
+ Url = modDetails.Url,
+ });
+ }
}
///
@@ -392,7 +532,34 @@ public class GameController
/// Profile of player to get name from
private void AddPlayerToPmcNames(PmcData pmcProfile)
{
- throw new NotImplementedException();
+ var playerName = pmcProfile.Info.Nickname;
+ if (playerName is not null)
+ {
+ var bots = _databaseService.GetBots().Types;
+
+ // Official names can only be 15 chars in length
+ 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);
+ }
+ }
+ }
}
///
diff --git a/Core/Controllers/TradeController.cs b/Core/Controllers/TradeController.cs
index adf2bc31..030add0c 100644
--- a/Core/Controllers/TradeController.cs
+++ b/Core/Controllers/TradeController.cs
@@ -1,16 +1,80 @@
using Core.Annotations;
+using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Ragfair;
using Core.Models.Eft.Trade;
using Core.Models.Enums;
+using Core.Models.Spt.Config;
+using Core.Routers;
+using Core.Servers;
+using Core.Services;
+using Core.Utils;
+using ILogger = Core.Models.Utils.ILogger;
namespace Core.Controllers;
[Injectable]
public class TradeController
{
+ private readonly ILogger _logger;
+ private readonly DatabaseService _databaseService;
+ private readonly EventOutputHolder _eventOutputHolder;
+ private readonly TradeHelper _tradeHelper;
+ private readonly TimeUtil _timeUtil;
+ private readonly HashUtil _hashUtil;
+ private readonly ItemHelper _itemHelper;
+ private readonly ProfileHelper _profileHelper;
+ private readonly RagfairOfferHelper _ragfairOfferHelper;
+ private readonly TraderHelper _traderHelper;
+ // private readonly RagfairServer _ragfairServer;
+ private readonly HttpResponseUtil _httpResponseUtil;
+ private readonly LocalisationService _localisationService;
+ private readonly RagfairPriceService _ragfairPriceService;
+ // private readonly MailSendService _mailSendService;
+ private readonly ConfigServer _configServer;
+
+ private readonly RagfairConfig _ragfairConfig;
+ private readonly TraderConfig _traderConfig;
+
+ public TradeController
+ (
+ ILogger logger,
+ DatabaseService databaseService,
+ EventOutputHolder eventOutputHolder,
+ TradeHelper tradeHelper,
+ TimeUtil timeUtil,
+ HashUtil hashUtil,
+ ItemHelper itemHelper,
+ ProfileHelper profileHelper,
+ RagfairOfferHelper ragfairOfferHelper,
+ TraderHelper traderHelper,
+ HttpResponseUtil httpResponseUtil,
+ LocalisationService localisationService,
+ RagfairPriceService ragfairPriceService,
+ ConfigServer configServer
+ )
+ {
+ _logger = logger;
+ _databaseService = databaseService;
+ _eventOutputHolder = eventOutputHolder;
+ _tradeHelper = tradeHelper;
+ _timeUtil = timeUtil;
+ _hashUtil = hashUtil;
+ _itemHelper = itemHelper;
+ _profileHelper = profileHelper;
+ _ragfairOfferHelper = ragfairOfferHelper;
+ _traderHelper = traderHelper;
+ _httpResponseUtil = httpResponseUtil;
+ _localisationService = localisationService;
+ _ragfairPriceService = ragfairPriceService;
+ _configServer = configServer;
+
+ _ragfairConfig = _configServer.GetConfig(ConfigTypes.RAGFAIR);
+ _traderConfig = _configServer.GetConfig(ConfigTypes.TRADER);
+ }
+
///
/// Handle TradingConfirm event
///
diff --git a/Core/Generators/BotGenerator.cs b/Core/Generators/BotGenerator.cs
index 53f5b3fe..e1a7bd56 100644
--- a/Core/Generators/BotGenerator.cs
+++ b/Core/Generators/BotGenerator.cs
@@ -42,7 +42,7 @@ public class BotGenerator
/// base bot template to use (e.g. assault/pmcbot)
/// profile of player generating pscav
/// BotBase
- public BotBase GeneratePlayerScav(string sessionId, string role, string difficulty, BotType botTemplate, PmcData profile)
+ public PmcData GeneratePlayerScav(string sessionId, string role, string difficulty, BotType botTemplate, PmcData profile)
{
var bot = GetCloneOfBotBase();
bot.Info.Settings.BotDifficulty = difficulty;
@@ -65,7 +65,39 @@ public class BotGenerator
// Sets the name after scav name shown in parentheses
bot.Info.MainProfileNickname = profile.Info.Nickname;
- return bot;
+ return new PmcData
+ {
+ Id = bot.Id,
+ Aid = bot.Aid,
+ SessionId = bot.SessionId,
+ Savage = bot.Savage,
+ KarmaValue = bot.KarmaValue,
+ Info = bot.Info,
+ Customization = bot.Customization,
+ Health = bot.Health,
+ Inventory = bot.Inventory,
+ Skills = bot.Skills,
+ Stats = bot.Stats,
+ Encyclopedia = bot.Encyclopedia,
+ TaskConditionCounters = bot.TaskConditionCounters,
+ InsuredItems = bot.InsuredItems,
+ Hideout = bot.Hideout,
+ Quests = bot.Quests,
+ TradersInfo = bot.TradersInfo,
+ UnlockedInfo = bot.UnlockedInfo,
+ RagfairInfo = bot.RagfairInfo,
+ Achievements = bot.Achievements,
+ RepeatableQuests = bot.RepeatableQuests,
+ Bonuses = bot.Bonuses,
+ Notes = bot.Notes,
+ CarExtractCounts = bot.CarExtractCounts,
+ CoopExtractCounts = bot.CoopExtractCounts,
+ SurvivorClass = bot.SurvivorClass,
+ WishList = bot.WishList,
+ MoneyTransferLimitData = bot.MoneyTransferLimitData,
+ IsPmc = bot.IsPmc,
+ Prestige = new Prestige()
+ };
}
///
@@ -112,6 +144,8 @@ public class BotGenerator
{
_logger.Error("NOT IMPLEMENTED BotGenerator.GenerateBot");
+ bot.Inventory.Items = [];
+
return bot;
}
diff --git a/Core/Generators/PlayerScavGenerator.cs b/Core/Generators/PlayerScavGenerator.cs
index 45d4e396..0d4e8a58 100644
--- a/Core/Generators/PlayerScavGenerator.cs
+++ b/Core/Generators/PlayerScavGenerator.cs
@@ -100,7 +100,7 @@ public class PlayerScavGenerator
var baseBotNode = ConstructBotBaseTemplate(playerScavKarmaSettings.BotTypeForLoot);
AdjustBotTemplateWithKarmaSpecificSettings(playerScavKarmaSettings, baseBotNode);
- var scavData = (PmcData)_botGenerator.GeneratePlayerScav(
+ var scavData = _botGenerator.GeneratePlayerScav(
sessionID,
playerScavKarmaSettings.BotTypeForLoot.ToLower(),
"easy",
diff --git a/Core/Helpers/HealthHelper.cs b/Core/Helpers/HealthHelper.cs
index 863a8d08..989d8a91 100644
--- a/Core/Helpers/HealthHelper.cs
+++ b/Core/Helpers/HealthHelper.cs
@@ -1,8 +1,9 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Health;
using Core.Models.Eft.Profile;
+using BodyPartHealth = Core.Models.Eft.Common.Tables.BodyPartHealth;
using Effects = Core.Models.Eft.Profile.Effects;
using Health = Core.Models.Eft.Profile.Health;
@@ -53,7 +54,7 @@ public class HealthHelper
///
/// Post-raid body part data
/// Player profile on server
- protected void TransferPostRaidLimbEffectsToProfile(BodyPartsHealth postRaidBodyParts, PmcData profileData)
+ protected void TransferPostRaidLimbEffectsToProfile(Dictionary postRaidBodyParts, PmcData profileData)
{
throw new NotImplementedException();
}
diff --git a/Core/Helpers/ProfileHelper.cs b/Core/Helpers/ProfileHelper.cs
index 635c23a6..da486721 100644
--- a/Core/Helpers/ProfileHelper.cs
+++ b/Core/Helpers/ProfileHelper.cs
@@ -1,4 +1,4 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Profile;
@@ -352,8 +352,8 @@ public class ProfileHelper
public PmcData RemoveSecureContainer(PmcData profile)
{
var items = profile.Inventory.Items;
- var secureContainer = items.First(i => i.SlotId == "SecuredContainer");
- if (secureContainer != null)
+ var secureContainer = items.FirstOrDefault(i => i.SlotId == "SecuredContainer");
+ if (secureContainer is not null)
{
// Find and remove container + children
var childItemsInSecureContainer = _itemHelper.FindAndReturnChildrenByItems(items, secureContainer.Id);
diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs
index cc1913ce..6c6f1b41 100644
--- a/Core/Models/Eft/Common/Tables/BotBase.cs
+++ b/Core/Models/Eft/Common/Tables/BotBase.cs
@@ -231,22 +231,11 @@ public class BotBaseHealth
public CurrentMax? Hydration { get; set; }
public CurrentMax? Energy { get; set; }
public CurrentMax? Temperature { get; set; }
- public BodyPartsHealth? BodyParts { get; set; }
+ public Dictionary? BodyParts { get; set; }
public double? UpdateTime { get; set; }
public bool? Immortal { get; set; }
}
-public class BodyPartsHealth
-{
- public BodyPartHealth? Head { get; set; }
- public BodyPartHealth? Chest { get; set; }
- public BodyPartHealth? Stomach { get; set; }
- public BodyPartHealth? LeftArm { get; set; }
- public BodyPartHealth? RightArm { get; set; }
- public BodyPartHealth? LeftLeg { get; set; }
- public BodyPartHealth? RightLeg { get; set; }
-}
-
public class BodyPartHealth
{
public CurrentMax? Health { get; set; }
diff --git a/Core/Services/FenceService.cs b/Core/Services/FenceService.cs
index d11c465c..13c1d3bd 100644
--- a/Core/Services/FenceService.cs
+++ b/Core/Services/FenceService.cs
@@ -1,4 +1,4 @@
-using Core.Annotations;
+using Core.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Spt.Config;
@@ -9,6 +9,14 @@ namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class FenceService
{
+ private readonly DatabaseService _databaseService;
+
+ public FenceService(
+ DatabaseService databaseService)
+ {
+ _databaseService = databaseService;
+ }
+
///
/// Replace main fence assort with new assort
///
@@ -495,7 +503,30 @@ public class FenceService
/// FenceLevel object
public FenceLevel GetFenceInfo(PmcData pmcData)
{
- throw new NotImplementedException();
+ var fenceSettings = _databaseService.GetGlobals().Configuration.FenceSettings;
+ pmcData.TradersInfo.TryGetValue(fenceSettings.FenceIdentifier, out var pmcFenceInfo);
+
+ if (pmcFenceInfo is null)
+ {
+ return fenceSettings.Levels["0"];
+ }
+
+ var fenceLevels = fenceSettings.Levels.Select(x => x.Key);
+ var minLevel = fenceLevels.Min();
+ var maxLevel = fenceLevels.Max();
+ var pmcFenceLevel = Math.Floor(pmcFenceInfo.Standing.Value);
+
+ if (pmcFenceLevel < int.Parse(minLevel))
+ {
+ return fenceSettings.Levels[minLevel];
+ }
+
+ if (pmcFenceLevel > int.Parse(maxLevel))
+ {
+ return fenceSettings.Levels[maxLevel];
+ }
+
+ return fenceSettings.Levels[pmcFenceLevel.ToString()];
}
///
diff --git a/Core/Services/ProfileFixerService.cs b/Core/Services/ProfileFixerService.cs
index 3726df4d..e5abd9bd 100644
--- a/Core/Services/ProfileFixerService.cs
+++ b/Core/Services/ProfileFixerService.cs
@@ -27,6 +27,7 @@ public class ProfileFixerService
private readonly LocalisationService _localisationService;
private readonly ConfigServer _configServer;
private readonly CoreConfig _coreConfig;
+ private readonly InventoryHelper _inventoryHelper;
public ProfileFixerService(
ILogger logger,
@@ -37,7 +38,9 @@ public class ProfileFixerService
TraderHelper traderHelper,
DatabaseService databaseService,
LocalisationService localisationService,
- ConfigServer configServer)
+ ConfigServer configServer,
+ InventoryHelper inventoryHelper
+ )
{
_logger = logger;
_hashUtil = hashUtil;
@@ -48,6 +51,7 @@ public class ProfileFixerService
_databaseService = databaseService;
_localisationService = localisationService;
_configServer = configServer;
+ _inventoryHelper = inventoryHelper;
_coreConfig = _configServer.GetConfig(ConfigTypes.CORE);
}
@@ -89,7 +93,8 @@ public class ProfileFixerService
}
var traderDialogues = traderDialoguesKvP.Value;
- foreach (var message in traderDialogues.Messages) {
+ foreach (var message in traderDialogues.Messages)
+ {
// Skip any messages without attached items
if (message.Items?.Data is null || message.Items?.Stash is null)
{
@@ -135,14 +140,15 @@ public class ProfileFixerService
.GroupBy(item => item.Id)
.ToDictionary(x => x.Key, x => x.ToList());
- foreach (var mappingKvP in itemMapping) {
+ foreach (var mappingKvP in itemMapping)
+ {
// Only one item for this id, not a dupe
if (mappingKvP.Value.Count == 1)
{
continue;
}
- _logger.Warning($"{ mappingKvP.Value.Count - 1} duplicate(s) found for item: {mappingKvP.Key}");
+ _logger.Warning($"{mappingKvP.Value.Count - 1} duplicate(s) found for item: {mappingKvP.Key}");
var itemAJson = _jsonUtil.Serialize(mappingKvP.Value[0]);
var itemBJson = _jsonUtil.Serialize(mappingKvP.Value[1]);
if (itemAJson == itemBJson)
@@ -167,7 +173,8 @@ public class ProfileFixerService
}
// Iterate over all inventory items
- foreach (var item in pmcProfile.Inventory.Items.Where((x) => x.SlotId is not null)) {
+ foreach (var item in pmcProfile.Inventory.Items.Where((x) => x.SlotId is not null))
+ {
if (item.Upd is null)
{
// Ignore items without a upd object
@@ -178,7 +185,7 @@ public class ProfileFixerService
Regex regxp = new Regex("[^a-zA-Z0-9 -]");
if (item.Upd.Tag?.Name is not null && !regxp.IsMatch(item.Upd.Tag.Name))
{
- _logger.Warning($"Fixed item: { item.Id}s Tag value, removed invalid characters");
+ _logger.Warning($"Fixed item: {item.Id}s Tag value, removed invalid characters");
item.Upd.Tag.Name = regxp.Replace(item.Upd.Tag.Name, "");
}
@@ -208,8 +215,8 @@ public class ProfileFixerService
if (customizationDb[pmcProfile.Customization.Body] is null)
{
var defaultBody = playerIsUsec
- ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecBody")
- : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearBody");
+ ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecBody")
+ : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearBody");
pmcProfile.Customization.Body = defaultBody.Id;
}
@@ -217,8 +224,8 @@ public class ProfileFixerService
if (customizationDb[pmcProfile.Customization.Hands] is null)
{
var defaultHands = playerIsUsec
- ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecHands")
- : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearHands");
+ ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecHands")
+ : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearHands");
pmcProfile.Customization.Hands = defaultHands.Id;
}
@@ -226,8 +233,8 @@ public class ProfileFixerService
if (customizationDb[pmcProfile.Customization.Feet] is null)
{
var defaultFeet = playerIsUsec
- ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecFeet")
- : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearFeet");
+ ? customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultUsecFeet")
+ : customizationDbArray.FirstOrDefault((x) => x.Name == "DefaultBearFeet");
pmcProfile.Customization.Feet = defaultFeet.Id;
}
}
@@ -267,7 +274,8 @@ public class ProfileFixerService
var achievements = _databaseService.GetAchievements();
// Loop over TaskConditionCounters objects and add once we want to remove to counterKeysToRemove
- foreach (var TaskConditionCounterKvP in pmcProfile.TaskConditionCounters) {
+ foreach (var TaskConditionCounterKvP in pmcProfile.TaskConditionCounters)
+ {
// Only check if profile has repeatable quests
if (pmcProfile.RepeatableQuests is not null && activeRepeatableQuests.Count > 0)
{
@@ -286,7 +294,8 @@ public class ProfileFixerService
}
}
- foreach (var counterKeyToRemove in taskConditionKeysToRemove) {
+ foreach (var counterKeyToRemove in taskConditionKeysToRemove)
+ {
_logger.Debug($"Removed: {counterKeyToRemove} TaskConditionCounter object");
pmcProfile.TaskConditionCounters.Remove(counterKeyToRemove);
}
@@ -295,9 +304,10 @@ public class ProfileFixerService
protected List GetActiveRepeatableQuests(List repeatableQuests)
{
var activeQuests = new List();
- foreach (var repeatableQuest in repeatableQuests.Where(questType => questType.ActiveQuests?.Count > 0)) {
- // daily/weekly collection has active quests in them, add to array and return
- activeQuests.AddRange(repeatableQuest.ActiveQuests);
+ foreach (var repeatableQuest in repeatableQuests.Where(questType => questType.ActiveQuests?.Count > 0))
+ {
+ // daily/weekly collection has active quests in them, add to array and return
+ activeQuests.AddRange(repeatableQuest.ActiveQuests);
}
return activeQuests;
@@ -316,7 +326,6 @@ public class ProfileFixerService
for (var i = profileQuests.Count - 1; i >= 0; i--)
{
-
if (!quests.ContainsKey(profileQuests[i].QId) || activeRepeatableQuests.Any((x) => x.Id == profileQuests[i].QId))
{
profileQuests.RemoveAt(i);
@@ -331,7 +340,6 @@ public class ProfileFixerService
/// The profile to validate quest productions for
protected void VerifyQuestProductionUnlocks(PmcData pmcProfile)
{
-
var quests = _databaseService.GetQuests();
var profileQuests = pmcProfile.Quests;
@@ -351,7 +359,8 @@ public class ProfileFixerService
if (productionRewards is not null)
{
- foreach (var reward in productionRewards) {
+ foreach (var reward in productionRewards)
+ {
VerifyQuestProductionUnlock(pmcProfile, reward, quest);
}
}
@@ -365,7 +374,8 @@ public class ProfileFixerService
if (productionRewards is not null)
{
- foreach (var reward in productionRewards) {
+ foreach (var reward in productionRewards)
+ {
VerifyQuestProductionUnlock(pmcProfile, reward, quest);
}
}
@@ -387,9 +397,11 @@ public class ProfileFixerService
if (matchingProductions.Count != 1)
{
- _logger.Error(_localisationService.GetText("quest-unable_to_find_matching_hideout_production", new {
+ _logger.Error(_localisationService.GetText("quest-unable_to_find_matching_hideout_production", new
+ {
questName = questDetails.QuestName,
- matchCount = matchingProductions.Count}));
+ matchCount = matchingProductions.Count
+ }));
return;
}
@@ -399,7 +411,7 @@ public class ProfileFixerService
if (!pmcProfile.UnlockedInfo.UnlockedProductionRecipe.Contains(matchingProductionId))
{
pmcProfile.UnlockedInfo.UnlockedProductionRecipe.Add(matchingProductionId);
- _logger.Debug($"Added production: { matchingProductionId} to unlocked production recipes for: { questDetails.QuestName}");
+ _logger.Debug($"Added production: {matchingProductionId} to unlocked production recipes for: {questDetails.QuestName}");
}
}
@@ -430,7 +442,7 @@ public class ProfileFixerService
{
if (!slots.Any((x) => x.LocationIndex == i))
{
- slots.Add(new HideoutSlot{ LocationIndex = i });
+ slots.Add(new HideoutSlot { LocationIndex = i });
}
}
@@ -452,6 +464,8 @@ public class ProfileFixerService
}
}
+ private readonly List _areas = ["hideout", "main"];
+
/**
* Checks profile inventory for items that do not exist inside the items db
* @param sessionId Session id
@@ -459,7 +473,150 @@ public class ProfileFixerService
*/
public void CheckForOrphanedModdedItems(string sessionId, SptProfile fullProfile)
{
- throw new NotImplementedException();
+ var itemsDb = _databaseService.GetItems();
+ var pmcProfile = fullProfile.CharacterData.PmcData;
+
+ // Get items placed in root of stash
+ // TODO: extend to other areas / sub items
+ var inventoryItemsToCheck = pmcProfile.Inventory.Items.Where(item => _areas.Contains(item.SlotId ?? ""));
+ if (inventoryItemsToCheck is not null)
+ {
+ // Check each item in inventory to ensure item exists in itemdb
+ foreach (var item in inventoryItemsToCheck)
+ {
+ if (itemsDb[item.Template] is not null)
+ {
+ _logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
+
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ _logger.Success($"Deleting item from inventory and insurance with id: {item.Id} tpl: {item.Template}");
+
+ // also deletes from insured array
+ _inventoryHelper.RemoveItem(pmcProfile, item.Id, sessionId);
+ }
+ }
+ }
+ }
+
+ if (fullProfile.UserBuildData is not null)
+ {
+ // Remove invalid builds from weapon, equipment and magazine build lists
+ var weaponBuilds = fullProfile.UserBuildData?.WeaponBuilds ?? new List();
+ fullProfile.UserBuildData.WeaponBuilds = weaponBuilds.Where(build =>
+ {
+ return !ShouldRemoveWeaponEquipmentBuild("weapon", build, itemsDb);
+ }).ToList();
+
+ var equipmentBuilds = fullProfile.UserBuildData.EquipmentBuilds ?? new List();
+ fullProfile.UserBuildData.EquipmentBuilds = equipmentBuilds.Where(build =>
+ {
+ return !ShouldRemoveWeaponEquipmentBuild("equipment", build, itemsDb);
+ }).ToList();
+
+ var magazineBuild = fullProfile.UserBuildData.MagazineBuilds ?? new List();
+ fullProfile.UserBuildData.MagazineBuilds = magazineBuild.Where(build =>
+ {
+ return !ShouldRemoveMagazineBuild(build, itemsDb);
+ }).ToList();
+ }
+
+ // Iterate over dialogs, looking for messages with items not found in item db, remove message if item found
+ foreach (var dialog in fullProfile.DialogueRecords)
+ {
+ if (dialog.Value.Messages is null)
+ continue; // Skip dialog with no messages
+
+ foreach (var message in dialog.Value.Messages)
+ {
+ if (message.Items.Data is null)
+ continue; // skip messages with no items
+
+ // Fix message with no items but have the flags to indicate items to collect
+ if (message.Items.Data.Count == 0 && (message.HasRewards ?? false))
+ {
+ message.HasRewards = false;
+ message.RewardCollected = true;
+ continue;
+ }
+
+ // Iterate over all items in message
+ foreach (var item in message.Items.Data)
+ {
+ // Check item exists in itemsDb
+ if (itemsDb[item.Template] is null)
+ _logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
+
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ dialog.Value.Messages.Remove(message);
+ _logger.Warning($"Item: {item.Template} has resulted in the deletion of message: {message.Id} from dialog {dialog}");
+ }
+
+ break;
+ }
+ }
+ }
+
+ var clothing = _databaseService.GetTemplates().Customization;
+ foreach (var suit in fullProfile.Suits)
+ {
+ if (suit is null)
+ {
+ _logger.Error(_localisationService.GetText("fixer-clothing_item_found", suit));
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ fullProfile.Suits.Remove(suit);
+ _logger.Warning($"Non-default suit purchase: {suit} removed from profile");
+ }
+ }
+ }
+
+ foreach (var repeatable in fullProfile.CharacterData.PmcData.RepeatableQuests ?? new())
+ {
+ foreach (var activeQuest in repeatable.ActiveQuests ?? new())
+ {
+ if (!_traderHelper.TraderEnumHasValue(activeQuest.TraderId))
+ {
+ _logger.Error(_localisationService.GetText("fixer-trader_found", activeQuest.TraderId));
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ _logger.Warning($"Non-default quest: {activeQuest.Id} from trader: {activeQuest.TraderId} removed from RepeatableQuests list in profile");
+ repeatable.ActiveQuests.Remove(activeQuest);
+ }
+
+ continue;
+ }
+
+ foreach (var successReward in activeQuest.Rewards.Success ?? new())
+ {
+ if (successReward.Type.ToString() == "Item")
+ {
+ foreach (var item in successReward.Items)
+ {
+ if (itemsDb[item.Template] is null)
+ {
+ _logger.Warning($"Non-default quest: {activeQuest.Id} from trader: {activeQuest.TraderId} removed from RepeatableQuests list in profile");
+ repeatable.ActiveQuests.Remove(activeQuest);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ foreach (var TraderPurchase in fullProfile.TraderPurchases)
+ {
+ if (_traderHelper.TraderEnumHasValue(TraderPurchase.Key))
+ {
+ _logger.Error(_localisationService.GetText("fixer-trader_found", TraderPurchase.Key));
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ _logger.Warning($"Non-default trader: {TraderPurchase.Key} purchase removed from traderPurchases list in profile");
+ fullProfile.TraderPurchases.Remove(TraderPurchase.Key);
+ }
+ }
+ }
}
/**
@@ -470,22 +627,45 @@ public class ProfileFixerService
*/
protected bool ShouldRemoveWeaponEquipmentBuild(
string buildType,
- WeaponBuild equipmentBuild,
+ UserBuild build,
Dictionary itemsDb)
{
- // Get items not found in items db
- foreach (var item in equipmentBuild.Items.Where(item => !itemsDb.ContainsKey(item.Template)))
+ if (buildType == "weapon")
{
- _logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
-
- if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ // Get items not found in items db
+ foreach (var item in (build as WeaponBuild).Items.Where(item => !itemsDb.ContainsKey(item.Template)))
{
- _logger.Warning($"Item: { item.Template} has resulted in the deletion of { buildType} build: { equipmentBuild.Name}");
+ _logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
- return true;
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ _logger.Warning($"Item: {item.Template} has resulted in the deletion of {buildType} build: {build.Name}");
+
+ return true;
+ }
+
+ break;
}
+ }
+
+ // TODO: refactor to be generic
- break;
+ if (buildType == "equipment")
+ {
+ // Get items not found in items db
+ foreach (var item in (build as EquipmentBuild).Items.Where(item => !itemsDb.ContainsKey(item.Template)))
+ {
+ _logger.Error(_localisationService.GetText("fixer-mod_item_found", item.Template));
+
+ if (_coreConfig.Fixes.RemoveModItemsFromProfile)
+ {
+ _logger.Warning($"Item: {item.Template} has resulted in the deletion of {buildType} build: {build.Name}");
+
+ return true;
+ }
+
+ break;
+ }
}
return false;
@@ -533,7 +713,7 @@ public class ProfileFixerService
_logger.Error(_localisationService.GetText("fixer-trader_found", traderId));
if (_coreConfig.Fixes.RemoveInvalidTradersFromProfile)
{
- _logger.Warning($"Non - default trader: { traderId} removed from PMC TradersInfo in: { fullProfile.ProfileInfo.ProfileId} profile");
+ _logger.Warning($"Non - default trader: {traderId} removed from PMC TradersInfo in: {fullProfile.ProfileInfo.ProfileId} profile");
fullProfile.CharacterData.PmcData.TradersInfo.Remove(traderId);
}
}
@@ -547,7 +727,7 @@ public class ProfileFixerService
_logger.Error(_localisationService.GetText("fixer-trader_found", traderId));
if (_coreConfig.Fixes.RemoveInvalidTradersFromProfile)
{
- _logger.Warning($"Non - default trader: {traderId} removed from Scav TradersInfo in: { fullProfile.ProfileInfo.ProfileId} profile");
+ _logger.Warning($"Non - default trader: {traderId} removed from Scav TradersInfo in: {fullProfile.ProfileInfo.ProfileId} profile");
fullProfile.CharacterData.ScavData.TradersInfo.Remove(traderId);
}
}