From ec006b4b217648b25b5743b4e22c869445cd4f11 Mon Sep 17 00:00:00 2001 From: CWX Date: Sun, 19 Jan 2025 23:37:28 +0000 Subject: [PATCH] more implementations --- Libraries/Core/Helpers/AssortHelper.cs | 130 ++++++++++++++- Libraries/Core/Helpers/HandbookHelper.cs | 2 +- Libraries/Core/Helpers/TraderAssortHelper.cs | 166 +++++++++++++++++-- 3 files changed, 277 insertions(+), 21 deletions(-) diff --git a/Libraries/Core/Helpers/AssortHelper.cs b/Libraries/Core/Helpers/AssortHelper.cs index 376abcbc..b595062a 100644 --- a/Libraries/Core/Helpers/AssortHelper.cs +++ b/Libraries/Core/Helpers/AssortHelper.cs @@ -1,11 +1,22 @@ using SptCommon.Annotations; using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; +using Core.Models.Enums; +using Core.Models.Utils; +using Core.Servers; +using Core.Services; +using SptCommon.Extensions; namespace Core.Helpers; [Injectable] -public class AssortHelper +public class AssortHelper( + ISptLogger _logger, + ItemHelper _itemHelper, + DatabaseServer _databaseServer, + LocalisationService _localisationService, + QuestHelper _questHelper +) { /** * Remove assorts from a trader that have not been unlocked yet (via player completing corresponding quest) @@ -18,11 +29,39 @@ public class AssortHelper public TraderAssort StripLockedQuestAssort( PmcData pmcProfile, string traderId, - List traderAssorts, + TraderAssort traderAssorts, Dictionary> mergedQuestAssorts, bool flea = false) { - throw new NotImplementedException(); + var strippedTraderAssorts = traderAssorts; + + // Trader assort does not always contain loyal_level_items + if (traderAssorts.LoyalLevelItems is null) + { + _logger.Warning(_localisationService.GetText("assort-missing_loyalty_level_object", traderId)); + + return traderAssorts; + } + + // Iterate over all assorts, removing items that haven't yet been unlocked by quests (ASSORTMENT_UNLOCK) + foreach (var assortId in traderAssorts.LoyalLevelItems) + { + // 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; + } + + // 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); + } + } + + return strippedTraderAssorts; } /** @@ -31,11 +70,36 @@ public class AssortHelper * @param assortId Assort to look for linked quest id * @returns quest id + array of quest status the assort should show for */ - protected (string questId, QuestStatus[] status) GetQuestIdAndStatusThatShowAssort( + protected KeyValuePair>? GetQuestIdAndStatusThatShowAssort( Dictionary> mergedQuestAssorts, string assortId) { - throw new NotImplementedException(); + if (mergedQuestAssorts.Get>("started").Contains(assortId)) + { + // Assort unlocked by starting quest, assort is visible to player when : started or ready to hand in + handed in + return new KeyValuePair>( + mergedQuestAssorts.Get>("started")[assortId], + [QuestStatusEnum.Started, QuestStatusEnum.AvailableForFinish, QuestStatusEnum.Success] + ); + } + + if (mergedQuestAssorts.Get>("success").Contains(assortId)) + { + return new KeyValuePair>( + mergedQuestAssorts.Get>("success")[assortId], + [QuestStatusEnum.Success] + ); + } + + if (mergedQuestAssorts.Get>("fail").Contains(assortId)) + { + return new KeyValuePair>( + mergedQuestAssorts.Get>("success")[assortId], + [QuestStatusEnum.Fail] + ); + } + + return null; } /** @@ -45,9 +109,28 @@ public class AssortHelper * @param assort traders assorts * @returns traders assorts minus locked loyalty assorts */ - public TraderAssort StripLockedLoyaltyAssort(PmcData pmcProfile, string traderId, List assort) + public TraderAssort StripLockedLoyaltyAssort(PmcData pmcProfile, string traderId, TraderAssort assort) { - throw new NotImplementedException(); + var strippedAssort = assort; + + // Trader assort does not always contain loyal_level_items + if (assort.LoyalLevelItems is null) + { + _logger.Warning(_localisationService.GetText("assort-missing_loyalty_level_object", traderId)); + + return strippedAssort; + } + + // Remove items restricted by loyalty levels above those reached by the player + foreach (var item in assort.LoyalLevelItems) + { + if (assort.LoyalLevelItems[item.Key] > pmcProfile.TradersInfo[traderId].LoyaltyLevel) + { + strippedAssort = RemoveItemFromAssort(assort, item.Key); + } + } + + return strippedAssort; } /** @@ -56,8 +139,37 @@ public class AssortHelper * @param itemID item id to remove from assort * @returns Modified assort */ - public TraderAssort RemoveItemFromAssort(List assort, string itemID, bool flea = false) + public TraderAssort RemoveItemFromAssort(TraderAssort assort, string itemID, bool flea = false) { - throw new NotImplementedException(); + var idsToRemove = _itemHelper.FindAndReturnChildrenByItems(assort.Items, itemID); + + if (assort.BarterScheme[itemID] is not null && flea) + { + foreach (var barterSchemes in assort.BarterScheme[itemID]) + { + foreach (var barterScheme in barterSchemes) + { + barterScheme.SptQuestLocked = true; + } + } + + return assort; + } + + assort.BarterScheme.Remove(itemID); + assort.LoyalLevelItems.Remove(itemID); + + foreach (var i in idsToRemove) + { + foreach (var a in assort.Items.ToList()) + { + if (a.Id == i) + { + assort.Items.Remove(a); + } + } + } + + return assort; } } diff --git a/Libraries/Core/Helpers/HandbookHelper.cs b/Libraries/Core/Helpers/HandbookHelper.cs index 5ba7e9e9..06e3c1f7 100644 --- a/Libraries/Core/Helpers/HandbookHelper.cs +++ b/Libraries/Core/Helpers/HandbookHelper.cs @@ -8,7 +8,7 @@ using Core.Utils.Cloners; namespace Core.Helpers; -[Injectable] +[Injectable(InjectionType.Singleton)] public class HandbookHelper( DatabaseService _databaseService, ConfigServer _configServer, diff --git a/Libraries/Core/Helpers/TraderAssortHelper.cs b/Libraries/Core/Helpers/TraderAssortHelper.cs index 2f2ba6f7..76bf4e1b 100644 --- a/Libraries/Core/Helpers/TraderAssortHelper.cs +++ b/Libraries/Core/Helpers/TraderAssortHelper.cs @@ -1,12 +1,40 @@ -using SptCommon.Annotations; +using Core.Generators; +using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; +using Core.Models.Enums; +using Core.Models.Spt.Config; +using Core.Models.Utils; +using Core.Servers; +using Core.Services; +using Core.Utils; +using Core.Utils.Cloners; namespace Core.Helpers; -[Injectable] -public class TraderAssortHelper() +[Injectable(InjectionType.Singleton)] +public class TraderAssortHelper( + ISptLogger _logger, + MathUtil _mathUtil, + TimeUtil _timeUtil, + DatabaseService _databaseService, + ProfileHelper _profileHelper, + AssortHelper _assortHelper, + PaymentHelper _paymentHelper, + RagfairAssortGenerator _ragfairAssortGenerator, + RagfairOfferGenerator _ragfairOfferGenerator, + TraderAssortService _traderAssortService, + LocalisationService _localisationService, + TraderPurchasePersisterService _traderPurchasePersisterService, + TraderHelper _traderHelper, + FenceService _fenceService, + ConfigServer _configServer, + ICloner _cloner +) { - + protected TraderConfig _traderConfig = _configServer.GetConfig(); + protected Dictionary> _mergedQuestAssorts = new Dictionary>(); + protected bool createdMergedQuestAssorts = false; + /// /// Get a traders assorts /// Can be used for returning ragfair / fence assorts @@ -18,7 +46,77 @@ public class TraderAssortHelper() /// a traders' assorts public TraderAssort GetAssort(string sessionId, string traderId, bool showLockedAssorts = false) { - throw new NotImplementedException(); + var traderClone = _cloner.Clone(_databaseService.GetTrader(traderId)); + var fullProfile = _profileHelper.GetFullProfile(sessionId); + var pmcProfile = fullProfile?.CharacterData?.PmcData; + + if (traderId == Traders.FENCE) + { + return _fenceService.GetFenceAssorts(pmcProfile); + } + + // Strip assorts player should not see yet + if (!showLockedAssorts) + { + traderClone.Assort = _assortHelper.StripLockedLoyaltyAssort(pmcProfile, traderId, traderClone.Assort); + } + + ResetBuyRestrictionCurrentValue(traderClone.Assort.Items); + + // Append nextResupply value to assorts so client knows when refresh is occuring + traderClone.Assort.NextResupply = traderClone.Base.NextResupply; + + // Adjust displayed assort counts based on values stored in profile + var assortPurchasesfromTrader = _traderPurchasePersisterService.GetProfileTraderPurchases( + sessionId, + traderId + ); + + foreach (var assortId in assortPurchasesfromTrader) + { + // Find assort we want to update current buy count of + var assortToAdjust = traderClone.Assort.Items.FirstOrDefault(x => x.Id == assortId.Key); + if (assortToAdjust is null) + { + _logger.Debug($"Cannot find trader: {traderClone.Base.Nickname} assort: {assortId} to adjust BuyRestrictionCurrent value, skipping"); + + continue; + } + + if (assortToAdjust.Upd is null) + { + _logger.Debug( + $"Unable to adjust assort {assortToAdjust.Id} item: {assortToAdjust.Template} BuyRestrictionCurrent value, assort has a null upd object" + ); + + continue; + } + + assortToAdjust.Upd.BuyRestrictionCurrent = assortPurchasesfromTrader[assortId.Key].PurchaseCount; + } + + // Get rid of quest locked assorts + if (!this.createdMergedQuestAssorts) + { + HydrateMergedQuestAssorts(); + this.createdMergedQuestAssorts = true; + } + + traderClone.Assort = _assortHelper.StripLockedQuestAssort( + pmcProfile, + traderId, + traderClone.Assort, + _mergedQuestAssorts, + showLockedAssorts + ); + + // Filter out root assorts that are blacklisted for this profile + if (fullProfile.SptData.BlacklistedItemTemplates?.Count > 0) + { + RemoveItemsFromAssort(traderClone.Assort, fullProfile.SptData.BlacklistedItemTemplates); + } + + return traderClone.Assort; } /// @@ -28,7 +126,11 @@ public class TraderAssortHelper() /// Item TPLs the assort should not have protected void RemoveItemsFromAssort(TraderAssort assortToFilter, List itemsTplsToRemove) { - throw new NotImplementedException(); + assortToFilter.Items = assortToFilter.Items.Where( + item => + item.ParentId == "hideout" && itemsTplsToRemove.Contains(item.Template) + ) + .ToList(); } /// @@ -37,7 +139,17 @@ public class TraderAssortHelper() /// Items to adjust protected void ResetBuyRestrictionCurrentValue(List assortItems) { - throw new NotImplementedException(); + // iterate over root items + foreach (var assort in assortItems.Where(item => item.SlotId == "hideout")) + { + // no value to adjust + if (assort.Upd.BuyRestrictionCurrent is null) + { + continue; + } + + assort.Upd.BuyRestrictionCurrent = 0; + } } /// @@ -45,7 +157,30 @@ public class TraderAssortHelper() /// protected void HydrateMergedQuestAssorts() { - throw new NotImplementedException(); + // Loop every trader + var traders = _databaseService.GetTraders(); + foreach (var trader in traders) + { + // Trader has quest assort data + if (trader.Value.QuestAssort is not null) + { + // Started/Success/fail + foreach (var assort in trader.Value.QuestAssort) + { + // Each assort to quest id record + foreach (var assortId in assort.Value.Values) + { + // Null guard + if (!_mergedQuestAssorts.TryGetValue(assortId, out var _)) + { + _mergedQuestAssorts.TryAdd(assortId, new Dictionary()); + } + + _mergedQuestAssorts[assort.Key][assortId] = trader.Value.QuestAssort[assort.Key][assortId]; + } + } + } + } } /// @@ -55,7 +190,13 @@ public class TraderAssortHelper() /// trader details to alter public void ResetExpiredTrader(Trader trader) { - throw new NotImplementedException(); + trader.Assort.Items = GetPristineTraderAssorts(trader.Base.Id); + + // Update resupply value to next timestamp + trader.Base.NextResupply = _traderHelper.GetNextUpdateTimestamp(trader.Base.Id); + + // Flag a refresh is needed so ragfair update() will pick it up + trader.Base.RefreshTraderRagfairOffers = true; } /// @@ -65,7 +206,10 @@ public class TraderAssortHelper() /// true they need refreshing public bool TraderAssortsHaveExpired(string traderID) { - throw new NotImplementedException(); + var time = _timeUtil.GetTimeStamp(); + var trader = _databaseService.GetTables().Traders[traderID]; + + return trader.Base.NextResupply <= time; } /// @@ -75,6 +219,6 @@ public class TraderAssortHelper() /// array of Items protected List GetPristineTraderAssorts(string traderId) { - throw new NotImplementedException(); + return _cloner.Clone(_traderAssortService.GetPristineTraderAssort(traderId).Items); } }