Merge branch 'main' of https://github.com/sp-tarkov/server-csharp
This commit is contained in:
@@ -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
|
||||
/// <param name="pmcProfile">Player profile</param>
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -365,7 +371,98 @@ public class GameController
|
||||
/// <param name="pmcProfile">Profile to adjust values for</param>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -374,7 +471,21 @@ public class GameController
|
||||
/// <param name="pmcProfile">Profile to add gifts to</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -383,7 +494,36 @@ public class GameController
|
||||
/// <param name="fullProfile">Profile to add mod details to</param>
|
||||
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<string, ModDetails>();
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -392,7 +532,34 @@ public class GameController
|
||||
/// <param name="pmcProfile">Profile of player to get name from</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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<RagfairConfig>(ConfigTypes.RAGFAIR);
|
||||
_traderConfig = _configServer.GetConfig<TraderConfig>(ConfigTypes.TRADER);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle TradingConfirm event
|
||||
/// </summary>
|
||||
|
||||
@@ -42,7 +42,7 @@ public class BotGenerator
|
||||
/// <param name="botTemplate">base bot template to use (e.g. assault/pmcbot)</param>
|
||||
/// <param name="profile">profile of player generating pscav</param>
|
||||
/// <returns>BotBase</returns>
|
||||
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()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,6 +144,8 @@ public class BotGenerator
|
||||
{
|
||||
_logger.Error("NOT IMPLEMENTED BotGenerator.GenerateBot");
|
||||
|
||||
bot.Inventory.Items = [];
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="postRaidBodyParts">Post-raid body part data</param>
|
||||
/// <param name="profileData">Player profile on server</param>
|
||||
protected void TransferPostRaidLimbEffectsToProfile(BodyPartsHealth postRaidBodyParts, PmcData profileData)
|
||||
protected void TransferPostRaidLimbEffectsToProfile(Dictionary<string, BodyPartHealth> postRaidBodyParts, PmcData profileData)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<string, BodyPartHealth>? 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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace main fence assort with new assort
|
||||
/// </summary>
|
||||
@@ -495,7 +503,30 @@ public class FenceService
|
||||
/// <returns>FenceLevel object</returns>
|
||||
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()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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<CoreConfig>(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<RepeatableQuest> GetActiveRepeatableQuests(List<PmcDataRepeatableQuest> repeatableQuests)
|
||||
{
|
||||
var activeQuests = new List<RepeatableQuest>();
|
||||
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
|
||||
/// <param name="pmcProfile">The profile to validate quest productions for</param>
|
||||
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<string> _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<WeaponBuild>();
|
||||
fullProfile.UserBuildData.WeaponBuilds = weaponBuilds.Where(build =>
|
||||
{
|
||||
return !ShouldRemoveWeaponEquipmentBuild("weapon", build, itemsDb);
|
||||
}).ToList();
|
||||
|
||||
var equipmentBuilds = fullProfile.UserBuildData.EquipmentBuilds ?? new List<EquipmentBuild>();
|
||||
fullProfile.UserBuildData.EquipmentBuilds = equipmentBuilds.Where(build =>
|
||||
{
|
||||
return !ShouldRemoveWeaponEquipmentBuild("equipment", build, itemsDb);
|
||||
}).ToList();
|
||||
|
||||
var magazineBuild = fullProfile.UserBuildData.MagazineBuilds ?? new List<MagazineBuild>();
|
||||
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<string, TemplateItem> 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user