Refactor: Update various functions with changes from code review

This commit is contained in:
Dev 2023-03-21 14:19:49 +00:00
parent 927273d71d
commit 1fc35881d8
7 changed files with 141 additions and 112 deletions

View File

@ -166,12 +166,12 @@ export class QuestController
/**
* Is the quest for the opposite side the player is on
* @param side player side (usec/bear)
* @param questId questId to check
* @param playerSide Player side (usec/bear)
* @param questId QuestId to check
*/
protected questIsForOtherSide(side: string, questId: string): boolean
protected questIsForOtherSide(playerSide: string, questId: string): boolean
{
const isUsec = side.toLowerCase() === "usec";
const isUsec = playerSide.toLowerCase() === "usec";
if (isUsec && this.questConfig.bearOnlyQuests.includes(questId))
{
// player is usec and quest is bear only, skip
@ -295,18 +295,17 @@ export class QuestController
*/
protected getRepeatableQuestFromProfile(pmcData: IPmcData, acceptedQuest: IAcceptQuestRequestData): IRepeatableQuest
{
let result: IRepeatableQuest;
for (const repeatable of pmcData.RepeatableQuests)
for (const repeatableQuest of pmcData.RepeatableQuests)
{
result = repeatable.activeQuests.find(x => x._id === acceptedQuest.qid);
const result = repeatableQuest.activeQuests.find(x => x._id === acceptedQuest.qid);
if (result)
{
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatable.name}`);
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
break;
}
}
return result;
return undefined;
}
/**

View File

@ -324,11 +324,26 @@ export class BotEquipmentModGenerator
*/
protected setScopeSpawnChancesToFull(modSpawnChances: ModsChances): void
{
modSpawnChances["mod_scope"] = 100;
modSpawnChances["mod_scope_000"] = 100;
modSpawnChances["mod_scope_001"] = 100;
modSpawnChances["mod_scope_002"] = 100;
modSpawnChances["mod_scope_003"] = 100;
if (!modSpawnChances)
{
this.logger.warning("Unable to adjust scope spawn chances as spawn chance object is empty");
return;
}
const fullSpawnChancePercent = 100;
const scopeMods = [
"mod_scope",
"mod_scope_000",
"mod_scope_001",
"mod_scope_002",
"mod_scope_003"
];
for (const modName of scopeMods)
{
modSpawnChances[modName] = fullSpawnChancePercent;
}
}
protected sortModKeys(unsortedKeys: string[]): string[]
@ -423,7 +438,7 @@ export class BotEquipmentModGenerator
}
/**
* randomly choose if a mod should be spawned, 100% for required mods OR mod is ammo slot
* Randomly choose if a mod should be spawned, 100% for required mods OR mod is ammo slot
* never return true for an item that has 0% spawn chance
* @param itemSlot slot the item sits in
* @param modSlot slot the mod sits in

View File

@ -257,23 +257,19 @@ export class BotInventoryGenerator
*/
protected getFilteredDynamicModsForItem(itemTpl: string, equipmentBlacklist: EquipmentFilterDetails[]): Record<string, string[]>
{
const results = this.botEquipmentModPoolService.getModsForGearSlot(itemTpl);
for (const modSlot in results)
const modPool = this.botEquipmentModPoolService.getModsForGearSlot(itemTpl);
for (const modSlot of Object.keys(modPool))
{
const blacklistedMods = equipmentBlacklist[0].equipment[modSlot];
if (!blacklistedMods)
{
continue;
}
const filteredMods = results[modSlot].filter(x => !blacklistedMods.includes(x));
const blacklistedMods = equipmentBlacklist[0].equipment[modSlot] || [];
const filteredMods = modPool[modSlot].filter(x => !blacklistedMods.includes(x));
if (filteredMods.length > 0)
{
results[modSlot] = filteredMods;
modPool[modSlot] = filteredMods;
}
}
return results;
return modPool;
}
/**

View File

@ -41,64 +41,64 @@ export class BotGeneratorHelper
*/
public generateExtraPropertiesForItem(itemTemplate: ITemplateItem, botRole: string = null): { upd?: Upd }
{
const properties: Upd = {};
const itemProperties: Upd = {};
if (itemTemplate._props.MaxDurability)
{
if (itemTemplate._props.weapClass) // Is weapon
{
properties.Repairable = this.generateWeaponRepairableProperties(itemTemplate, botRole);
itemProperties.Repairable = this.generateWeaponRepairableProperties(itemTemplate, botRole);
}
else if (itemTemplate._props.armorClass) // Is armor
{
properties.Repairable = this.generateArmorRepairableProperties(itemTemplate, botRole);
itemProperties.Repairable = this.generateArmorRepairableProperties(itemTemplate, botRole);
}
}
if (itemTemplate._props.HasHinge)
{
properties.Togglable = { On: true };
itemProperties.Togglable = { On: true };
}
if (itemTemplate._props.Foldable)
{
properties.Foldable = { Folded: false };
itemProperties.Foldable = { Folded: false };
}
if (itemTemplate._props.weapFireType?.length)
{
if (itemTemplate._props.weapFireType.includes("fullauto"))
{
properties.FireMode = { FireMode: "fullauto" };
itemProperties.FireMode = { FireMode: "fullauto" };
}
else
{
properties.FireMode = { FireMode: this.randomUtil.getArrayValue(itemTemplate._props.weapFireType) };
itemProperties.FireMode = { FireMode: this.randomUtil.getArrayValue(itemTemplate._props.weapFireType) };
}
}
if (itemTemplate._props.MaxHpResource)
{
properties.MedKit = { HpResource: itemTemplate._props.MaxHpResource };
itemProperties.MedKit = { HpResource: itemTemplate._props.MaxHpResource };
}
if (itemTemplate._props.MaxResource && itemTemplate._props.foodUseTime)
{
properties.FoodDrink = { HpPercent: itemTemplate._props.MaxResource };
itemProperties.FoodDrink = { HpPercent: itemTemplate._props.MaxResource };
}
if ([BaseClasses.FLASHLIGHT, BaseClasses.TACTICAL_COMBO].includes(<BaseClasses>itemTemplate._parent))
{
// Get chance from botconfig for bot type, use 50% if no value found
const lightLaserActiveChance = this.getBotEquipmentSettingFromConfig(botRole, "lightLaserIsActiveChancePercent", 50);
properties.Light = { IsActive: (this.randomUtil.getChance100(lightLaserActiveChance)), SelectedMode: 0 };
itemProperties.Light = { IsActive: (this.randomUtil.getChance100(lightLaserActiveChance)), SelectedMode: 0 };
}
if (itemTemplate._parent === BaseClasses.NIGHTVISION)
{
// Get chance from botconfig for bot type, use 50% if no value found
const nvgActiveChance = this.getBotEquipmentSettingFromConfig(botRole, "nvgIsActiveChancePercent", 50);
properties.Togglable = { On: (this.randomUtil.getChance100(nvgActiveChance)) };
itemProperties.Togglable = { On: (this.randomUtil.getChance100(nvgActiveChance)) };
}
// Togglable face shield
@ -106,11 +106,11 @@ export class BotGeneratorHelper
{
// Get chance from botconfig for bot type, use 75% if no value found
const faceShieldActiveChance = this.getBotEquipmentSettingFromConfig(botRole, "faceShieldIsActiveChancePercent", 75);
properties.Togglable = { On: (this.randomUtil.getChance100(faceShieldActiveChance)) };
itemProperties.Togglable = { On: (this.randomUtil.getChance100(faceShieldActiveChance)) };
}
return Object.keys(properties).length
? { upd: properties }
return Object.keys(itemProperties).length
? { upd: itemProperties }
: {};
}

View File

@ -48,6 +48,7 @@ export class BotWeaponGeneratorHelper
}
else if (parentItem._id === BaseClasses.UBGL)
{
// underbarrel launchers can only have 1 chambered grenade
chamberBulletCount = 1;
}
else

View File

@ -477,35 +477,38 @@ class ItemHelper
}
/**
* split item stack if it exceeds StackMaxSize
* split item stack if it exceeds its StackMaxSize property
* @param itemToSplit item being split into smaller stacks
* @returns Array of split items
*/
public splitStack(item: Item): Item[]
public splitStack(itemToSplit: Item): Item[]
{
if (!(("upd" in item) && ("StackObjectsCount" in item.upd)))
if (!(itemToSplit?.upd?.StackObjectsCount != null))
{
return [item];
return [itemToSplit];
}
const maxStack = this.databaseServer.getTables().templates.items[item._tpl]._props.StackMaxSize;
let count = item.upd.StackObjectsCount;
const maxStackSize = this.databaseServer.getTables().templates.items[itemToSplit._tpl]._props.StackMaxSize;
let remainingCount = itemToSplit.upd.StackObjectsCount;
const stacks: Item[] = [];
// If the current count is already equal or less than the max
// then just return the item as is.
if (count <= maxStack)
if (remainingCount <= maxStackSize)
{
stacks.push(this.jsonUtil.clone(item));
stacks.push(this.jsonUtil.clone(itemToSplit));
return stacks;
}
while (count)
while (remainingCount)
{
const amount = Math.min(count, maxStack);
const newStack = this.jsonUtil.clone(item);
const amount = Math.min(remainingCount, maxStackSize);
const newStack = this.jsonUtil.clone(itemToSplit);
newStack._id = this.hashUtil.generate();
newStack.upd.StackObjectsCount = amount;
count -= amount;
remainingCount -= amount;
stacks.push(newStack);
}
@ -514,19 +517,19 @@ class ItemHelper
/**
* Find Barter items in the inventory
* @param {string} by
* @param {string} by tpl or id
* @param {Object} pmcData
* @param {string} barterItemId
* @returns Array of Item objects
*/
public findBarterItems(by: string, pmcData: IPmcData, barterItemId: string): Item[]
public findBarterItems(by: "tpl" | "id", pmcData: IPmcData, barterItemId: string): Item[]
{
// find required items to take after buying (handles multiple items)
const barterIDs = typeof barterItemId === "string"
? [barterItemId]
: barterItemId;
let itemsArray: Item[] = [];
let barterItems: Item[] = [];
for (const barterID of barterIDs)
{
const filterResult = pmcData.Inventory.items.filter(item =>
@ -536,10 +539,15 @@ class ItemHelper
: (item._id === barterID);
});
itemsArray = Object.assign(itemsArray, filterResult);
barterItems = Object.assign(barterItems, filterResult);
}
return itemsArray;
if (barterItems.length === 0)
{
this.logger.warning(`No items found for barter Id: ${barterIDs}`);
}
return barterItems;
}
/**
@ -800,6 +808,7 @@ class ItemHelper
// Add new stack-size-correct items to ammo box
let currentStoredCartridgeCount = 0;
// Location in ammoBox cartridges will be placed
let location = 0;
while (currentStoredCartridgeCount < ammoBoxMaxCartridgeCount)
{
@ -808,7 +817,7 @@ class ItemHelper
? ammoBoxMaxCartridgeCount
: cartridgeMaxStackSize;
// Add cartridge item object into items array
// Add cartridge item into items array
ammoBox.push(this.createCartridges(ammoBox[0]._id, cartridgeTpl, cartridgeCountToAdd, location));
currentStoredCartridgeCount += cartridgeCountToAdd;
@ -832,7 +841,7 @@ class ItemHelper
minSizePercent = 0.25
): void
{
// no caliber defined, choose one
// no caliber defined, choose one at random
if (!caliber)
{
caliber = this.getRandomValidCaliber(magTemplate);

View File

@ -60,20 +60,16 @@ export class QuestHelper
/**
* Get status of a quest in player profile by its id
* @param pmcData Profile to search
* @param questID Quest id to look up
* @param questId Quest id to look up
* @returns QuestStatus enum
*/
public getQuestStatus(pmcData: IPmcData, questID: string): QuestStatus
public getQuestStatus(pmcData: IPmcData, questId: string): QuestStatus
{
for (const quest of pmcData.Quests)
{
if (quest.qid === questID)
{
return quest.status;
}
}
const quest = pmcData.Quests.find(q => q.qid === questId);
return QuestStatus.Locked;
return quest
? quest.status
: QuestStatus.Locked;
}
/**
@ -191,6 +187,11 @@ export class QuestHelper
}
}
/**
* take reward item from quest and set FiR status + fix stack sizes + fix mod Ids
* @param reward Reward item to fix
* @returns Fixed rewards
*/
protected processReward(reward: Reward): Reward[]
{
let rewardItems: Reward[] = [];
@ -252,22 +253,16 @@ export class QuestHelper
/**
* Gets a flat list of reward items for the given quest at a specific state (e.g. Fail/Success)
* @param quest quest to get rewards for
* @param state Quest status that holds the items (Started, Success, Fail)
* @param status Quest status that holds the items (Started, Success, Fail)
* @returns array of items with the correct maxStack
*/
public getQuestRewardItems(quest: IQuest, state: QuestStatus): Reward[]
public getQuestRewardItems(quest: IQuest, status: QuestStatus): Reward[]
{
let questRewards = [];
// Iterate over all quest rewards
for (const reward of quest.rewards[QuestStatus[state]]) // get string version of QuestStatus
{
// Gather up item rewards
if ("Item" === reward.type)
{
questRewards = questRewards.concat(this.processReward(reward));
}
}
// Iterate over all rewards with the desired status, flatten out items that have a type of Item
const questRewards = quest.rewards[QuestStatus[status]]
.flatMap((reward: Reward) => reward.type === "Item"
? this.processReward(reward)
: []);
return questRewards;
}
@ -319,47 +314,52 @@ export class QuestHelper
}
/**
* TODO: what is going on here
* @param acceptedQuestId Quest to add to profile
* Get quests that can be shown to player after starting a quest
* @param startedQuestId Quest started by player
* @param sessionID Session id
* @returns Array of quests in profile + quest passed in as param
* @returns Quests accessible to player incuding newly unlocked quests now quest (startedQuestId) was started
*/
public acceptedUnlocked(acceptedQuestId: string, sessionID: string): IQuest[]
public acceptedUnlocked(startedQuestId: string, sessionID: string): IQuest[]
{
const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID);
// Get quest acceptance data from profile
const profileQuest = profile.Quests.find(x => x.qid === startedQuestId);
const quests = this.getQuestsFromDb().filter((q) =>
// Get quests that
const eligibleQuests = this.getQuestsFromDb().filter((quest) =>
{
const acceptedQuestCondition = q.conditions.AvailableForStart.find(
c =>
// Quest is accessible to player when the accepted quest passed into param is started
// e.g. Quest A passed in, quest B is looped over and has requirement of A to be started, include it
const acceptedQuestCondition = quest.conditions.AvailableForStart.find(x =>
{
return c._parent === "Quest"
&& c._props.target === acceptedQuestId
&& c._props.status[0] === QuestStatus.Started;
return x._parent === "Quest"
&& x._props.target === startedQuestId
&& x._props.status[0] === QuestStatus.Started;
});
// Not found, skip quest
if (!acceptedQuestCondition)
{
return false;
}
const profileQuest = profile.Quests.find(x => x.qid === acceptedQuestId);
return profileQuest && (profileQuest.status === QuestStatus.Started || profileQuest.status === QuestStatus.AvailableForFinish);
// Include if quest found in profile and is started or ready to hand in
return profileQuest && ([QuestStatus.Started, QuestStatus.AvailableForFinish].includes(profileQuest.status));
});
return this.getQuestsWithOnlyLevelRequirementStartCondition(quests);
return this.getQuestsWithOnlyLevelRequirementStartCondition(eligibleQuests);
}
/**
* TODO: what is going on here
* @param failedQuestId
* @param sessionID Session id
* Get quests that can be shown to player after failing a quest
* @param failedQuestId Id of the quest failed by player
* @param sessionId Session id
* @returns
*/
public failedUnlocked(failedQuestId: string, sessionID: string): IQuest[]
public failedUnlocked(failedQuestId: string, sessionId: string): IQuest[]
{
const profile = this.profileHelper.getPmcProfile(sessionID);
const profile = this.profileHelper.getPmcProfile(sessionId);
const profileQuest = profile.Quests.find(x => x.qid === failedQuestId);
const quests = this.getQuestsFromDb().filter((q) =>
{
@ -376,8 +376,6 @@ export class QuestHelper
return false;
}
const profileQuest = profile.Quests.find(x => x.qid === failedQuestId);
return profileQuest && (profileQuest.status === QuestStatus.Fail);
});
@ -430,14 +428,7 @@ export class QuestHelper
const item = pmcData.Inventory.items[inventoryItemIndex];
item.upd.StackObjectsCount = newStackSize;
output.profileChanges[sessionID].items.change.push({
"_id": item._id,
"_tpl": item._tpl,
"parentId": item.parentId,
"slotId": item.slotId,
"location": item.location,
"upd": { "StackObjectsCount": item.upd.StackObjectsCount }
});
this.addItemStackSizeChangeIntoEventResponse(output, sessionID, item);
}
else
{
@ -448,6 +439,24 @@ export class QuestHelper
}
}
/**
* Add item stack change object into output route event response
* @param output Response to add item change event into
* @param sessionId Session id
* @param item Item that was adjusted
*/
protected addItemStackSizeChangeIntoEventResponse(output: IItemEventRouterResponse, sessionId: string, item: Item): void
{
output.profileChanges[sessionId].items.change.push({
"_id": item._id,
"_tpl": item._tpl,
"parentId": item.parentId,
"slotId": item.slotId,
"location": item.location,
"upd": { "StackObjectsCount": item.upd.StackObjectsCount }
});
}
/**
* Get quests, strip all requirement conditions except level
* @param quests quests to process