Fix daily reward budget not using preset prices for some items (!256)
- Switch item cost calculation to use a new helper that checks for a preset first, to more accurately keep track of the budget - Subtract weapon price from reward budget - Decrement reward counter for skipped ammo - Add a new helper for filtering the reward pool by budget, so it's consistent in the two places it's done - Add a metric boatload of debug output for daily generation Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/256 Co-authored-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com> Co-committed-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com>
This commit is contained in:
parent
561c5a7ffc
commit
15257ea263
@ -116,7 +116,10 @@ export class RepeatableQuestRewardGenerator
|
|||||||
|
|
||||||
// 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 this.itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink)
|
||||||
let roublesBudget = rewardRoubles;
|
let roublesBudget = rewardRoubles;
|
||||||
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
|
const rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
|
||||||
|
this.logger.debug(
|
||||||
|
`Generating daily quest for ${traderId} with budget ${roublesBudget} for ${rewardNumItems} items`,
|
||||||
|
);
|
||||||
|
|
||||||
const rewards: IQuestRewards = { Started: [], Success: [], Fail: [] };
|
const rewards: IQuestRewards = { Started: [], Success: [], Fail: [] };
|
||||||
|
|
||||||
@ -152,6 +155,8 @@ export class RepeatableQuestRewardGenerator
|
|||||||
const presetPrice = this.itemHelper.getItemAndChildrenPrice(tpls);
|
const presetPrice = this.itemHelper.getItemAndChildrenPrice(tpls);
|
||||||
if (presetPrice <= roublesBudget)
|
if (presetPrice <= roublesBudget)
|
||||||
{
|
{
|
||||||
|
this.logger.debug(` Added weapon ${tpls[0]} with price ${presetPrice}`);
|
||||||
|
roublesBudget -= presetPrice;
|
||||||
chosenPreset = this.jsonUtil.clone(randomPreset);
|
chosenPreset = this.jsonUtil.clone(randomPreset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -179,6 +184,7 @@ export class RepeatableQuestRewardGenerator
|
|||||||
// Don't reward ammo that stacks to less than what's defined in config
|
// Don't reward ammo that stacks to less than what's defined in config
|
||||||
if (itemSelected._props.StackMaxSize < repeatableConfig.rewardAmmoStackMinSize)
|
if (itemSelected._props.StackMaxSize < repeatableConfig.rewardAmmoStackMinSize)
|
||||||
{
|
{
|
||||||
|
i--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,18 +205,17 @@ export class RepeatableQuestRewardGenerator
|
|||||||
rewards.Success.push(this.generateRewardItem(itemSelected._id, rewardItemStackCount, rewardIndex));
|
rewards.Success.push(this.generateRewardItem(itemSelected._id, rewardItemStackCount, rewardIndex));
|
||||||
rewardIndex++;
|
rewardIndex++;
|
||||||
|
|
||||||
const itemCost = this.itemHelper.getStaticItemPrice(itemSelected._id);
|
const itemCost = this.presetHelper.getDefaultPresetOrItemPrice(itemSelected._id);
|
||||||
roublesBudget -= rewardItemStackCount * itemCost;
|
roublesBudget -= rewardItemStackCount * itemCost;
|
||||||
|
this.logger.debug(` Added item ${itemSelected._id} with price ${rewardItemStackCount * itemCost}`);
|
||||||
|
|
||||||
// If we still have budget narrow down possible items
|
// If we still have budget narrow down possible items
|
||||||
if (roublesBudget > 0)
|
if (roublesBudget > 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
|
||||||
rewardItemPool = rewardItemPool.filter((x) =>
|
if (!this.filterRewardPoolWithinBudget(rewardItemPool, roublesBudget, 0))
|
||||||
this.itemHelper.getStaticItemPrice(x._id) < roublesBudget
|
|
||||||
);
|
|
||||||
if (rewardItemPool.length === 0)
|
|
||||||
{
|
{
|
||||||
|
this.logger.debug(` Reward pool empty with ${roublesBudget} remaining`);
|
||||||
break; // No reward items left, exit
|
break; // No reward items left, exit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,23 +237,49 @@ export class RepeatableQuestRewardGenerator
|
|||||||
};
|
};
|
||||||
rewards.Success.push(reward);
|
rewards.Success.push(reward);
|
||||||
rewardIndex++;
|
rewardIndex++;
|
||||||
|
|
||||||
|
this.logger.debug(` Adding ${rewardReputation} trader reputation reward`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chance of adding skill reward
|
// Chance of adding skill reward
|
||||||
if (this.randomUtil.getChance100(skillRewardChance * 100))
|
if (this.randomUtil.getChance100(skillRewardChance * 100))
|
||||||
{
|
{
|
||||||
|
const targetSkill = this.randomUtil.getArrayValue(questConfig.possibleSkillRewards);
|
||||||
const reward: IQuestReward = {
|
const reward: IQuestReward = {
|
||||||
target: this.randomUtil.getArrayValue(questConfig.possibleSkillRewards),
|
target: targetSkill,
|
||||||
value: skillPointReward,
|
value: skillPointReward,
|
||||||
type: QuestRewardType.SKILL,
|
type: QuestRewardType.SKILL,
|
||||||
index: rewardIndex,
|
index: rewardIndex,
|
||||||
};
|
};
|
||||||
rewards.Success.push(reward);
|
rewards.Success.push(reward);
|
||||||
|
|
||||||
|
this.logger.debug(` Adding ${skillPointReward} skill points to ${targetSkill}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rewards;
|
return rewards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param rewardItems List of reward items to filter
|
||||||
|
* @param roublesBudget The budget remaining for rewards
|
||||||
|
* @param minPrice The minimum priced item to include
|
||||||
|
* @returns True if any items remain in `rewardItems`, false otherwise
|
||||||
|
*/
|
||||||
|
protected filterRewardPoolWithinBudget(
|
||||||
|
rewardItems: ITemplateItem[],
|
||||||
|
roublesBudget: number,
|
||||||
|
minPrice: number,
|
||||||
|
): boolean
|
||||||
|
{
|
||||||
|
rewardItems.filter((item) =>
|
||||||
|
{
|
||||||
|
const itemPrice = this.presetHelper.getDefaultPresetOrItemPrice(item._id);
|
||||||
|
return itemPrice < roublesBudget && itemPrice > minPrice;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (rewardItems.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a randomised number a reward items stack size should be based on its handbook price
|
* Get a randomised number a reward items stack size should be based on its handbook price
|
||||||
* @param item Reward item to get stack size for
|
* @param item Reward item to get stack size for
|
||||||
@ -256,7 +287,7 @@ export class RepeatableQuestRewardGenerator
|
|||||||
*/
|
*/
|
||||||
protected getRandomisedRewardItemStackSizeByPrice(item: ITemplateItem): number
|
protected getRandomisedRewardItemStackSizeByPrice(item: ITemplateItem): number
|
||||||
{
|
{
|
||||||
const rewardItemPrice = this.itemHelper.getStaticItemPrice(item._id);
|
const rewardItemPrice = this.presetHelper.getDefaultPresetOrItemPrice(item._id);
|
||||||
if (rewardItemPrice < 3000)
|
if (rewardItemPrice < 3000)
|
||||||
{
|
{
|
||||||
return this.randomUtil.getArrayValue([2, 3, 4]);
|
return this.randomUtil.getArrayValue([2, 3, 4]);
|
||||||
@ -278,7 +309,7 @@ export class RepeatableQuestRewardGenerator
|
|||||||
*/
|
*/
|
||||||
protected canIncreaseRewardItemStackSize(item: ITemplateItem, maxRoublePriceToStack: number): boolean
|
protected canIncreaseRewardItemStackSize(item: ITemplateItem, maxRoublePriceToStack: number): boolean
|
||||||
{
|
{
|
||||||
return this.itemHelper.getStaticItemPrice(item._id) < maxRoublePriceToStack
|
return this.presetHelper.getDefaultPresetOrItemPrice(item._id) < maxRoublePriceToStack
|
||||||
&& !this.itemHelper.isOfBaseclasses(item._id, [
|
&& !this.itemHelper.isOfBaseclasses(item._id, [
|
||||||
BaseClasses.WEAPON,
|
BaseClasses.WEAPON,
|
||||||
BaseClasses.ARMORED_EQUIPMENT,
|
BaseClasses.ARMORED_EQUIPMENT,
|
||||||
@ -324,21 +355,8 @@ export class RepeatableQuestRewardGenerator
|
|||||||
const rewardableItemPool = this.getRewardableItems(repeatableConfig, traderId);
|
const rewardableItemPool = this.getRewardableItems(repeatableConfig, traderId);
|
||||||
const minPrice = Math.min(25000, 0.5 * roublesBudget);
|
const minPrice = Math.min(25000, 0.5 * roublesBudget);
|
||||||
|
|
||||||
let rewardableItemPoolWithinBudget = rewardableItemPool.filter((item) =>
|
let rewardableItemPoolWithinBudget = rewardableItemPool.map((x) => x[1]);
|
||||||
{
|
if (!this.filterRewardPoolWithinBudget(rewardableItemPoolWithinBudget, roublesBudget, minPrice))
|
||||||
// Get default preset if it exists
|
|
||||||
const defaultPreset = this.presetHelper.getDefaultPreset(item[0]);
|
|
||||||
|
|
||||||
// Bundle up tpls we want price for
|
|
||||||
const tpls = defaultPreset ? defaultPreset._items.map((item) => item._tpl) : [item[0]];
|
|
||||||
|
|
||||||
// Get price of tpls
|
|
||||||
const itemPrice = this.itemHelper.getItemAndChildrenPrice(tpls);
|
|
||||||
|
|
||||||
return itemPrice < roublesBudget && itemPrice > minPrice;
|
|
||||||
}).map((x) => x[1]);
|
|
||||||
|
|
||||||
if (rewardableItemPoolWithinBudget.length === 0)
|
|
||||||
{
|
{
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
this.localisationService.getText("repeatable-no_reward_item_found_in_price_range", {
|
this.localisationService.getText("repeatable-no_reward_item_found_in_price_range", {
|
||||||
|
@ -160,4 +160,21 @@ export class PresetHelper
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the price of the preset for the given item tpl, or for the tpl itself if no preset exists
|
||||||
|
* @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 getDefaultPresetOrItemPrice(tpl: string): number
|
||||||
|
{
|
||||||
|
// Get default preset if it exists
|
||||||
|
const defaultPreset = this.getDefaultPreset(tpl);
|
||||||
|
|
||||||
|
// Bundle up tpls we want price for
|
||||||
|
const tpls = defaultPreset ? defaultPreset._items.map((item) => item._tpl) : [tpl];
|
||||||
|
|
||||||
|
// Get price of tpls
|
||||||
|
return this.itemHelper.getItemAndChildrenPrice(tpls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user