Repeatable quest improvements
This commit is contained in:
@@ -497,7 +497,7 @@ public class RepeatableQuestGenerator(
|
||||
(double)(_mathUtil.Interp1(pmcLevel, levelsConfig, roublesConfig) * multi));
|
||||
roublesBudget = Math.Max(roublesBudget, 5000d);
|
||||
var itemSelection = possibleItemsToRetrievePool.Where(
|
||||
(x) => _itemHelper.GetItemPrice(x.Key) < roublesBudget).ToList();
|
||||
(x) => _itemHelper.GetItemPrice(x.Id) < roublesBudget).ToList();
|
||||
|
||||
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
|
||||
// [{"minPlayerLevel": 1, "itemIds": ["id1",...]}, {"minPlayerLevel": 15, "itemIds": ["id3",...]}]
|
||||
@@ -511,8 +511,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.Key, v)) ||
|
||||
itemIdsWhitelisted.Contains(x.Key)
|
||||
itemIdsWhitelisted.Any((v) => _itemHelper.IsOfBaseclass(x.Id, v)) ||
|
||||
itemIdsWhitelisted.Contains(x.Id)
|
||||
);
|
||||
}).ToList();
|
||||
// check if items are missing
|
||||
@@ -530,8 +530,8 @@ public class RepeatableQuestGenerator(
|
||||
|
||||
itemSelection = itemSelection.Where((x) => {
|
||||
return (
|
||||
itemIdsBlacklisted.All((v) => !_itemHelper.IsOfBaseclass(x.Key, v)) ||
|
||||
!itemIdsBlacklisted.Contains(x.Key)
|
||||
itemIdsBlacklisted.All((v) => !_itemHelper.IsOfBaseclass(x.Id, v)) ||
|
||||
!itemIdsBlacklisted.Contains(x.Id)
|
||||
);
|
||||
}).ToList();
|
||||
}
|
||||
@@ -572,10 +572,10 @@ public class RepeatableQuestGenerator(
|
||||
usedItemIndexes.Add(chosenItemIndex);
|
||||
|
||||
var itemSelected = itemSelection[chosenItemIndex];
|
||||
var itemUnitPrice = _itemHelper.GetItemPrice(itemSelected.Key).Value;
|
||||
var itemUnitPrice = _itemHelper.GetItemPrice(itemSelected.Id).Value;
|
||||
var minValue = (double)completionConfig.MinimumRequestedAmount.Value;
|
||||
var maxValue = (double)completionConfig.MaximumRequestedAmount.Value;
|
||||
if (_itemHelper.IsOfBaseclass(itemSelected.Key, BaseClasses.AMMO)) {
|
||||
if (_itemHelper.IsOfBaseclass(itemSelected.Id, BaseClasses.AMMO)) {
|
||||
// Prevent multiple ammo requirements from being picked
|
||||
if (isAmmo > 0 && isAmmo < _maxRandomNumberAttempts) {
|
||||
isAmmo++;
|
||||
@@ -600,12 +600,12 @@ public class RepeatableQuestGenerator(
|
||||
roublesBudget -= value * itemUnitPrice;
|
||||
|
||||
// Push a CompletionCondition with the item and the amount of the item
|
||||
chosenRequirementItemsTpls.Add(itemSelected.Key);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCompletionAvailableForFinish(itemSelected.Key, value));
|
||||
chosenRequirementItemsTpls.Add(itemSelected.Id);
|
||||
quest.Conditions.AvailableForFinish.Add(GenerateCompletionAvailableForFinish(itemSelected.Id, value));
|
||||
|
||||
if (roublesBudget > 0) {
|
||||
// Reduce the list possible items to fulfill the new budget constraint
|
||||
itemSelection = itemSelection.Where((dbItem) => _itemHelper.GetItemPrice(dbItem.Key) < roublesBudget).ToList();
|
||||
itemSelection = itemSelection.Where((dbItem) => _itemHelper.GetItemPrice(dbItem.Id) < roublesBudget).ToList();
|
||||
if (!itemSelection.Any()) {
|
||||
break;
|
||||
}
|
||||
@@ -805,7 +805,7 @@ public class RepeatableQuestGenerator(
|
||||
string side,
|
||||
string sessionId)
|
||||
{
|
||||
Quest questData = null;
|
||||
RepeatableQuest questData = null;
|
||||
switch (type)
|
||||
{
|
||||
case "Elimination":
|
||||
@@ -889,6 +889,6 @@ public class RepeatableQuestGenerator(
|
||||
questClone.QuestStatus.Uid = sessionId; // Needs to match user id
|
||||
questClone.QuestStatus.QId = questClone.Id; // Needs to match quest id
|
||||
|
||||
return (RepeatableQuest)questClone;
|
||||
return questClone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using SptCommon.Annotations;
|
||||
using Core.Helpers;
|
||||
using Core.Models.Eft.Common.Tables;
|
||||
using Core.Models.Eft.Player;
|
||||
using Core.Models.Enums;
|
||||
using Core.Models.Spt.Config;
|
||||
using Core.Models.Spt.Repeatable;
|
||||
@@ -9,6 +10,7 @@ using Core.Servers;
|
||||
using Core.Services;
|
||||
using Core.Utils;
|
||||
using Core.Utils.Cloners;
|
||||
using System.Linq;
|
||||
|
||||
namespace Core.Generators;
|
||||
|
||||
@@ -69,7 +71,7 @@ public class RepeatableQuestRewardGenerator(
|
||||
// Get budget to spend on item rewards (copy of raw roubles given)
|
||||
var itemRewardBudget = rewardParams.RewardRoubles;
|
||||
|
||||
// Possible improvement -> draw trader-specific items e.g. with this.itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink)
|
||||
// Possible improvement -> draw trader-specific items e.g. with _itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink)
|
||||
QuestRewards rewards = new() { Started = [], Success = [], Fail = [] };
|
||||
|
||||
// Start reward index to keep track
|
||||
@@ -94,11 +96,11 @@ public class RepeatableQuestRewardGenerator(
|
||||
}
|
||||
|
||||
// Add money reward
|
||||
rewards.Success.Add(GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex));
|
||||
rewards.Success.Add(GetMoneyReward(traderId, rewardParams.RewardRoubles.Value, rewardIndex));
|
||||
rewardIndex++;
|
||||
|
||||
// Add GP coin reward
|
||||
rewards.Success.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex));
|
||||
rewards.Success.Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount.Value, rewardIndex));
|
||||
rewardIndex++;
|
||||
|
||||
// Add preset weapon to reward if checks pass
|
||||
@@ -283,9 +285,38 @@ public class RepeatableQuestRewardGenerator(
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private List<TemplateItem> ChooseRewardItemsWithinBudget(RepeatableQuestConfig repeatableConfig, double? itemRewardBudget, string traderId)
|
||||
private List<TemplateItem> ChooseRewardItemsWithinBudget(RepeatableQuestConfig repeatableConfig, double? roublesBudget, string traderId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// First filter for type and baseclass to avoid lookup in handbook for non-available items
|
||||
var rewardableItemPool = GetRewardableItems(repeatableConfig, traderId);
|
||||
var minPrice = Math.Min(25000, 0.5 * roublesBudget.Value);
|
||||
|
||||
var rewardableItemPoolWithinBudget = FilterRewardPoolWithinBudget(
|
||||
rewardableItemPool,
|
||||
roublesBudget.Value,
|
||||
minPrice);
|
||||
|
||||
if (rewardableItemPoolWithinBudget.Count == 0)
|
||||
{
|
||||
_logger.Warning(_localisationService.GetText("repeatable-no_reward_item_found_in_price_range", new {
|
||||
minPrice = minPrice,
|
||||
roublesBudget = roublesBudget }));
|
||||
|
||||
// In case we don't find any items in the price range
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool
|
||||
.Where((x) => _itemHelper.GetItemPrice(x.Id) < roublesBudget)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return rewardableItemPoolWithinBudget;
|
||||
}
|
||||
|
||||
private List<TemplateItem> FilterRewardPoolWithinBudget(List<TemplateItem> rewardItems, double roublesBudget, double minPrice)
|
||||
{
|
||||
return rewardItems.Where((item) => {
|
||||
var itemPrice = _presetHelper.GetDefaultPresetOrItemPrice(item.Id);
|
||||
return itemPrice < roublesBudget && itemPrice > minPrice;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private KeyValuePair<Reward, double>? GetRandomWeaponPresetWithinBudget(double? itemRewardBudget, double rewardIndex)
|
||||
@@ -293,19 +324,109 @@ public class RepeatableQuestRewardGenerator(
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Reward GenerateItemReward(string d235b4d86f7742e017bc88a, object gpCoinRewardCount, double rewardIndex)
|
||||
private Reward GenerateItemReward(string tpl, double count, int index, bool foundInRaid = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var id = _hashUtil.Generate();
|
||||
var questRewardItem = new Reward{
|
||||
Id = _hashUtil.Generate(),
|
||||
Unknown = false,
|
||||
GameMode = [],
|
||||
AvailableInGameEditions = [],
|
||||
Index = index,
|
||||
Target = id,
|
||||
Value = count,
|
||||
IsEncoded = false,
|
||||
FindInRaid = foundInRaid,
|
||||
Type = RewardType.Item,
|
||||
Items = [],
|
||||
};
|
||||
|
||||
var rootItem = new Item { Id = id, Template = tpl, Upd = new Upd { StackObjectsCount = count, SpawnedInSession = foundInRaid }
|
||||
};
|
||||
questRewardItem.Items = [rootItem];
|
||||
|
||||
return questRewardItem;
|
||||
}
|
||||
|
||||
private Reward GetMoneyReward(string traderId, object rewardRoubles, double rewardIndex)
|
||||
private Reward GetMoneyReward(string traderId, double rewardRoubles, int rewardIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Determine currency based on trader
|
||||
// PK and Fence use Euros, everyone else is Roubles
|
||||
var currency = traderId is Traders.PEACEKEEPER or Traders.FENCE ? Money.EUROS : Money.ROUBLES;
|
||||
|
||||
// Convert reward amount to Euros if necessary
|
||||
var rewardAmountToGivePlayer =
|
||||
currency == Money.EUROS ? _handbookHelper.FromRUB(rewardRoubles, Money.EUROS) : rewardRoubles;
|
||||
|
||||
// Get chosen currency + amount and return
|
||||
return GenerateItemReward(currency, rewardAmountToGivePlayer, rewardIndex, false);
|
||||
}
|
||||
|
||||
|
||||
public Dictionary<string, List<TemplateItem>> GetRewardableItems(RepeatableQuestConfig repeatableConfig, string traderId)
|
||||
public List<TemplateItem> GetRewardableItems(RepeatableQuestConfig repeatableQuestConfig, string traderId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// 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
|
||||
// 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().Values.Where((itemTemplate => {
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate.Parent == "")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seasonalItems.Contains(itemTemplate.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var traderWhitelist = repeatableQuestConfig.TraderWhitelist.FirstOrDefault(
|
||||
(trader) => trader.TraderId == traderId);
|
||||
|
||||
return IsValidRewardItem(itemTemplate.Id, repeatableQuestConfig, traderWhitelist?.RewardBaseWhitelist);
|
||||
})).ToList();
|
||||
}
|
||||
|
||||
private bool IsValidRewardItem(string tpl, RepeatableQuestConfig repeatableQuestConfig, List<string>? itemBaseWhitelist = null)
|
||||
{
|
||||
// Return early if not valid item to give as reward
|
||||
if (!_itemHelper.isValidItem(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check item is not blacklisted
|
||||
if (
|
||||
_itemFilterService.IsItemBlacklisted(tpl) ||
|
||||
_itemFilterService.IsItemRewardBlacklisted(tpl) ||
|
||||
repeatableQuestConfig.RewardBlacklist.Contains(tpl) ||
|
||||
_itemFilterService.IsItemBlacklisted(tpl)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Item has blacklisted base types
|
||||
if (_itemHelper.IsOfBaseclasses(tpl, repeatableQuestConfig.RewardBaseTypeBlacklist ))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip boss items
|
||||
if (_itemFilterService.IsBossItem(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trader has specific item base types they can give as rewards to player
|
||||
if (itemBaseWhitelist is not null && !_itemHelper.IsOfBaseclasses(tpl, itemBaseWhitelist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using SptCommon.Annotations;
|
||||
using SptCommon.Annotations;
|
||||
using Core.Models.Eft.Common;
|
||||
using Core.Models.Enums;
|
||||
using Core.Services;
|
||||
@@ -171,7 +171,7 @@ public class PresetHelper(
|
||||
* @param tpl The item template to get the price of
|
||||
* @returns The price of the given item preset, or base item if no preset exists
|
||||
*/
|
||||
public decimal GetDefaultPresetOrItemPrice(string tpl)
|
||||
public double GetDefaultPresetOrItemPrice(string tpl)
|
||||
{
|
||||
// Get default preset if it exists
|
||||
var defaultPreset = GetDefaultPreset(tpl);
|
||||
|
||||
@@ -71,16 +71,16 @@ public record RepeatableQuestStatus
|
||||
public record RepeatableTemplates
|
||||
{
|
||||
[JsonPropertyName("Elimination")]
|
||||
public Quest? Elimination { get; set; }
|
||||
public RepeatableQuest? Elimination { get; set; }
|
||||
|
||||
[JsonPropertyName("Completion")]
|
||||
public Quest? Completion { get; set; }
|
||||
public RepeatableQuest? Completion { get; set; }
|
||||
|
||||
[JsonPropertyName("Exploration")]
|
||||
public Quest? Exploration { get; set; }
|
||||
public RepeatableQuest? Exploration { get; set; }
|
||||
|
||||
[JsonPropertyName("Pickup")]
|
||||
public Quest? Pickup { get; set; }
|
||||
public RepeatableQuest? Pickup { get; set; }
|
||||
}
|
||||
|
||||
public record PmcDataRepeatableQuest
|
||||
|
||||
@@ -104,6 +104,11 @@ public class ItemFilterService(
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provided template id is blacklisted in config/item.json/lootableItemBlacklist
|
||||
* @param tpl template id
|
||||
* @returns true if blacklisted
|
||||
*/
|
||||
public bool IsLootableItemBlacklisted(string itemKey)
|
||||
{
|
||||
if (_lootableItemBlacklistCache is null)
|
||||
@@ -140,4 +145,24 @@ public class ItemFilterService(
|
||||
_itemBlacklistCache.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the provided template id is boss item in config/item.json
|
||||
* @param tpl template id
|
||||
* @returns true if boss item
|
||||
*/
|
||||
public bool IsBossItem(string tpl)
|
||||
{
|
||||
return _itemConfig.BossItems.Contains(tpl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if item is blacklisted from being a reward for player
|
||||
* @param tpl item tpl to check is on blacklist
|
||||
* @returns True when blacklisted
|
||||
*/
|
||||
public bool IsItemRewardBlacklisted(string tpl)
|
||||
{
|
||||
return _itemConfig.RewardItemBlacklist.Contains(tpl);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user