Move RepeatableQuestRewardGenerator.cs and update some reward generation data

This commit is contained in:
Cj
2025-06-24 02:59:37 -04:00
parent 2646f90273
commit c6d5638057
3 changed files with 148 additions and 163 deletions
@@ -219,14 +219,14 @@
"numQuests": 3, "numQuests": 3,
"minPlayerLevel": 5, "minPlayerLevel": 5,
"rewardScaling": { "rewardScaling": {
"levels": [1, 10, 20, 30, 40, 50, 60], "levels": [5, 10, 20, 30, 40, 50, 60],
"experience": [1000, 2000, 8000, 13000, 19000, 24000, 30000], "experience": [1000, 2000, 8000, 13000, 19000, 24000, 30000],
"roubles": [11000, 20000, 32000, 45000, 58000, 70000, 82000], "roubles": [11000, 20000, 32000, 45000, 58000, 70000, 82000],
"gpCoins": [2, 3, 6, 6, 8, 8, 10], "gpCoins": [2, 3, 6, 6, 8, 8, 10],
"items": [2, 4, 5, 5, 5, 5, 5], "items": [2, 3, 4, 5, 5, 5, 5],
"reputation": [0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.03], "reputation": [0.01, 0.02, 0.03, 0.03, 0.03, 0.03, 0.03],
"rewardSpread": 0.5, "rewardSpread": 0.40,
"skillRewardChance": [0, 0.01, 0.05, 0.1, 0.1, 0.15, 0.15], "skillRewardChance": [0, 1, 5, 10, 10, 15, 15],
"skillPointReward": [10, 15, 20, 25, 30, 35, 40] "skillPointReward": [10, 15, 20, 25, 30, 35, 40]
}, },
"locations": { "locations": {
@@ -1031,14 +1031,14 @@
"numQuests": 1, "numQuests": 1,
"minPlayerLevel": 15, "minPlayerLevel": 15,
"rewardScaling": { "rewardScaling": {
"levels": [1, 10, 20, 30, 40, 50, 60], "levels": [5, 10, 20, 30, 40, 50, 60],
"experience": [5000, 15000, 27000, 80000, 210000, 260000, 350000], "experience": [7500, 18000, 30000, 80000, 210000, 260000, 350000],
"roubles": [20000, 50000, 175000, 350000, 540000, 710000, 880000], "roubles": [20000, 125000, 200000, 350000, 540000, 710000, 880000],
"gpCoins": [10, 10, 16, 16, 20, 30, 35], "gpCoins": [10, 10, 16, 16, 20, 30, 35],
"items": [4, 5, 5, 6, 6, 7, 7], "items": [3, 3, 4, 4, 5, 5, 5],
"reputation": [0.02, 0.03, 0.04, 0.04, 0.05, 0.05, 0.05], "reputation": [0.02, 0.03, 0.04, 0.04, 0.05, 0.06, 0.07],
"rewardSpread": 0.5, "rewardSpread": 0.5,
"skillRewardChance": [0, 0.05, 0.1, 0.15, 0.2, 0.2, 0.2], "skillRewardChance": [0, 5, 10, 15, 20, 20, 20],
"skillPointReward": [25, 35, 45, 50, 55, 60, 65] "skillPointReward": [25, 35, 45, 50, 55, 60, 65]
}, },
"locations": { "locations": {
@@ -13,26 +13,26 @@ using SPTarkov.Server.Core.Utils.Cloners;
using SPTarkov.Server.Core.Utils.Collections; using SPTarkov.Server.Core.Utils.Collections;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Generators; namespace SPTarkov.Server.Core.Generators.RepeatableQuestGeneration;
[Injectable] [Injectable]
public class RepeatableQuestRewardGenerator( public class RepeatableQuestRewardGenerator(
ISptLogger<RepeatableQuestRewardGenerator> _logger, ISptLogger<RepeatableQuestRewardGenerator> logger,
RandomUtil _randomUtil, RandomUtil randomUtil,
HashUtil _hashUtil, HashUtil hashUtil,
MathUtil _mathUtil, MathUtil mathUtil,
DatabaseService _databaseService, DatabaseService databaseService,
ItemHelper _itemHelper, ItemHelper itemHelper,
PresetHelper _presetHelper, PresetHelper presetHelper,
HandbookHelper _handbookHelper, HandbookHelper handbookHelper,
LocalisationService _localisationService, LocalisationService localisationService,
ItemFilterService _itemFilterService, ItemFilterService itemFilterService,
SeasonalEventService _seasonalEventService, SeasonalEventService seasonalEventService,
ConfigServer _configServer, ConfigServer configServer,
ICloner _cloner ICloner cloner
) )
{ {
protected QuestConfig _questConfig = _configServer.GetConfig<QuestConfig>(); protected QuestConfig QuestConfig = configServer.GetConfig<QuestConfig>();
/// <summary> /// <summary>
/// Generate the reward for a mission. A reward can consist of: <br /> /// Generate the reward for a mission. A reward can consist of: <br />
@@ -94,7 +94,7 @@ public class RepeatableQuestRewardGenerator(
rewards.Success.Add( rewards.Success.Add(
new Reward new Reward
{ {
Id = _hashUtil.Generate(), Id = hashUtil.Generate(),
Unknown = false, Unknown = false,
GameMode = [], GameMode = [],
AvailableInGameEditions = [], AvailableInGameEditions = [],
@@ -108,13 +108,13 @@ public class RepeatableQuestRewardGenerator(
// Add money reward // Add money reward
rewards.Success.Add( rewards.Success.Add(
GetMoneyReward(traderId, rewardParams.RewardRoubles.Value, rewardIndex) GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex)
); );
rewardIndex++; rewardIndex++;
// Add GP coin reward // Add GP coin reward
rewards.Success.Add( rewards.Success.Add(
GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount.Value, rewardIndex) GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex)
); );
rewardIndex++; rewardIndex++;
@@ -125,17 +125,17 @@ public class RepeatableQuestRewardGenerator(
if (traderWhitelistDetails is null) if (traderWhitelistDetails is null)
{ {
_logger.Error($"Cound not find trader id: {traderId} in whitelist"); logger.Error($"Cound not find trader id: {traderId} in whitelist");
return null; return null;
} }
if ( if (
traderWhitelistDetails.RewardCanBeWeapon traderWhitelistDetails.RewardCanBeWeapon
&& _randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent) && randomUtil.GetChance100(traderWhitelistDetails.WeaponRewardChancePercent)
) )
{ {
var chosenWeapon = GetRandomWeaponPresetWithinBudget( var chosenWeapon = GetRandomWeaponPresetWithinBudget(
itemRewardBudget.Value, itemRewardBudget,
rewardIndex rewardIndex
); );
if (chosenWeapon is not null) if (chosenWeapon is not null)
@@ -158,16 +158,17 @@ public class RepeatableQuestRewardGenerator(
// Filter reward pool of items from blacklist, only use if there's at least 1 item remaining // Filter reward pool of items from blacklist, only use if there's at least 1 item remaining
var filteredRewardItemPool = inBudgetRewardItemPool.Where(item => var filteredRewardItemPool = inBudgetRewardItemPool.Where(item =>
!rewardTplBlacklist.Contains(item.Id) !rewardTplBlacklist.Contains(item.Id)
); ).ToList();
if (filteredRewardItemPool.Count() > 0)
if (filteredRewardItemPool.Count != 0)
{ {
inBudgetRewardItemPool = filteredRewardItemPool.ToList(); inBudgetRewardItemPool = filteredRewardItemPool.ToList();
} }
} }
if (_logger.IsLogEnabled(LogLevel.Debug)) if (logger.IsLogEnabled(LogLevel.Debug))
{ {
_logger.Debug( logger.Debug(
$"Generating: {repeatableConfig.Name} quest for: {traderId} with budget: {itemRewardBudget} totalling: {rewardParams.RewardNumItems} items" $"Generating: {repeatableConfig.Name} quest for: {traderId} with budget: {itemRewardBudget} totalling: {rewardParams.RewardNumItems} items"
); );
} }
@@ -176,8 +177,8 @@ public class RepeatableQuestRewardGenerator(
{ {
var itemsToReward = GetRewardableItemsFromPoolWithinBudget( var itemsToReward = GetRewardableItemsFromPoolWithinBudget(
inBudgetRewardItemPool, inBudgetRewardItemPool,
rewardParams.RewardNumItems.Value, rewardParams.RewardNumItems,
itemRewardBudget.Value, itemRewardBudget,
repeatableConfig repeatableConfig
); );
@@ -196,7 +197,7 @@ public class RepeatableQuestRewardGenerator(
{ {
Reward reward = new() Reward reward = new()
{ {
Id = _hashUtil.Generate(), Id = hashUtil.Generate(),
Unknown = false, Unknown = false,
GameMode = [], GameMode = [],
AvailableInGameEditions = [], AvailableInGameEditions = [],
@@ -208,21 +209,21 @@ public class RepeatableQuestRewardGenerator(
rewards.Success.Add(reward); rewards.Success.Add(reward);
rewardIndex++; rewardIndex++;
if (_logger.IsLogEnabled(LogLevel.Debug)) if (logger.IsLogEnabled(LogLevel.Debug))
{ {
_logger.Debug( logger.Debug(
$"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward" $"Adding: {rewardParams.RewardReputation} {traderId} trader reputation reward"
); );
} }
} }
// Chance of adding skill reward // Chance of adding skill reward
if (_randomUtil.GetChance100((double)rewardParams.SkillRewardChance * 100)) if (randomUtil.GetChance100(rewardParams.SkillRewardChance))
{ {
var targetSkill = _randomUtil.GetArrayValue(eliminationConfig.PossibleSkillRewards); var targetSkill = randomUtil.GetArrayValue(eliminationConfig.PossibleSkillRewards);
Reward reward = new() Reward reward = new()
{ {
Id = _hashUtil.Generate(), Id = hashUtil.Generate(),
Unknown = false, Unknown = false,
GameMode = [], GameMode = [],
AvailableInGameEditions = [], AvailableInGameEditions = [],
@@ -233,9 +234,9 @@ public class RepeatableQuestRewardGenerator(
}; };
rewards.Success.Add(reward); rewards.Success.Add(reward);
if (_logger.IsLogEnabled(LogLevel.Debug)) if (logger.IsLogEnabled(LogLevel.Debug))
{ {
_logger.Debug( logger.Debug(
$"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}" $"Adding {rewardParams.SkillPointReward} skill points to {targetSkill}"
); );
} }
@@ -245,8 +246,8 @@ public class RepeatableQuestRewardGenerator(
} }
protected QuestRewardValues GetQuestRewardValues( protected QuestRewardValues GetQuestRewardValues(
RewardScaling? rewardScaling, RewardScaling rewardScaling,
double? difficulty, double effectiveDifficulty,
int pmcLevel int pmcLevel
) )
{ {
@@ -261,16 +262,10 @@ public class RepeatableQuestRewardGenerator(
var skillPointRewardConfig = rewardScaling.SkillPointReward; var skillPointRewardConfig = rewardScaling.SkillPointReward;
var reputationConfig = rewardScaling.Reputation; var reputationConfig = rewardScaling.Reputation;
var effectiveDifficulty = difficulty is null ? 1 : difficulty;
if (difficulty is null)
{
_logger.Warning(_localisationService.GetText("repeatable-difficulty_was_nan"));
}
return new QuestRewardValues return new QuestRewardValues
{ {
SkillPointReward = _mathUtil.Interp1(pmcLevel, levelsConfig, skillPointRewardConfig), SkillPointReward = mathUtil.Interp1(pmcLevel, levelsConfig, skillPointRewardConfig),
SkillRewardChance = _mathUtil.Interp1(pmcLevel, levelsConfig, skillRewardChanceConfig), SkillRewardChance = mathUtil.Interp1(pmcLevel, levelsConfig, skillRewardChanceConfig),
RewardReputation = GetRewardRep( RewardReputation = GetRewardRep(
effectiveDifficulty, effectiveDifficulty,
pmcLevel, pmcLevel,
@@ -304,91 +299,81 @@ public class RepeatableQuestRewardGenerator(
} }
protected double GetRewardXp( protected double GetRewardXp(
double? effectiveDifficulty, double effectiveDifficulty,
int pmcLevel, int pmcLevel,
List<double>? levelsConfig, List<double> levelsConfig,
List<double>? xpConfig, List<double> xpConfig,
double? rewardSpreadConfig double rewardSpreadConfig
) )
{ {
var interpolatedXp = mathUtil.Interp1(pmcLevel, levelsConfig, xpConfig);
var randomSpread = randomUtil.GetDouble(1 - rewardSpreadConfig, 1 + rewardSpreadConfig);
return Math.Floor( return Math.Floor(
effectiveDifficulty effectiveDifficulty * interpolatedXp * randomSpread
* _mathUtil.Interp1(pmcLevel, levelsConfig, xpConfig)
* _randomUtil.GetDouble(
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig)
)
?? 0
); );
} }
protected double GetGpCoinRewardCount( protected double GetGpCoinRewardCount(
double? effectiveDifficulty, double effectiveDifficulty,
int pmcLevel, int pmcLevel,
List<double>? levelsConfig, List<double> levelsConfig,
List<double>? gpCoinConfig, List<double> gpCoinConfig,
double? rewardSpreadConfig double rewardSpreadConfig
) )
{ {
var interpolatedGpCoins = mathUtil.Interp1(pmcLevel, levelsConfig, gpCoinConfig);
var randomSpread = randomUtil.GetDouble(1 - rewardSpreadConfig, 1 + rewardSpreadConfig);
return Math.Ceiling( return Math.Ceiling(
effectiveDifficulty effectiveDifficulty * interpolatedGpCoins * randomSpread
* _mathUtil.Interp1(pmcLevel, levelsConfig, gpCoinConfig)
* _randomUtil.GetDouble(
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig)
)
?? 0
); );
} }
protected double GetRewardRep( protected double GetRewardRep(
double? effectiveDifficulty, double effectiveDifficulty,
int pmcLevel, int pmcLevel,
List<double>? levelsConfig, List<double> levelsConfig,
List<double>? reputationConfig, List<double> reputationConfig,
double? rewardSpreadConfig double rewardSpreadConfig
) )
{ {
return Math.Round(
100 var difficultyMod = 100 * effectiveDifficulty;
* effectiveDifficulty var interpolatedRep = mathUtil.Interp1(pmcLevel, levelsConfig, reputationConfig);
* _mathUtil.Interp1(pmcLevel, levelsConfig, reputationConfig) var randomSpread = randomUtil.GetDouble(1 - rewardSpreadConfig, 1 + rewardSpreadConfig);
* _randomUtil.GetDouble( var multiplier = difficultyMod * interpolatedRep * randomSpread;
(double)(1 - rewardSpreadConfig),
(double)(1 + rewardSpreadConfig) return Math.Round(multiplier) / 100;
)
?? 0
) / 100;
} }
protected int GetRewardNumItems( protected int GetRewardNumItems(
int pmcLevel, int pmcLevel,
List<double>? levelsConfig, List<double> levelsConfig,
List<double>? itemsConfig List<double> itemsConfig
) )
{ {
return _randomUtil.RandInt( var interpolatedNumItems = mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig);
return randomUtil.RandInt(
1, 1,
(int)Math.Round(_mathUtil.Interp1(pmcLevel, levelsConfig, itemsConfig)) + 1 (int)Math.Round(interpolatedNumItems) + 1
); );
} }
protected double GetRewardRoubles( protected double GetRewardRoubles(
double? effectiveDifficulty, double effectiveDifficulty,
int pmcLevel, int pmcLevel,
List<double>? levelsConfig, List<double> levelsConfig,
List<double>? roublesConfig, List<double> roublesConfig,
double? rewardSpreadConfig double rewardSpreadConfig
) )
{ {
var interpolatedRoubles = mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig);
var randomSpread = randomUtil.GetDouble(1d - rewardSpreadConfig, 1d + rewardSpreadConfig);
return Math.Floor( return Math.Floor(
effectiveDifficulty effectiveDifficulty * interpolatedRoubles * randomSpread
* _mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig)
* _randomUtil.GetDouble(
1d - rewardSpreadConfig.Value,
1d + rewardSpreadConfig.Value
)
?? 0
); );
} }
@@ -408,7 +393,7 @@ public class RepeatableQuestRewardGenerator(
) )
{ {
var itemsToReturn = new Dictionary<TemplateItem, int>(); var itemsToReturn = new Dictionary<TemplateItem, int>();
var exhausableItemPool = new ExhaustableArray<TemplateItem>(itemPool, _randomUtil, _cloner); var exhaustibleItemPool = new ExhaustableArray<TemplateItem>(itemPool, randomUtil, cloner);
for (var i = 0; i < maxItemCount; i++) for (var i = 0; i < maxItemCount; i++)
{ {
@@ -416,14 +401,14 @@ public class RepeatableQuestRewardGenerator(
var rewardItemStackCount = 1; var rewardItemStackCount = 1;
// Get a random item // Get a random item
var chosenItemFromPool = exhausableItemPool.GetRandomValue(); var chosenItemFromPool = exhaustibleItemPool.GetRandomValue();
if (!exhausableItemPool.HasValues()) if (chosenItemFromPool is null || !exhaustibleItemPool.HasValues())
{ {
break; break;
} }
// Handle edge case - ammo // Handle edge case - ammo
if (_itemHelper.IsOfBaseclass(chosenItemFromPool.Id, BaseClasses.AMMO)) if (itemHelper.IsOfBaseclass(chosenItemFromPool.Id, BaseClasses.AMMO))
{ {
// Don't reward ammo that stacks to less than what's allowed in config // Don't reward ammo that stacks to less than what's allowed in config
if ( if (
@@ -435,7 +420,7 @@ public class RepeatableQuestRewardGenerator(
continue; continue;
} }
// Choose smallest value between budget, fitting size and stack max // Choose the smallest value between budget, fitting size and stack max
rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget( rewardItemStackCount = CalculateAmmoStackSizeThatFitsBudget(
chosenItemFromPool, chosenItemFromPool,
itemRewardBudget, itemRewardBudget,
@@ -452,11 +437,11 @@ public class RepeatableQuestRewardGenerator(
itemsToReturn.Add(chosenItemFromPool, rewardItemStackCount); itemsToReturn.Add(chosenItemFromPool, rewardItemStackCount);
var itemCost = _presetHelper.GetDefaultPresetOrItemPrice(chosenItemFromPool.Id); var itemCost = presetHelper.GetDefaultPresetOrItemPrice(chosenItemFromPool.Id);
var calculatedItemRewardBudget = itemRewardBudget - rewardItemStackCount * itemCost; var calculatedItemRewardBudget = itemRewardBudget - rewardItemStackCount * itemCost;
if (_logger.IsLogEnabled(LogLevel.Debug)) if (logger.IsLogEnabled(LogLevel.Debug))
{ {
_logger.Debug( logger.Debug(
$"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}" $"Added item: {chosenItemFromPool.Id} with price: {rewardItemStackCount * itemCost}"
); );
} }
@@ -465,17 +450,17 @@ public class RepeatableQuestRewardGenerator(
if (calculatedItemRewardBudget > 0) if (calculatedItemRewardBudget > 0)
{ {
// Filter possible reward items to only items with a price below the remaining budget // Filter possible reward items to only items with a price below the remaining budget
exhausableItemPool = new ExhaustableArray<TemplateItem>( exhaustibleItemPool = new ExhaustableArray<TemplateItem>(
FilterRewardPoolWithinBudget(itemPool, calculatedItemRewardBudget, 0), FilterRewardPoolWithinBudget(itemPool, calculatedItemRewardBudget, 0),
_randomUtil, randomUtil,
_cloner cloner
); );
if (!exhausableItemPool.HasValues()) if (!exhaustibleItemPool.HasValues())
{ {
if (_logger.IsLogEnabled(LogLevel.Debug)) if (logger.IsLogEnabled(LogLevel.Debug))
{ {
_logger.Debug( logger.Debug(
$"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining" $"Reward pool empty with: {calculatedItemRewardBudget} roubles of budget remaining"
); );
} }
@@ -506,7 +491,7 @@ public class RepeatableQuestRewardGenerator(
// Calculate budget per reward item // Calculate budget per reward item
var stackRoubleBudget = roublesBudget / rewardNumItems; var stackRoubleBudget = roublesBudget / rewardNumItems;
var singleCartridgePrice = _handbookHelper.GetTemplatePrice(itemSelected.Id); var singleCartridgePrice = handbookHelper.GetTemplatePrice(itemSelected.Id);
// Get a stack size of ammo that fits rouble budget // Get a stack size of ammo that fits rouble budget
var stackSizeThatFitsBudget = Math.Round(stackRoubleBudget / singleCartridgePrice); var stackSizeThatFitsBudget = Math.Round(stackRoubleBudget / singleCartridgePrice);
@@ -525,14 +510,14 @@ public class RepeatableQuestRewardGenerator(
) )
{ {
var isEligibleForStackSizeIncrease = var isEligibleForStackSizeIncrease =
_presetHelper.GetDefaultPresetOrItemPrice(item.Id) < maxRoublePriceToStack presetHelper.GetDefaultPresetOrItemPrice(item.Id) < maxRoublePriceToStack
&& !_itemHelper.IsOfBaseclasses( && !itemHelper.IsOfBaseclasses(
item.Id, item.Id,
[BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO] [BaseClasses.WEAPON, BaseClasses.ARMORED_EQUIPMENT, BaseClasses.AMMO]
) )
&& !_itemHelper.ItemRequiresSoftInserts(item.Id); && !itemHelper.ItemRequiresSoftInserts(item.Id);
return isEligibleForStackSizeIncrease && _randomUtil.GetChance100(randomChanceToPass); return isEligibleForStackSizeIncrease && randomUtil.GetChance100(randomChanceToPass);
} }
/// <summary> /// <summary>
@@ -542,7 +527,7 @@ public class RepeatableQuestRewardGenerator(
/// <returns> Matching stack size for the passed in items price </returns> /// <returns> Matching stack size for the passed in items price </returns>
protected int GetRandomisedRewardItemStackSizeByPrice(TemplateItem item) protected int GetRandomisedRewardItemStackSizeByPrice(TemplateItem item)
{ {
var rewardItemPrice = _presetHelper.GetDefaultPresetOrItemPrice(item.Id); var rewardItemPrice = presetHelper.GetDefaultPresetOrItemPrice(item.Id);
// Define price tiers and corresponding stack size options // Define price tiers and corresponding stack size options
var priceTiers = new List<Tuple<int, List<int>?>> var priceTiers = new List<Tuple<int, List<int>?>>
@@ -559,7 +544,7 @@ public class RepeatableQuestRewardGenerator(
return 4; // Default to 2 if no tier matches return 4; // Default to 2 if no tier matches
} }
return _randomUtil.GetArrayValue(tier.Item2); return randomUtil.GetArrayValue(tier.Item2);
} }
/// <summary> /// <summary>
@@ -587,8 +572,8 @@ public class RepeatableQuestRewardGenerator(
if (rewardableItemPoolWithinBudget.Count == 0) if (rewardableItemPoolWithinBudget.Count == 0)
{ {
_logger.Warning( logger.Warning(
_localisationService.GetText( localisationService.GetText(
"repeatable-no_reward_item_found_in_price_range", "repeatable-no_reward_item_found_in_price_range",
new { minPrice, roublesBudget } new { minPrice, roublesBudget }
) )
@@ -596,7 +581,7 @@ public class RepeatableQuestRewardGenerator(
// In case we don't find any items in the price range // In case we don't find any items in the price range
rewardableItemPoolWithinBudget = rewardableItemPool rewardableItemPoolWithinBudget = rewardableItemPool
.Where(x => _itemHelper.GetItemPrice(x.Id) < roublesBudget) .Where(x => itemHelper.GetItemPrice(x.Id) < roublesBudget)
.ToList(); .ToList();
} }
@@ -619,7 +604,7 @@ public class RepeatableQuestRewardGenerator(
return rewardItems return rewardItems
.Where(item => .Where(item =>
{ {
var itemPrice = _presetHelper.GetDefaultPresetOrItemPrice(item.Id); var itemPrice = presetHelper.GetDefaultPresetOrItemPrice(item.Id);
return itemPrice < roublesBudget && itemPrice > minPrice; return itemPrice < roublesBudget && itemPrice > minPrice;
}) })
.ToList(); .ToList();
@@ -638,9 +623,9 @@ public class RepeatableQuestRewardGenerator(
{ {
// Add a random default preset weapon as reward // Add a random default preset weapon as reward
var defaultPresetPool = new ExhaustableArray<Preset>( var defaultPresetPool = new ExhaustableArray<Preset>(
_presetHelper.GetDefaultWeaponPresets().Values.ToList(), presetHelper.GetDefaultWeaponPresets().Values.ToList(),
_randomUtil, randomUtil,
_cloner cloner
); );
while (defaultPresetPool.HasValues()) while (defaultPresetPool.HasValues())
@@ -655,11 +640,11 @@ public class RepeatableQuestRewardGenerator(
var tpls = randomPreset.Items.Select(item => item.Template).ToList(); var tpls = randomPreset.Items.Select(item => item.Template).ToList();
// Does preset items fit our budget // Does preset items fit our budget
var presetPrice = _itemHelper.GetItemAndChildrenPrice(tpls); var presetPrice = itemHelper.GetItemAndChildrenPrice(tpls);
if (presetPrice <= roublesBudget) if (presetPrice <= roublesBudget)
{ {
_logger.Debug($"Added weapon: {tpls[0]}with price: {presetPrice}"); logger.Debug($"Added weapon: {tpls[0]}with price: {presetPrice}");
var chosenPreset = _cloner.Clone(randomPreset); var chosenPreset = cloner.Clone(randomPreset);
return new KeyValuePair<Reward, double>( return new KeyValuePair<Reward, double>(
GeneratePresetReward( GeneratePresetReward(
@@ -693,10 +678,10 @@ public class RepeatableQuestRewardGenerator(
bool foundInRaid = true bool foundInRaid = true
) )
{ {
var id = _hashUtil.Generate(); var id = hashUtil.Generate();
var questRewardItem = new Reward var questRewardItem = new Reward
{ {
Id = _hashUtil.Generate(), Id = hashUtil.Generate(),
Unknown = false, Unknown = false,
GameMode = [], GameMode = [],
AvailableInGameEditions = [], AvailableInGameEditions = [],
@@ -710,18 +695,18 @@ public class RepeatableQuestRewardGenerator(
}; };
// Get presets root item // Get presets root item
var rootItem = preset.FirstOrDefault(item => item.Template == tpl); var rootItem = preset?.FirstOrDefault(item => item.Template == tpl);
if (rootItem is null) if (rootItem is null)
{ {
_logger.Warning($"Root item of preset: {tpl} not found"); logger.Warning($"Root item of preset: {tpl} not found");
} }
if (rootItem.Upd is not null) if (rootItem?.Upd is not null)
{ {
rootItem.Upd.SpawnedInSession = foundInRaid; rootItem.Upd.SpawnedInSession = foundInRaid;
} }
questRewardItem.Items = _itemHelper.ReparentItemAndChildren(rootItem, preset); questRewardItem.Items = itemHelper.ReparentItemAndChildren(rootItem, preset);
questRewardItem.Target = rootItem.Id; // Target property and root items id must match questRewardItem.Target = rootItem.Id; // Target property and root items id must match
return questRewardItem; return questRewardItem;
@@ -742,10 +727,10 @@ public class RepeatableQuestRewardGenerator(
bool foundInRaid = true bool foundInRaid = true
) )
{ {
var id = _hashUtil.Generate(); var id = hashUtil.Generate();
var questRewardItem = new Reward var questRewardItem = new Reward
{ {
Id = _hashUtil.Generate(), Id = hashUtil.Generate(),
Unknown = false, Unknown = false,
GameMode = [], GameMode = [],
AvailableInGameEditions = [], AvailableInGameEditions = [],
@@ -780,7 +765,7 @@ public class RepeatableQuestRewardGenerator(
// Convert reward amount to Euros if necessary // Convert reward amount to Euros if necessary
var rewardAmountToGivePlayer = var rewardAmountToGivePlayer =
currency == Money.EUROS currency == Money.EUROS
? _handbookHelper.FromRUB(rewardRoubles, Money.EUROS) ? handbookHelper.FromRUB(rewardRoubles, Money.EUROS)
: rewardRoubles; : rewardRoubles;
// Get chosen currency + amount and return // Get chosen currency + amount and return
@@ -803,12 +788,12 @@ public class RepeatableQuestRewardGenerator(
) )
{ {
// Get an array of seasonal items that should not be shown right now as seasonal event is not active // Get an array of seasonal items that should not be shown right now as seasonal event is not active
var seasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems(); var seasonalItems = seasonalEventService.GetInactiveSeasonalEventItems();
// Check for specific base classes which don't make sense as reward item // Check for specific base classes which don't make sense as reward item
// also check if the price is greater than 0; there are some items whose price can not be found // also check if the price is greater than 0; there are some items whose price can not be found
// those are not in the game yet (e.g. AGS grenade launcher) // those are not in the game yet (e.g. AGS grenade launcher)
return _databaseService return databaseService
.GetItems() .GetItems()
.Values.Where(itemTemplate => .Values.Where(itemTemplate =>
{ {
@@ -854,36 +839,36 @@ public class RepeatableQuestRewardGenerator(
) )
{ {
// Return early if not valid item to give as reward // Return early if not valid item to give as reward
if (!_itemHelper.IsValidItem(tpl)) if (!itemHelper.IsValidItem(tpl))
{ {
return false; return false;
} }
// Check item is not blacklisted // Check item is not blacklisted
if ( if (
_itemFilterService.IsItemBlacklisted(tpl) itemFilterService.IsItemBlacklisted(tpl)
|| _itemFilterService.IsItemRewardBlacklisted(tpl) || itemFilterService.IsItemRewardBlacklisted(tpl)
|| itemTplBlacklist.Contains(tpl) || itemTplBlacklist.Contains(tpl)
|| _itemFilterService.IsItemBlacklisted(tpl) || itemFilterService.IsItemBlacklisted(tpl)
) )
{ {
return false; return false;
} }
// Item has blacklisted base types // Item has blacklisted base types
if (_itemHelper.IsOfBaseclasses(tpl, itemTypeBlacklist)) if (itemHelper.IsOfBaseclasses(tpl, itemTypeBlacklist))
{ {
return false; return false;
} }
// Skip boss items // Skip boss items
if (_itemFilterService.IsBossItem(tpl)) if (itemFilterService.IsBossItem(tpl))
{ {
return false; return false;
} }
// Trader has specific item base types they can give as rewards to player // Trader has specific item base types they can give as rewards to player
if (itemBaseWhitelist is not null && !_itemHelper.IsOfBaseclasses(tpl, itemBaseWhitelist)) if (itemBaseWhitelist is not null && !itemHelper.IsOfBaseclasses(tpl, itemBaseWhitelist))
{ {
return false; return false;
} }
@@ -8,23 +8,23 @@ public record QuestRewardValues
public Dictionary<string, object> ExtensionData { get; set; } public Dictionary<string, object> ExtensionData { get; set; }
[JsonPropertyName("skillPointReward")] [JsonPropertyName("skillPointReward")]
public double? SkillPointReward { get; set; } public required double SkillPointReward { get; set; }
[JsonPropertyName("skillRewardChance")] [JsonPropertyName("skillRewardChance")]
public double? SkillRewardChance { get; set; } public required double SkillRewardChance { get; set; }
[JsonPropertyName("rewardReputation")] [JsonPropertyName("rewardReputation")]
public double? RewardReputation { get; set; } public required double RewardReputation { get; set; }
[JsonPropertyName("rewardNumItems")] [JsonPropertyName("rewardNumItems")]
public int? RewardNumItems { get; set; } public required int RewardNumItems { get; set; }
[JsonPropertyName("rewardRoubles")] [JsonPropertyName("rewardRoubles")]
public double? RewardRoubles { get; set; } public required double RewardRoubles { get; set; }
[JsonPropertyName("gpCoinRewardCount")] [JsonPropertyName("gpCoinRewardCount")]
public double? GpCoinRewardCount { get; set; } public required double GpCoinRewardCount { get; set; }
[JsonPropertyName("rewardXP")] [JsonPropertyName("rewardXP")]
public double? RewardXP { get; set; } public required double RewardXP { get; set; }
} }