diff --git a/project/assets/configs/bot.json b/project/assets/configs/bot.json index f9c916dc..30009cd6 100644 --- a/project/assets/configs/bot.json +++ b/project/assets/configs/bot.json @@ -331,7 +331,7 @@ "bosssanitar": { "5efde6b4f5448336730dbd61": 1, "5eff09cd30a7dc22fd1ddfed": 1 - }, + }, "bosstagilla": {}, "bossknight": {}, "bosszryachiy": {}, @@ -399,11 +399,11 @@ "60098ad7c2240c0fe85c570a": 2 }, "arenafighterevent": { - "5734758f24597738025ee253": 1 - }, + "5734758f24597738025ee253": 1 + }, "arenafighter": { - "5734758f24597738025ee253": 1 - }, + "5734758f24597738025ee253": 1 + }, "crazyassaultevent": {}, "assaultgroup": {}, "gifter": {}, @@ -790,7 +790,7 @@ "lightIsActiveDayChancePercent": 35, "lightIsActiveNightChancePercent": 95, "laserIsActiveChancePercent": 95, - "forceStock": true, + "forceStock": true, "weaponModLimits": { "scopeLimit": 1, "lightLaserLimit": 1 @@ -1655,18 +1655,18 @@ "5bffdc370db834001d23eca8": 100, "54491bb74bdc2d09088b4567": 100 } - }, - "clothing": { - "add": {}, - "edit": { - "body": { - "5cc0858d14c02e000c6bea66": 20, - "5cde95d97d6c8b647a3769b0": 20 - }, - "feet": { - "5cc085bb14c02e000e67a5c5": 20, - "5cde95ef7d6c8b04713c4f2d": 20 - } + } + }, + "clothing": { + "add": {}, + "edit": { + "body": { + "5cc0858d14c02e000c6bea66": 20, + "5cde95d97d6c8b647a3769b0": 20 + }, + "feet": { + "5cc085bb14c02e000e67a5c5": 20, + "5cde95ef7d6c8b04713c4f2d": 20 } } } @@ -1775,6 +1775,7 @@ } }, "equipment": { + "add": {}, "edit": { "ArmorVest": { "5c0e5bab86f77461f55ed1f3": 135, @@ -1841,6 +1842,7 @@ "max": 50 }, "equipment": { + "add": {}, "edit": { "FirstPrimaryWeapon": { "5c501a4d2e221602b412b540": 2 @@ -1948,6 +1950,7 @@ } }, "equipment": { + "add": {}, "edit": { "FirstPrimaryWeapon": { "65290f395ae2ae97b80fdf2d": 8, @@ -2652,4 +2655,4 @@ } } } -} +} \ No newline at end of file diff --git a/project/assets/database/bots/types/bear.json b/project/assets/database/bots/types/bear.json index 8b42f5af..bb5b0d4d 100644 --- a/project/assets/database/bots/types/bear.json +++ b/project/assets/database/bots/types/bear.json @@ -2502,14 +2502,15 @@ "backpackLoot": { "weights": { "0": 1, + "3": 5, + "4": 7, + "5": 6, + "8": 6, "10": 5, "12": 4, "15": 4, "20": 3, - "23": 1, - "3": 2, - "5": 5, - "8": 6 + "23": 1 }, "whitelist": [] }, @@ -2593,7 +2594,7 @@ }, "stims": { "weights": { - "0": 1, + "0": 2, "1": 2, "2": 1 }, @@ -2602,7 +2603,7 @@ "vestLoot": { "weights": { "0": 1, - "1": 2, + "1": 3, "2": 3, "3": 2, "4": 1 diff --git a/project/assets/database/bots/types/usec.json b/project/assets/database/bots/types/usec.json index f14bd29c..d3d8778a 100644 --- a/project/assets/database/bots/types/usec.json +++ b/project/assets/database/bots/types/usec.json @@ -2499,14 +2499,15 @@ "backpackLoot": { "weights": { "0": 1, + "3": 5, + "4": 7, + "5": 6, + "8": 6, "10": 5, "12": 4, "15": 4, "20": 3, - "23": 1, - "3": 2, - "5": 5, - "8": 6 + "23": 1 }, "whitelist": [] }, @@ -2590,7 +2591,7 @@ }, "stims": { "weights": { - "0": 1, + "0": 2, "1": 2, "2": 1 }, @@ -2599,7 +2600,7 @@ "vestLoot": { "weights": { "0": 1, - "1": 2, + "1": 3, "2": 3, "3": 2, "4": 1 diff --git a/project/assets/database/locales/server/en.json b/project/assets/database/locales/server/en.json index 2346aadf..5d0b5947 100644 --- a/project/assets/database/locales/server/en.json +++ b/project/assets/database/locales/server/en.json @@ -531,6 +531,8 @@ "pmcresponse-killer_negative_28": "Not to worry, i stashed your gear at your moms house", "pmcresponse-killer_negative_29": "Were you even trying", "pmcresponse-killer_negative_30": "I bet you actually paid 250 big ones for that new edition", + "pmcresponse-killer_negative_31": "Get ratted", + "pmcresponse-killer_negative_32": "Rat attack", "pmcresponse-killer_plead_1": "I was trying to extract a quest item and you were in my path", "pmcresponse-killer_plead_2": "I was looting barrel caches and you were in the way, sorry", "pmcresponse-killer_plead_3": "I need PMC kills, Im sure you understand", diff --git a/project/assets/database/templates/profiles.json b/project/assets/database/templates/profiles.json index 66e4fbf5..aab52e6f 100644 --- a/project/assets/database/templates/profiles.json +++ b/project/assets/database/templates/profiles.json @@ -20138,7 +20138,12 @@ "y": 0, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613bb72b5b0ba138a0fa9e8", "_tpl": "590c5d4b86f774784e1b9c45", @@ -20938,7 +20943,12 @@ "y": 32, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613bb72b5b0ba138a0faa3c", "_tpl": "5448fee04bdc2dbc018b4567", @@ -20949,7 +20959,12 @@ "y": 32, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613bb72b5b0ba138a0faa3d", "_tpl": "5448fee04bdc2dbc018b4567", @@ -20960,7 +20975,12 @@ "y": 32, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613bb72b5b0ba138a0faa3e", "_tpl": "57347da92459774491567cf5", @@ -23798,7 +23818,12 @@ "y": 0, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613e1cf291a2e76b002699b", "_tpl": "590c5f0d86f77413997acfab", @@ -25430,7 +25455,12 @@ "y": 29, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613e1cf291a2e76b0026a3f", "_tpl": "5d02778e86f774203e7dedbe", @@ -25668,7 +25698,12 @@ "y": 29, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613e1cf291a2e76b0026a59", "_tpl": "5e8488fa988a8701445df1e4", @@ -25839,7 +25874,12 @@ "y": 29, "r": 0, "isSearched": true - } + }, + "upd": { + "FoodDrink": { + "HpPercent": 60 + } + } }, { "_id": "6613e1cf291a2e76b0026a68", "_tpl": "544fb3364bdc2d34748b456a", diff --git a/project/src/controllers/HealthController.ts b/project/src/controllers/HealthController.ts index bed64766..f69ade0e 100644 --- a/project/src/controllers/HealthController.ts +++ b/project/src/controllers/HealthController.ts @@ -127,6 +127,9 @@ export class HealthController const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource; if (consumedItemMaxResource > 1) { + // Ensure item has a upd object + this.itemHelper.addUpdObjectToItem(itemToConsume); + if (itemToConsume.upd.FoodDrink === undefined) { itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count }; diff --git a/project/src/generators/BotLootGenerator.ts b/project/src/generators/BotLootGenerator.ts index 0f01bf6c..e0e85505 100644 --- a/project/src/generators/BotLootGenerator.ts +++ b/project/src/generators/BotLootGenerator.ts @@ -119,6 +119,10 @@ export class BotLootGenerator const containersBotHasAvailable = this.getAvailableContainersBotCanStoreItemsIn(botInventory); + // This set is passed as a reference to fill up the containers that are already full, this aliviates + // generation of the bots by avoiding checking the slots of containers we already know are full + const containersIdFull = new Set(); + // Special items this.addLootFromPool( this.botLootCacheService.getLootFromCache(botRole, isPmc, LootCacheType.SPECIAL, botJsonTemplate), @@ -127,6 +131,9 @@ export class BotLootGenerator botInventory, botRole, botItemLimits, + undefined, + undefined, + containersIdFull, ); // Healing items / Meds @@ -139,6 +146,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Drugs @@ -151,6 +159,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Food @@ -163,6 +172,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Drink @@ -175,6 +185,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Currency @@ -187,6 +198,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Stims @@ -199,6 +211,7 @@ export class BotLootGenerator botItemLimits, 0, isPmc, + containersIdFull, ); // Grenades @@ -211,6 +224,7 @@ export class BotLootGenerator null, 0, isPmc, + containersIdFull, ); // Backpack - generate loot if they have one @@ -228,6 +242,7 @@ export class BotLootGenerator botRole, isPmc, botLevel, + containersIdFull, ); } @@ -240,6 +255,7 @@ export class BotLootGenerator botItemLimits, this.pmcConfig.maxBackpackLootTotalRub, isPmc, + containersIdFull, ); } @@ -256,6 +272,7 @@ export class BotLootGenerator botItemLimits, this.pmcConfig.maxVestLootTotalRub, isPmc, + containersIdFull, ); } @@ -269,6 +286,7 @@ export class BotLootGenerator botItemLimits, this.pmcConfig.maxPocketLootTotalRub, isPmc, + containersIdFull, ); // Secure @@ -281,6 +299,7 @@ export class BotLootGenerator null, -1, isPmc, + containersIdFull, ); } @@ -373,6 +392,7 @@ export class BotLootGenerator itemSpawnLimits: IItemSpawnLimitSettings = null, totalValueLimitRub = 0, isPmc = false, + containersIdFull = new Set(), ): void { // Loot pool has items @@ -465,6 +485,7 @@ export class BotLootGenerator itemToAddTemplate._id, itemWithChildrenToAdd, inventoryToAddItemsTo, + containersIdFull, ); // Handle when item cannot be added @@ -593,6 +614,7 @@ export class BotLootGenerator botRole: string, isPmc: boolean, botLevel: number, + containersIdFull?: Set, ): void { const chosenWeaponType = this.randomUtil.getArrayValue([ @@ -625,6 +647,7 @@ export class BotLootGenerator generatedWeapon.weapon[0]._tpl, [...generatedWeapon.weapon], botInventory, + containersIdFull, ); if (result !== ItemAddedResult.SUCCESS) diff --git a/project/src/generators/FenceBaseAssortGenerator.ts b/project/src/generators/FenceBaseAssortGenerator.ts index be6ac7a2..a1e92070 100644 --- a/project/src/generators/FenceBaseAssortGenerator.ts +++ b/project/src/generators/FenceBaseAssortGenerator.ts @@ -106,7 +106,11 @@ export class FenceBaseAssortGenerator if (this.itemHelper.isOfBaseclass(rootItemDb._id, BaseClasses.AMMO_BOX)) { - this.itemHelper.addCartridgesToAmmoBox(itemWithChildrenToAdd, rootItemDb); + // Only add cartridges to box if box has no children + if (itemWithChildrenToAdd.length === 1) + { + this.itemHelper.addCartridgesToAmmoBox(itemWithChildrenToAdd, rootItemDb); + } } // Ensure IDs are unique diff --git a/project/src/helpers/BotGeneratorHelper.ts b/project/src/helpers/BotGeneratorHelper.ts index 8a565d3e..698a61de 100644 --- a/project/src/helpers/BotGeneratorHelper.ts +++ b/project/src/helpers/BotGeneratorHelper.ts @@ -532,12 +532,17 @@ export class BotGeneratorHelper rootItemTplId: string, itemWithChildren: Item[], inventory: Inventory, + containersIdFull?: Set, ): ItemAddedResult { /** Track how many containers are unable to be found */ let missingContainerCount = 0; for (const equipmentSlotId of equipmentSlots) { + if (containersIdFull?.has(equipmentSlotId)) + { + continue; + } // Get container to put item into const container = inventory.items.find((item) => item.slotId === equipmentSlotId); if (!container) @@ -583,8 +588,11 @@ export class BotGeneratorHelper const totalSlotGridCount = containerTemplate[1]._props.Grids.length; for (const slotGrid of containerTemplate[1]._props.Grids) { - // Grid is empty, skip - if (slotGrid._props.cellsH === 0 || slotGrid._props.cellsV === 0) + // Grid is empty, skip or item size is bigger than grid + if ( + (slotGrid._props.cellsH === 0 || slotGrid._props.cellsV === 0) + || (itemSize[0] * itemSize[1] > slotGrid._props.cellsV * slotGrid._props.cellsH) + ) { continue; } @@ -592,12 +600,6 @@ export class BotGeneratorHelper // Can't put item type in grid, skip all grids as we're assuming they have the same rules if (!this.itemAllowedInContainer(slotGrid, rootItemTplId)) { - // Only one possible slot and item is incompatible, exit function and inform caller - if (equipmentSlots.length === 1) - { - return ItemAddedResult.INCOMPATIBLE_ITEM; - } - // Multiple containers, maybe next one allows item, only break out of loop for this containers grids break; } @@ -653,15 +655,25 @@ export class BotGeneratorHelper // If we've checked all grids in container and reached this point, there's no space for item if (currentGridCount >= totalSlotGridCount) { - return ItemAddedResult.NO_SPACE; + break; } - currentGridCount++; + currentGridCount++; // No space in this grid, move to next container grid and try again } + + // if we got to this point, the item couldnt be placed on the container + if (containersIdFull) + { + // if the item was a one by one, we know it must be full. Or if the maps cant find a slot for a one by one + if ((itemSize[0] === 1 && itemSize[1] === 1)) + { + containersIdFull.add(equipmentSlotId); + } + } } - return ItemAddedResult.UNKNOWN; + return ItemAddedResult.NO_SPACE; } /** diff --git a/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts b/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts index 9bfb7004..199f82e3 100644 --- a/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts +++ b/project/src/helpers/Dialogue/Commando/SptCommands/GiveCommand/GiveSptCommand.ts @@ -8,6 +8,7 @@ import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; +import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { LocaleService } from "@spt-aki/services/LocaleService"; import { MailSendService } from "@spt-aki/services/MailSendService"; import { HashUtil } from "@spt-aki/utils/HashUtil"; @@ -29,7 +30,7 @@ export class GiveSptCommand implements ISptCommand private static commandRegex = /^spt give (((([a-z]{2,5}) )?"(.+)"|\w+) )?([0-9]+)$/; private static maxAllowedDistance = 1.5; - protected savedCommand: SavedCommand; + protected savedCommand: Map = new Map(); public constructor( @inject("WinstonLogger") protected logger: ILogger, @@ -40,6 +41,7 @@ export class GiveSptCommand implements ISptCommand @inject("MailSendService") protected mailSendService: MailSendService, @inject("LocaleService") protected localeService: LocaleService, @inject("DatabaseServer") protected databaseServer: DatabaseServer, + @inject("ItemFilterService") protected itemFilterService: ItemFilterService, ) { } @@ -76,7 +78,7 @@ export class GiveSptCommand implements ISptCommand // This is a reply to a give request previously made pending a reply if (result[1] === undefined) { - if (this.savedCommand === undefined) + if (!this.savedCommand.has(sessionId)) { this.mailSendService.sendUserMessageToPlayer( sessionId, @@ -85,7 +87,8 @@ export class GiveSptCommand implements ISptCommand ); return request.dialogId; } - if (+result[6] > this.savedCommand.potentialItemNames.length) + const savedCommand = this.savedCommand.get(sessionId); + if (+result[6] > savedCommand.potentialItemNames.length) { this.mailSendService.sendUserMessageToPlayer( sessionId, @@ -94,16 +97,19 @@ export class GiveSptCommand implements ISptCommand ); return request.dialogId; } - item = this.savedCommand.potentialItemNames[+result[6] - 1]; - quantity = this.savedCommand.quantity; - locale = this.savedCommand.locale; + item = savedCommand.potentialItemNames[+result[6] - 1]; + quantity = savedCommand.quantity; + locale = savedCommand.locale; isItemName = true; - this.savedCommand = undefined; + this.savedCommand.delete(sessionId); } else { // A new give request was entered, we need to ignore the old saved command - this.savedCommand = undefined; + if (this.savedCommand.has(sessionId)) + { + this.savedCommand.delete(sessionId); + } isItemName = result[5] !== undefined; item = result[5] ? result[5] : result[2]; quantity = +result[6]; @@ -134,9 +140,9 @@ export class GiveSptCommand implements ISptCommand const closestItemsMatchedByName = closestMatch( item.toLowerCase(), - this.itemHelper.getItems().filter((i) => i._type !== "Node").map((i) => - localizedGlobal[`${i?._id} Name`]?.toLowerCase() - ).filter((i) => i !== undefined), + this.itemHelper.getItems().filter((i) => i._type !== "Node").filter((i) => + !this.itemFilterService.isItemBlacklisted(i._id) + ).map((i) => localizedGlobal[`${i?._id} Name`]?.toLowerCase()).filter((i) => i !== undefined), true, ) as string[]; @@ -156,7 +162,7 @@ export class GiveSptCommand implements ISptCommand const slicedItems = closestItemsMatchedByName.slice(0, 10); // max 10 item names and map them const itemList = slicedItems.map((itemName) => `${i++}. ${itemName}`).join("\n"); - this.savedCommand = new SavedCommand(quantity, slicedItems, locale); + this.savedCommand.set(sessionId, new SavedCommand(quantity, slicedItems, locale)); this.mailSendService.sendUserMessageToPlayer( sessionId, commandHandler, @@ -185,7 +191,7 @@ export class GiveSptCommand implements ISptCommand // If item is an item name, we need to search using that item name and the locale which one we want otherwise // item is just the tplId. const tplId = isItemName - ? this.itemHelper.getItems().find((i) => + ? this.itemHelper.getItems().filter((i) => !this.itemFilterService.isItemBlacklisted(i._id)).find((i) => this.databaseServer.getTables().locales.global[locale][`${i?._id} Name`]?.toLowerCase() === item )._id : item; diff --git a/project/src/helpers/ItemHelper.ts b/project/src/helpers/ItemHelper.ts index ec79e501..ee4076ab 100644 --- a/project/src/helpers/ItemHelper.ts +++ b/project/src/helpers/ItemHelper.ts @@ -1090,6 +1090,12 @@ export class ItemHelper const cartridgeDetails = this.getItem(cartridgeTpl); const cartridgeMaxStackSize = cartridgeDetails[1]._props.StackMaxSize; + // Exit if ammo already exists in box + if (ammoBox.find((item) => item._tpl === cartridgeTpl)) + { + return; + } + // Add new stack-size-correct items to ammo box let currentStoredCartridgeCount = 0; const maxPerStack = Math.min(ammoBoxMaxCartridgeCount, cartridgeMaxStackSize); @@ -1667,7 +1673,7 @@ export class ItemHelper if (warningMessageWhenMissing) { - this.logger.warning(warningMessageWhenMissing); + this.logger.debug(warningMessageWhenMissing); } return true;