Created GetItemsToRetrievePool to handle logic instead of using GetRewardableItems, now returns tpls instead of entire objects #401
Decoupled `IsValidRewardItem` from repeatable config Made `IsValidRewardItem` public Added `RequiredItemTypeBlacklist` to Completion config, Blacklisted ammo More nullguards
This commit is contained in:
@@ -330,7 +330,6 @@
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"55818b224bdc2dde698b456f",
|
||||
"57864a66245977548f04a81f",
|
||||
"55818af64bdc2d5b648b4570",
|
||||
"550aa4dd4bdc2dc9348b4569",
|
||||
"55818a594bdc2db9688b456a",
|
||||
"55818a104bdc2db9688b4569"
|
||||
@@ -1216,7 +1215,10 @@
|
||||
"requiredItemMinDurabilityMinMax": {
|
||||
"min": 60,
|
||||
"max": 80
|
||||
}
|
||||
},
|
||||
"requiredItemTypeBlacklist": [
|
||||
"5485a8684bdc2da71d8b4567"
|
||||
]
|
||||
},
|
||||
"Elimination": [
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ public class RepeatableQuestGenerator(
|
||||
DatabaseService _databaseService,
|
||||
LocalisationService _localisationService,
|
||||
ConfigServer _configServer,
|
||||
SeasonalEventService _seasonalEventService,
|
||||
ICloner _cloner
|
||||
)
|
||||
{
|
||||
@@ -557,25 +558,27 @@ public class RepeatableQuestGenerator(
|
||||
RepeatableQuestConfig repeatableConfig
|
||||
)
|
||||
{
|
||||
var completionConfig = repeatableConfig.QuestConfig.Completion;
|
||||
var completionConfig = repeatableConfig?.QuestConfig?.Completion;
|
||||
if (completionConfig is null)
|
||||
{
|
||||
_logger.Error("Unable to generate Completion quest, no Completion config found");
|
||||
return null;
|
||||
}
|
||||
var levelsConfig = repeatableConfig.RewardScaling.Levels;
|
||||
var roublesConfig = repeatableConfig.RewardScaling.Roubles;
|
||||
|
||||
var quest = GenerateRepeatableTemplate("Completion", traderId, repeatableConfig.Side, sessionId);
|
||||
|
||||
// Filter the items.json items to items the player must retrieve to complete quest: shouldn't be a quest item or "non-existant"
|
||||
var possibleItemsToRetrievePool = _repeatableQuestRewardGenerator.GetRewardableItems(
|
||||
repeatableConfig,
|
||||
traderId
|
||||
);
|
||||
// Filter the items.json items to items the player must retrieve to complete quest: shouldn't be a quest item or "non-existent"
|
||||
var itemsToRetrievePool = GetItemsToRetrievePool(completionConfig, repeatableConfig.RewardBlacklist);
|
||||
|
||||
// Be fair, don't var the items be more expensive than the reward
|
||||
var multi = _randomUtil.GetDouble(0.5, 1);
|
||||
// Be fair, don't value the items be more expensive than the reward
|
||||
var multiplier = _randomUtil.GetDouble(0.5, 1);
|
||||
var roublesBudget = Math.Floor(
|
||||
(double) (_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multi)
|
||||
(double) (_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multiplier)
|
||||
);
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
var itemSelection = possibleItemsToRetrievePool.Where(x => _itemHelper.GetItemPrice(x.Id) < roublesBudget
|
||||
var itemSelection = itemsToRetrievePool.Where(itemTpl => _itemHelper.GetItemPrice(itemTpl) < roublesBudget
|
||||
)
|
||||
.ToList();
|
||||
|
||||
@@ -585,7 +588,7 @@ public class RepeatableQuestGenerator(
|
||||
{
|
||||
var itemWhitelist = _databaseService.GetTemplates().RepeatableQuests.Data.Completion.ItemsWhitelist;
|
||||
|
||||
// Filter and concatenate the arrays according to current player level
|
||||
// Filter and concatenate items according to current player level
|
||||
var itemIdsWhitelisted = itemWhitelist
|
||||
.Where(p => p.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(x => x.ItemIds)
|
||||
@@ -593,8 +596,8 @@ public class RepeatableQuestGenerator(
|
||||
itemSelection = itemSelection.Where(x =>
|
||||
{
|
||||
// Whitelist can contain item tpls and item base type ids
|
||||
return itemIdsWhitelisted.Any(v => _itemHelper.IsOfBaseclass(x.Id, v)) ||
|
||||
itemIdsWhitelisted.Contains(x.Id);
|
||||
return itemIdsWhitelisted.Any(v => _itemHelper.IsOfBaseclass(x, v)) ||
|
||||
itemIdsWhitelisted.Contains(x);
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
@@ -607,7 +610,7 @@ public class RepeatableQuestGenerator(
|
||||
{
|
||||
var itemBlacklist = _databaseService.GetTemplates().RepeatableQuests.Data.Completion.ItemsBlacklist;
|
||||
|
||||
// we filter and concatenate the arrays according to current player level
|
||||
// Filter and concatenate the arrays according to current player level
|
||||
var itemIdsBlacklisted = itemBlacklist
|
||||
.Where(p => p.MinPlayerLevel <= pmcLevel)
|
||||
.SelectMany(x => x.ItemIds)
|
||||
@@ -615,13 +618,14 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
itemSelection = itemSelection.Where(x =>
|
||||
{
|
||||
return itemIdsBlacklisted.All(v => !_itemHelper.IsOfBaseclass(x.Id, v)) ||
|
||||
!itemIdsBlacklisted.Contains(x.Id);
|
||||
return itemIdsBlacklisted.All(v => !_itemHelper.IsOfBaseclass(x, v)) ||
|
||||
!itemIdsBlacklisted.Contains(x);
|
||||
}
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// Filtering too harsh
|
||||
if (!itemSelection.Any())
|
||||
{
|
||||
_logger.Error(
|
||||
@@ -633,10 +637,7 @@ public class RepeatableQuestGenerator(
|
||||
return null;
|
||||
}
|
||||
|
||||
// Draw items to ask player to retrieve
|
||||
var isAmmo = 0;
|
||||
|
||||
// Store the indexes of items we are asking player to provide
|
||||
// Store the indexes of items we are asking player to supply
|
||||
var distinctItemsToRetrieveCount = _randomUtil.GetInt(1, completionConfig.UniqueItemCount.Value);
|
||||
var chosenRequirementItemsTpls = new List<string>();
|
||||
var usedItemIndexes = new HashSet<int>();
|
||||
@@ -674,32 +675,18 @@ public class RepeatableQuestGenerator(
|
||||
return null;
|
||||
}
|
||||
|
||||
// Store index of item we've already chosen for later checking
|
||||
usedItemIndexes.Add(chosenItemIndex);
|
||||
|
||||
var itemSelected = itemSelection[chosenItemIndex];
|
||||
var itemUnitPrice = _itemHelper.GetItemPrice(itemSelected.Id).Value;
|
||||
var tplChosen = itemSelection[chosenItemIndex];
|
||||
var itemPrice = _itemHelper.GetItemPrice(tplChosen).Value;
|
||||
var minValue = completionConfig.MinimumRequestedAmount.Value;
|
||||
var maxValue = completionConfig.MaximumRequestedAmount.Value;
|
||||
if (_itemHelper.IsOfBaseclass(itemSelected.Id, BaseClasses.AMMO))
|
||||
{
|
||||
// Prevent multiple ammo requirements from being picked
|
||||
if (isAmmo > 0 && isAmmo < _maxRandomNumberAttempts)
|
||||
{
|
||||
isAmmo++;
|
||||
i--;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
isAmmo++;
|
||||
minValue = completionConfig.MinimumRequestedBulletAmount.Value;
|
||||
maxValue = completionConfig.MaximumRequestedBulletAmount.Value;
|
||||
}
|
||||
|
||||
var value = minValue;
|
||||
|
||||
// Get the value range within budget
|
||||
var x = (int) Math.Floor(roublesBudget / itemUnitPrice);
|
||||
var x = (int) Math.Floor(roublesBudget / itemPrice);
|
||||
maxValue = Math.Min(maxValue, x);
|
||||
if (maxValue > minValue)
|
||||
// If it doesn't blow the budget we have for the request, draw a random amount of the selected
|
||||
@@ -708,19 +695,21 @@ public class RepeatableQuestGenerator(
|
||||
value = _randomUtil.RandInt(minValue, maxValue + 1);
|
||||
}
|
||||
|
||||
roublesBudget -= value * itemUnitPrice;
|
||||
roublesBudget -= value * itemPrice;
|
||||
|
||||
// Push a CompletionCondition with the item and the amount of the item
|
||||
chosenRequirementItemsTpls.Add(itemSelected.Id);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCompletionAvailableForFinish(itemSelected.Id, value, repeatableConfig.QuestConfig.Completion));
|
||||
// Push a CompletionCondition with the item and the amount of the item into quest
|
||||
chosenRequirementItemsTpls.Add(tplChosen);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCompletionAvailableForFinish(tplChosen, value, repeatableConfig.QuestConfig.Completion));
|
||||
|
||||
// Is there budget left for more items
|
||||
if (roublesBudget > 0)
|
||||
{
|
||||
// Reduce the list possible items to fulfill the new budget constraint
|
||||
itemSelection = itemSelection.Where(dbItem => _itemHelper.GetItemPrice(dbItem.Id) < roublesBudget)
|
||||
// Reduce item pool to fit budget
|
||||
itemSelection = itemSelection.Where(tpl => _itemHelper.GetItemPrice(tpl) < roublesBudget)
|
||||
.ToList();
|
||||
if (!itemSelection.Any())
|
||||
{
|
||||
// Nothing fits new budget, exit
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -742,6 +731,44 @@ public class RepeatableQuestGenerator(
|
||||
return quest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a pool of item tpls the player should reasonably be able to retrieve
|
||||
/// </summary>
|
||||
/// <param name="completionConfig">Completion quest type config</param>
|
||||
/// <param name="itemTplBlacklist">Item tpls to not add to pool</param>
|
||||
/// <returns>Set of item tpls</returns>
|
||||
protected HashSet<string> GetItemsToRetrievePool(Completion completionConfig, HashSet<string> itemTplBlacklist)
|
||||
{
|
||||
// Get seasonal items that should not be added to pool as seasonal event is not active
|
||||
var seasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
|
||||
// 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
|
||||
return _databaseService.GetItems()
|
||||
.Values.Where(itemTemplate =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate.Parent == string.Empty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seasonalItems.Contains(itemTemplate.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid reward items share same logic as items to retrieve
|
||||
return _repeatableQuestRewardGenerator.IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
itemTplBlacklist,
|
||||
completionConfig.RequiredItemTypeBlacklist
|
||||
);
|
||||
}
|
||||
).Select(item => item.Id)
|
||||
.ToHashSet();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repeatable quest, besides some more or less static components, exists of reward and condition (see
|
||||
/// assets/database/templates/repeatableQuests.json)
|
||||
|
||||
@@ -693,7 +693,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
// Get an array of seasonal items that should not be shown right now as seasonal event is not active
|
||||
var seasonalItems = _seasonalEventService.GetInactiveSeasonalEventItems();
|
||||
|
||||
// Check for specific baseclasses 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
|
||||
// those are not in the game yet (e.g. AGS grenade launcher)
|
||||
return _databaseService.GetItems()
|
||||
@@ -715,7 +715,8 @@ public class RepeatableQuestRewardGenerator(
|
||||
|
||||
return IsValidRewardItem(
|
||||
itemTemplate.Id,
|
||||
repeatableQuestConfig,
|
||||
repeatableQuestConfig.RewardBlacklist,
|
||||
repeatableQuestConfig.RewardBaseTypeBlacklist,
|
||||
traderWhitelist?.RewardBaseWhitelist
|
||||
);
|
||||
}
|
||||
@@ -728,10 +729,13 @@ public class RepeatableQuestRewardGenerator(
|
||||
/// or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
|
||||
/// </summary>
|
||||
/// <param name="tpl"> Template id of item to check</param>
|
||||
/// <param name="repeatableQuestConfig"> Config </param>
|
||||
/// <param name="itemTplBlacklist"> Specific item tpls to ignore </param>
|
||||
/// <param name="itemTypeBlacklist"> Specific item base types to ignore </param>
|
||||
/// <param name="itemBaseWhitelist"> Default null, specific trader item base classes</param>
|
||||
/// <returns> True if item is valid reward </returns>
|
||||
protected bool IsValidRewardItem(string tpl, RepeatableQuestConfig repeatableQuestConfig,
|
||||
public bool IsValidRewardItem(string tpl,
|
||||
HashSet<string> itemTplBlacklist,
|
||||
HashSet<string> itemTypeBlacklist,
|
||||
List<string>? itemBaseWhitelist = null)
|
||||
{
|
||||
// Return early if not valid item to give as reward
|
||||
@@ -744,7 +748,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
if (
|
||||
_itemFilterService.IsItemBlacklisted(tpl) ||
|
||||
_itemFilterService.IsItemRewardBlacklisted(tpl) ||
|
||||
repeatableQuestConfig.RewardBlacklist.Contains(tpl) ||
|
||||
itemTplBlacklist.Contains(tpl) ||
|
||||
_itemFilterService.IsItemBlacklisted(tpl)
|
||||
)
|
||||
{
|
||||
@@ -752,7 +756,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
}
|
||||
|
||||
// Item has blacklisted base types
|
||||
if (_itemHelper.IsOfBaseclasses(tpl, repeatableQuestConfig.RewardBaseTypeBlacklist))
|
||||
if (_itemHelper.IsOfBaseclasses(tpl, itemTypeBlacklist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -600,6 +600,16 @@ public record Completion : BaseQuestConfig
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blacklisted item types to not collect
|
||||
/// </summary>
|
||||
[JsonPropertyName("requiredItemTypeBlacklist")]
|
||||
public HashSet<string>? RequiredItemTypeBlacklist
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public record Pickup : BaseQuestConfig
|
||||
|
||||
Reference in New Issue
Block a user