Files
SPT-Server-Build/Libraries/SPTarkov.Server.Core/Helpers/QuestRewardHelper.cs
T
2025-03-07 13:16:43 +00:00

167 lines
6.5 KiB
C#

using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.ItemEvent;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Services;
using SPTarkov.Server.Core.Utils;
using SPTarkov.Server.Core.Utils.Cloners;
using SPTarkov.Common.Annotations;
using SPTarkov.Common.Extensions;
namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class QuestRewardHelper(
ISptLogger<QuestRewardHelper> _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<QuestConfig>();
/**
* 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<Item> 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<Item>();
}
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<Item>();
}
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<List<Reward>>(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;
}
/// <summary>
/// Get players money reward bonus from profile
/// </summary>
/// <param name="pmcData">player profile</param>
/// <returns>bonus as a percent</returns>
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 multiplier, 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<Reward>) clonedQuest.Rewards.GetType()
.GetProperties()
.FirstOrDefault(p => p.Name == questStatus.ToString())
.GetValue(quest.Rewards) ??
new List<Reward>();
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 = newCurrencyAmount;
reward.Value = newCurrencyAmount;
}
return clonedQuest;
}
}