using SptCommon.Annotations; using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.ItemEvent; 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; using SptCommon.Extensions; namespace Core.Helpers; [Injectable] public class QuestRewardHelper( ISptLogger _logger, HashUtil _hashUtil, TimeUtil _timeUtil, ItemHelper _itemHelper, PaymentHelper _paymentHelper, TraderHelper _traderHelper, DatabaseService _databaseService, QuestConditionHelper _questConditionHelper, ProfileHelper _profileHelper, PresetHelper _presetHelper, RewardHelper _rewardHelper, LocalisationService _localisationService, ICloner _cloner, ConfigServer _configServer ) { protected QuestConfig _questConfig = _configServer.GetConfig(); /** * Give player quest rewards - Skills/exp/trader standing/items/assort unlocks - Returns reward items player earned * @param profileData Player profile (scav or pmc) * @param questId questId of quest to get rewards for * @param state State of the quest to get rewards for * @param sessionId Session id * @param questResponse Response to send back to client * @returns Array of reward objects */ public IEnumerable ApplyQuestReward(PmcData profileData, string questId, QuestStatusEnum state, string sessionId, ItemEventRouterResponse questResponse) { // Repeatable quest base data is always in PMCProfile, `profileData` may be scav profile // TODO: consider moving repeatable quest data to profile-agnostic location var fullProfile = _profileHelper.GetFullProfile(sessionId); var pmcProfile = fullProfile.CharacterData.PmcData; if (pmcProfile is null) { _logger.Error($"Unable to get pmc profile for: {sessionId}, no rewards given"); return Enumerable.Empty(); } var questDetails = GetQuestFromDb(questId, pmcProfile); if (questDetails is null) { _logger.Warning(_localisationService.GetText("quest-unable_to_find_quest_in_db_no_quest_rewards", questId)); return Enumerable.Empty(); } var questMoneyRewardBonusMultiplier = GetQuestMoneyRewardBonusMultiplier(pmcProfile); if (questMoneyRewardBonusMultiplier > 0) // money = money + (money * intelCenterBonus / 100) questDetails = ApplyMoneyBoost(questDetails, questMoneyRewardBonusMultiplier, state); // e.g. 'Success' or 'AvailableForFinish' var rewards = questDetails.Rewards.GetByJsonProp>(state.ToString()); return _rewardHelper.ApplyRewards( rewards, CustomisationSource.UNLOCKED_IN_GAME, fullProfile, profileData, questId, questResponse); } /** * Get quest by id from database (repeatables are stored in profile, check there if questId not found) * @param questId Id of quest to find * @param pmcData Player profile * @returns IQuest object */ protected Quest GetQuestFromDb(string questId, PmcData pmcData) { // May be a repeatable quest var quest = _databaseService.GetQuests()[questId]; if (quest == null) { // Check daily/weekly objects foreach (var repeatableQuest in pmcData.RepeatableQuests) { quest = repeatableQuest.ActiveQuests.FirstOrDefault(r => r.Id == questId); if (quest != null) break; } } return quest; } /// /// Get players money reward bonus from profile /// /// player profile /// bonus as a percent protected double GetQuestMoneyRewardBonusMultiplier(PmcData pmcData) { // Check player has intel center var moneyRewardbonuses = pmcData.Bonuses.Where(bonus => bonus.Type == BonusType.QuestMoneyReward); // Get a total of the quest money reward percent bonuses var moneyRewardBonusPercent = moneyRewardbonuses.Aggregate(0D, (accumulate, bonus) => accumulate + bonus.Value ?? 0); // Calculate hideout management bonus as a percentage (up to 51% bonus) var hideoutManagementSkill = _profileHelper.GetSkillFromProfile(pmcData, SkillTypes.HideoutManagement); // 5100 becomes 0.51, add 1 to it, 1.51 // We multiply the money reward bonuses by the hideout management skill multipler, giving the new result var hideoutManagementBonusMultiplier = (hideoutManagementSkill != null) ? (1 + hideoutManagementSkill.Progress / 1000) : 1; // e.g 15% * 1.4 return moneyRewardBonusPercent * hideoutManagementBonusMultiplier ?? 1; } /** * Adjust quest money rewards by passed in multiplier * @param quest Quest to multiple money rewards * @param bonusPercent Percent to adjust money rewards by * @param questStatus Status of quest to apply money boost to rewards of * @returns Updated quest */ public Quest ApplyMoneyBoost(Quest quest, double bonusPercent, QuestStatusEnum questStatus) { var clonedQuest = _cloner.Clone(quest); var rewards = (List)clonedQuest.Rewards.GetType() .GetProperties() .FirstOrDefault(p => p.Name == questStatus.ToString()) .GetValue(quest.Rewards) ?? new(); var currencyRewards = rewards.Where( r => r.Type.ToString() == "Item" && _paymentHelper.IsMoneyTpl(r.Items.FirstOrDefault().Template) ); foreach (var reward in currencyRewards) { // Add % bonus to existing StackObjectsCount var rewardItem = reward.Items[0]; var newCurrencyAmount = Math.Floor((rewardItem.Upd.StackObjectsCount ?? 0) * (1 + bonusPercent / 100)); rewardItem.Upd.StackObjectsCount = (int)newCurrencyAmount; reward.Value = newCurrencyAmount; } return clonedQuest; } }