diff --git a/project/src/generators/RepeatableQuestGenerator.ts b/project/src/generators/RepeatableQuestGenerator.ts index a5d991ba..74cb199b 100644 --- a/project/src/generators/RepeatableQuestGenerator.ts +++ b/project/src/generators/RepeatableQuestGenerator.ts @@ -28,6 +28,7 @@ import { inject, injectable } from "tsyringe"; @injectable() export class RepeatableQuestGenerator { protected questConfig: IQuestConfig; + protected maxRandomNumberAttempts = 6; constructor( @inject("PrimaryLogger") protected logger: ILogger, @@ -516,24 +517,44 @@ export class RepeatableQuestGenerator { // Draw items to ask player to retrieve let isAmmo = 0; - const randomNumbersUsed = []; + + // Store the indexes of items we are asking player to provide + const usedItemIndexes = new Set(); for (let i = 0; i < distinctItemsToRetrieveCount; i++) { - let randomNumber = this.randomUtil.randInt(itemSelection.length); - while (randomNumbersUsed.includes(randomNumber) && randomNumbersUsed.length !== itemSelection.length) { - randomNumber = this.randomUtil.randInt(itemSelection.length); + let chosenItemIndex = this.randomUtil.randInt(itemSelection.length); + let found = false; + + for (let i = 0; i < this.maxRandomNumberAttempts; i++) { + if (usedItemIndexes.has(chosenItemIndex)) { + chosenItemIndex = this.randomUtil.randInt(itemSelection.length); + } else { + found = true; + break; + } } - randomNumbersUsed.push(randomNumber); + if (!found) { + this.logger.error( + this.localisationService.getText("repeatable-no_reward_item_found_in_price_range", { + minPrice: 0, + roublesBudget: roublesBudget, + }), + ); - const itemSelected = itemSelection[randomNumber]; + return undefined; + } + usedItemIndexes.add(chosenItemIndex); + + const itemSelected = itemSelection[chosenItemIndex]; const itemUnitPrice = this.itemHelper.getItemPrice(itemSelected[0]); let minValue = completionConfig.minRequestedAmount; let maxValue = completionConfig.maxRequestedAmount; if (this.itemHelper.isOfBaseclass(itemSelected[0], BaseClasses.AMMO)) { - // Prevent multiple ammo requirements from being picked, stop after 6 attempts - if (isAmmo > 0 && isAmmo < 6) { + // Prevent multiple ammo requirements from being picked + if (isAmmo > 0 && isAmmo < this.maxRandomNumberAttempts) { isAmmo++; i--; + continue; } isAmmo++; @@ -542,21 +563,23 @@ export class RepeatableQuestGenerator { } let value = minValue; - // get the value range within budget + // Get the value range within budget maxValue = Math.min(maxValue, Math.floor(roublesBudget / itemUnitPrice)); if (maxValue > minValue) { - // if it doesn't blow the budget we have for the request, draw a random amount of the selected - // item type to be requested + // If it doesn't blow the budget we have for the request, draw a random amount of the selected + // Item type to be requested value = this.randomUtil.randInt(minValue, maxValue + 1); } roublesBudget -= value * itemUnitPrice; - // push a CompletionCondition with the item and the amount of the item + // Push a CompletionCondition with the item and the amount of the item quest.conditions.AvailableForFinish.push(this.generateCompletionAvailableForFinish(itemSelected[0], value)); if (roublesBudget > 0) { - // reduce the list possible items to fulfill the new budget constraint - itemSelection = itemSelection.filter((x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget); + // Reduce the list possible items to fulfill the new budget constraint + itemSelection = itemSelection.filter( + (dbItem) => this.itemHelper.getItemPrice(dbItem[0]) < roublesBudget, + ); if (itemSelection.length === 0) { break; }