Merge branch '3.8.1-DEV' of https://dev.sp-tarkov.com/SPT-AKI/Server into 3.9.0-DEV

This commit is contained in:
Dev 2024-04-29 21:50:12 +01:00
commit 267efdc7ea
11 changed files with 166 additions and 65 deletions

View File

@ -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 @@
}
}
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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 };

View File

@ -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<string>();
// 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<string>(),
): 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<string>,
): 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)

View File

@ -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

View File

@ -532,12 +532,17 @@ export class BotGeneratorHelper
rootItemTplId: string,
itemWithChildren: Item[],
inventory: Inventory,
containersIdFull?: Set<string>,
): 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;
}
/**

View File

@ -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<string, SavedCommand> = new Map<string, SavedCommand>();
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;

View File

@ -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;