Converted rewards data into dictionary - reduced need for reflection

Moved clone outside of `UpdateQuestsForGameEdition`
This commit is contained in:
Chomp
2025-07-26 12:53:56 +01:00
parent 8aff434933
commit bfd616e7c3
7 changed files with 63 additions and 94 deletions
@@ -58,7 +58,7 @@ public class RepeatableQuestRewardGenerator(
/// <param name="eliminationConfig"> Base Quest config</param>
/// <param name="rewardTplBlacklist"> Optional: list of tpls to NOT use when picking a reward </param>
/// <returns> QuestRewards </returns>
public QuestRewards? GenerateReward(
public Dictionary<string, List<Reward>>? GenerateReward(
int pmcLevel,
double difficulty,
MongoId traderId,
@@ -78,11 +78,11 @@ public class RepeatableQuestRewardGenerator(
var itemRewardBudget = rewardParams.RewardRoubles;
// Possible improvement -> draw trader-specific items e.g. with _itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink)
QuestRewards rewards = new()
var rewards = new Dictionary<string, List<Reward>>
{
Started = [],
Success = [],
Fail = [],
{ "Success", [] },
{ "Started", [] },
{ "Fail", [] },
};
// Start reward index to keep track
@@ -91,29 +91,29 @@ public class RepeatableQuestRewardGenerator(
// Add xp reward
if (rewardParams.RewardXP > 0)
{
rewards.Success.Add(
new Reward
{
Id = new MongoId(),
Unknown = false,
GameMode = [],
AvailableInGameEditions = [],
Index = rewardIndex,
Value = rewardParams.RewardXP,
Type = RewardType.Experience,
}
);
rewards["Success"]
.Add(
new Reward
{
Id = new MongoId(),
Unknown = false,
GameMode = [],
AvailableInGameEditions = [],
Index = rewardIndex,
Value = rewardParams.RewardXP,
Type = RewardType.Experience,
}
);
rewardIndex++;
}
// Add money reward
rewards.Success.Add(GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex));
rewards["Success"].Add(GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex));
rewardIndex++;
// Add GP coin reward
rewards.Success.Add(
GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex)
);
rewards["Success"]
.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
rewardIndex++;
// Add preset weapon to reward if checks pass
@@ -135,7 +135,7 @@ public class RepeatableQuestRewardGenerator(
var chosenWeapon = GetRandomWeaponPresetWithinBudget(itemRewardBudget, rewardIndex);
if (chosenWeapon is not null)
{
rewards.Success.Add(chosenWeapon.Value.Key);
rewards["Success"].Add(chosenWeapon.Value.Key);
// Subtract price of preset from item budget so we don't give player too much stuff
itemRewardBudget -= chosenWeapon.Value.Value;
@@ -180,9 +180,8 @@ public class RepeatableQuestRewardGenerator(
// Add item rewards
foreach (var itemReward in itemsToReward)
{
rewards.Success.Add(
GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex)
);
rewards["Success"]
.Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex));
rewardIndex++;
}
}
@@ -201,7 +200,7 @@ public class RepeatableQuestRewardGenerator(
Type = RewardType.TraderStanding,
Index = rewardIndex,
};
rewards.Success.Add(reward);
rewards["Success"].Add(reward);
rewardIndex++;
if (logger.IsLogEnabled(LogLevel.Debug))
@@ -227,7 +226,7 @@ public class RepeatableQuestRewardGenerator(
Type = RewardType.Skill,
Index = rewardIndex,
};
rewards.Success.Add(reward);
rewards["Success"].Add(reward);
if (logger.IsLogEnabled(LogLevel.Debug))
{
@@ -1,6 +1,5 @@
using System.Collections.Frozen;
using System.Globalization;
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
@@ -1403,7 +1402,10 @@ public class QuestHelper(
}
}
return UpdateQuestsForGameEdition(questsToShowPlayer, profile.Info.GameVersion);
return UpdateQuestsForGameEdition(
cloner.Clone(questsToShowPlayer),
profile.Info.GameVersion
);
}
/// <summary>
@@ -1414,11 +1416,10 @@ public class QuestHelper(
/// <returns>Collection of Quest objects with the rewards filtered correctly for the game version</returns>
protected List<Quest> UpdateQuestsForGameEdition(List<Quest> quests, string gameVersion)
{
var modifiedQuests = cloner.Clone(quests);
foreach (var quest in modifiedQuests)
foreach (var quest in quests)
{
// Remove any reward that doesn't pass the game edition check
var propsAsDict = quest.Rewards.GetAllPropsAsDict();
var propsAsDict = quest.Rewards;
foreach (var rewardType in propsAsDict)
{
if (rewardType.Value is null)
@@ -1426,13 +1427,13 @@ public class QuestHelper(
continue;
}
propsAsDict[rewardType.Key] = ((List<Reward>)propsAsDict[rewardType.Key])
propsAsDict[rewardType.Key] = propsAsDict[rewardType.Key]
.Where(reward => rewardHelper.RewardIsForGameEdition(reward, gameVersion))
.ToList();
}
}
return modifiedQuests;
return quests;
}
/// <summary>
@@ -1,4 +1,3 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
@@ -89,7 +88,7 @@ public class QuestRewardHelper(
}
// e.g. 'Success' or 'AvailableForFinish'
var rewards = questDetails.Rewards.GetByJsonProp<List<Reward>>(state.ToString());
var rewards = questDetails.Rewards[state.ToString()];
return rewardHelper.ApplyRewards(
rewards,
CustomisationSource.UNLOCKED_IN_GAME,
@@ -172,18 +171,20 @@ public class QuestRewardHelper(
public Quest ApplyMoneyBoost(Quest quest, double bonusPercent, QuestStatusEnum questStatus)
{
var clonedQuest = cloner.Clone(quest);
if (clonedQuest?.Rewards?.Success == null)
if (clonedQuest?.Rewards?["Success"] == null)
{
return clonedQuest;
}
// Grab just the money rewards from quest reward pool
var moneyRewards = clonedQuest.Rewards.Success.Where(reward =>
reward.Type == RewardType.Item
&& reward.Items != null
&& reward.Items.Count > 0
&& paymentHelper.IsMoneyTpl(reward.Items.FirstOrDefault().Template)
);
var moneyRewards = clonedQuest
.Rewards["Success"]
.Where(reward =>
reward.Type == RewardType.Item
&& reward.Items != null
&& reward.Items.Count > 0
&& paymentHelper.IsMoneyTpl(reward.Items.FirstOrDefault().Template)
);
foreach (var moneyReward in moneyRewards)
{
@@ -85,7 +85,7 @@ public record Quest
public string? TemplateId { get; set; }
[JsonPropertyName("rewards")]
public QuestRewards? Rewards { get; set; }
public Dictionary<string, List<Reward>>? Rewards { get; set; }
/// <summary>
/// Becomes 'AppearStatus' inside client
@@ -489,30 +489,3 @@ public record VisibilityCondition
[JsonPropertyName("conditionType")]
public required string ConditionType { get; set; }
}
public record QuestRewards
{
[JsonExtensionData]
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("AvailableForStart")]
public List<Reward>? AvailableForStart { get; set; }
[JsonPropertyName("AvailableForFinish")]
public List<Reward>? AvailableForFinish { get; set; }
[JsonPropertyName("Started")]
public List<Reward>? Started { get; set; }
[JsonPropertyName("Success")]
public List<Reward>? Success { get; set; }
[JsonPropertyName("Fail")]
public List<Reward>? Fail { get; set; }
[JsonPropertyName("FailRestartable")]
public List<Reward>? FailRestartable { get; set; }
[JsonPropertyName("Expired")]
public List<Reward>? Expired { get; set; }
}
@@ -240,7 +240,7 @@ public record SampleQuests
public bool? CanShowNotificationsInGame { get; set; }
[JsonPropertyName("rewards")]
public QuestRewards? Rewards { get; set; }
public Dictionary<string, List<Reward>>? Rewards { get; set; }
[JsonPropertyName("conditions")]
public QuestConditionTypes? Conditions { get; set; }
@@ -362,9 +362,9 @@ public class ProfileFixerService(
// For started or successful quests, check for unlocks in the `Started` rewards
if (profileQuest.Status is QuestStatusEnum.Started or QuestStatusEnum.Success)
{
var productionRewards = quest.Rewards.Started?.Where(reward =>
reward.Type == RewardType.ProductionScheme
);
var productionRewards = quest
.Rewards["Started"]
?.Where(reward => reward.Type == RewardType.ProductionScheme);
if (productionRewards is not null)
{
@@ -378,9 +378,9 @@ public class ProfileFixerService(
// For successful quests, check for unlocks in the `Success` rewards
if (profileQuest.Status is QuestStatusEnum.Success)
{
var productionRewards = quest.Rewards.Success?.Where(reward =>
reward.Type == RewardType.ProductionScheme
);
var productionRewards = quest
.Rewards["Success"]
?.Where(reward => reward.Type == RewardType.ProductionScheme);
if (productionRewards is not null)
{
@@ -793,16 +793,16 @@ public class ProfileFixerService(
continue;
}
if (activeQuest.Rewards?.Success is null)
if (activeQuest.Rewards?["Success"] is null)
{
continue;
}
// Get Item rewards only
foreach (
var successReward in activeQuest.Rewards.Success.Where(reward =>
reward.Type == RewardType.Item
)
var successReward in activeQuest
.Rewards["Success"]
.Where(reward => reward.Type == RewardType.Item)
)
foreach (var item in successReward.Items)
{