0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 03:30:43 -05:00

Initial implementation of an ItemTpl and Weapon enum generator (!364)

ItemTplGenerator will create a full ItemTpl.ts and Weapons.ts file with all items and weapons respectively
Renamed WeaponTypes to Weapons to better indicate its new format
Updated all references to these enums to use the new names
New npm command `npm run gen:items` to run this tool

Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com>
Reviewed-on: SPT/Server#364
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:
DrakiaXYZ 2024-06-16 08:33:01 +00:00 committed by chomp
parent 66c7ff30b0
commit edfe995c52
19 changed files with 4717 additions and 312 deletions

View File

@ -29,7 +29,8 @@
"run:debug": "gulp run:debug", "run:debug": "gulp run:debug",
"run:profiler": "gulp run:profiler", "run:profiler": "gulp run:profiler",
"gen:types": "tsc -p tsconfig.typedef.json --resolveJsonModule", "gen:types": "tsc -p tsconfig.typedef.json --resolveJsonModule",
"gen:docs": "typedoc --options ./typedoc.json --entryPointStrategy expand ./src" "gen:docs": "typedoc --options ./typedoc.json --entryPointStrategy expand ./src",
"gen:items": "ts-node -r tsconfig-paths/register ./src/tools/ItemTplGenerator/ItemTplGeneratorProgram.ts"
}, },
"dependencies": { "dependencies": {
"atomically": "~1.7", "atomically": "~1.7",
@ -64,10 +65,8 @@
"@types/ws": "~8.5", "@types/ws": "~8.5",
"@typescript-eslint/eslint-plugin": "~7.11", "@typescript-eslint/eslint-plugin": "~7.11",
"@typescript-eslint/parser": "~7.11", "@typescript-eslint/parser": "~7.11",
"typescript-eslint": "~7.11",
"@vitest/coverage-istanbul": "~1.6", "@vitest/coverage-istanbul": "~1.6",
"@vitest/ui": "~1.6", "@vitest/ui": "~1.6",
"vitest": "~1.6",
"@yao-pkg/pkg": "5.11.5", "@yao-pkg/pkg": "5.11.5",
"@yao-pkg/pkg-fetch": "3.5.9", "@yao-pkg/pkg-fetch": "3.5.9",
"cross-env": "~7.0", "cross-env": "~7.0",
@ -93,7 +92,9 @@
"tsconfig-paths": "~4.2", "tsconfig-paths": "~4.2",
"tslint-config-prettier": "~1.18", "tslint-config-prettier": "~1.18",
"typedoc": "~0.25", "typedoc": "~0.25",
"typemoq": "~2.1" "typemoq": "~2.1",
"typescript-eslint": "~7.11",
"vitest": "~1.6"
}, },
"targets": { "targets": {
"default": { "default": {

View File

@ -23,7 +23,7 @@ import { BonusType } from "@spt/models/enums/BonusType";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { SkillTypes } from "@spt/models/enums/SkillTypes"; import { SkillTypes } from "@spt/models/enums/SkillTypes";
import { Traders } from "@spt/models/enums/Traders"; import { Traders } from "@spt/models/enums/Traders";
import { Weapons12Gauge, Weapons20Gauge } from "@spt/models/enums/WeaponTypes"; import { Weapons } from "@spt/models/enums/Weapons";
import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; import { IBotConfig } from "@spt/models/spt/config/IBotConfig";
import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig";
import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig"; import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
@ -552,7 +552,11 @@ export class GameController
{ {
const itemDb = this.databaseService.getItems(); const itemDb = this.databaseService.getItems();
const shotguns = [Weapons12Gauge.SAIGA_12GA, Weapons20Gauge.TOZ_106, Weapons12Gauge.M870]; const shotguns = [
Weapons.SHOTGUN_12G_SAIGA_12K,
Weapons.SHOTGUN_20G_TOZ_106,
Weapons.SHOTGUN_12G_M870,
];
for (const shotgunId of shotguns) for (const shotgunId of shotguns)
{ {
if (itemDb[shotgunId]._props.ShotgunDispersion) if (itemDb[shotgunId]._props.ShotgunDispersion)

View File

@ -230,7 +230,7 @@ export class InraidController
// Check for cultist amulets in special slot (only slot it can fit) // Check for cultist amulets in special slot (only slot it can fit)
const sacredAmulet = this.itemHelper.getItemFromPoolByTpl( const sacredAmulet = this.itemHelper.getItemFromPoolByTpl(
serverPmcProfile.Inventory.items, serverPmcProfile.Inventory.items,
ItemTpl.SACRED_AMULET, ItemTpl.CULTISTAMULET_SACRED_AMULET,
"SpecialSlot"); "SpecialSlot");
if (sacredAmulet) if (sacredAmulet)
{ {

View File

@ -461,7 +461,7 @@ export class ProfileController
skills: playerPmc.Skills, skills: playerPmc.Skills,
equipment: { equipment: {
// Default inventory tpl // Default inventory tpl
Id: playerPmc.Inventory.items.find((item) => item._tpl === ItemTpl.DEFAULT_INVENTORY)._id, Id: playerPmc.Inventory.items.find((item) => item._tpl === ItemTpl.INVENTORY_DEFAULT)._id,
Items: playerPmc.Inventory.items, Items: playerPmc.Inventory.items,
}, },
achievements: playerPmc.Achievements, achievements: playerPmc.Achievements,

View File

@ -536,7 +536,7 @@ export class BotGenerator
for (const item of profile.Inventory.items) for (const item of profile.Inventory.items)
{ {
// Root item found, update its _id value to newly generated id // Root item found, update its _id value to newly generated id
if (item._tpl === ItemTpl.DEFAULT_INVENTORY) if (item._tpl === ItemTpl.INVENTORY_DEFAULT)
{ {
item._id = newInventoryItemId; item._id = newInventoryItemId;
@ -633,22 +633,22 @@ export class BotGenerator
switch (gameVersion) switch (gameVersion)
{ {
case "edge_of_darkness": case "edge_of_darkness":
return ItemTpl.DOG_TAG_USEC_EOD; return ItemTpl.BARTER_DOGTAG_USEC_EOD;
case "unheard_edition": case "unheard_edition":
return ItemTpl.DOG_TAG_USEC_UNHEARD; return ItemTpl.BARTER_DOGTAG_USEC_TUE;
default: default:
return ItemTpl.DOG_TAG_USEC; return ItemTpl.BARTER_DOGTAG_USEC;
} }
} }
switch (gameVersion) switch (gameVersion)
{ {
case "edge_of_darkness": case "edge_of_darkness":
return ItemTpl.DOG_TAG_BEAR_EOD; return ItemTpl.BARTER_DOGTAG_BEAR_EOD;
case "unheard_edition": case "unheard_edition":
return ItemTpl.DOG_TAG_BEAR_UNHEARD; return ItemTpl.BARTER_DOGTAG_BEAR_TUE;
default: default:
return ItemTpl.DOG_TAG_BEAR; return ItemTpl.BARTER_DOGTAG_BEAR;
} }
} }

View File

@ -109,11 +109,11 @@ export class BotInventoryGenerator
return { return {
items: [ items: [
{ _id: equipmentId, _tpl: ItemTpl.DEFAULT_INVENTORY }, { _id: equipmentId, _tpl: ItemTpl.INVENTORY_DEFAULT },
{ _id: stashId, _tpl: ItemTpl.STASH }, { _id: stashId, _tpl: ItemTpl.STASH_STANDARD_STASH_10X30 },
{ _id: questRaidItemsId, _tpl: ItemTpl.STASH_QUEST_RAID_ITEMS }, { _id: questRaidItemsId, _tpl: ItemTpl.STASH_QUESTRAID },
{ _id: questStashItemsId, _tpl: ItemTpl.STASH_QUEST_ITEMS }, { _id: questStashItemsId, _tpl: ItemTpl.STASH_QUESTOFFLINE },
{ _id: sortingTableId, _tpl: ItemTpl.SORTING_TABLE }, { _id: sortingTableId, _tpl: ItemTpl.SORTINGTABLE_SORTING_TABLE },
], ],
equipment: equipmentId, equipment: equipmentId,
stash: stashId, stash: stashId,

View File

@ -33,9 +33,9 @@ export class GiveSptCommand implements ISptCommand
private static acceptableConfidence = 0.9; private static acceptableConfidence = 0.9;
// exception for flares // exception for flares
private static excludedPresetItems = new Set<string>([ private static excludedPresetItems = new Set<string>([
ItemTpl.RSP30_SIGNAL_CARTRIDGE_RED, ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_RED,
ItemTpl.RSP30_SIGNAL_CARTRIDGE_GREEN, ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_GREEN,
ItemTpl.RSP30_SIGNAL_CARTRIDGE_YELLOW, ItemTpl.FLARE_RSP30_REACTIVE_SIGNAL_CARTRIDGE_YELLOW,
]); ]);
protected savedCommand: Map<string, SavedCommand> = new Map<string, SavedCommand>(); protected savedCommand: Map<string, SavedCommand> = new Map<string, SavedCommand>();

View File

@ -979,7 +979,7 @@ export class HideoutHelper
{ {
btcProd.Products.push({ btcProd.Products.push({
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
_tpl: ItemTpl.BITCOIN, _tpl: ItemTpl.BARTER_PHYSICAL_BITCOIN,
upd: { StackObjectsCount: 1 }, upd: { StackObjectsCount: 1 },
}); });
@ -1151,7 +1151,7 @@ export class HideoutHelper
itemsToAdd.push([ itemsToAdd.push([
{ {
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
_tpl: ItemTpl.BITCOIN, _tpl: ItemTpl.BARTER_PHYSICAL_BITCOIN,
upd: { StackObjectsCount: 1 }, upd: { StackObjectsCount: 1 },
}, },
]); ]);

View File

@ -722,12 +722,12 @@ export class ItemHelper
public isDogtag(tpl: string): boolean public isDogtag(tpl: string): boolean
{ {
const dogTagTpls = [ const dogTagTpls = [
ItemTpl.DOG_TAG_BEAR, ItemTpl.BARTER_DOGTAG_BEAR,
ItemTpl.DOG_TAG_BEAR_EOD, ItemTpl.BARTER_DOGTAG_BEAR_EOD,
ItemTpl.DOG_TAG_BEAR_UNHEARD, ItemTpl.BARTER_DOGTAG_BEAR_TUE,
ItemTpl.DOG_TAG_USEC, ItemTpl.BARTER_DOGTAG_USEC,
ItemTpl.DOG_TAG_USEC_EOD, ItemTpl.BARTER_DOGTAG_USEC_EOD,
ItemTpl.DOG_TAG_USEC_UNHEARD, ItemTpl.BARTER_DOGTAG_USEC_TUE,
]; ];
return dogTagTpls.includes(<any>tpl); return dogTagTpls.includes(<any>tpl);

File diff suppressed because it is too large Load Diff

View File

@ -1,217 +0,0 @@
export enum Weapons127x55
{
ASH_12 = "5cadfbf7ae92152ac412eeef",
RSH_12 = "633ec7c2a6918cb895019c6c",
}
export enum Weapons86x70
{
MK_18 = "5fc22d7c187fea44d52eda44",
AXMC = "627e14b21713922ded6f2c15",
}
export enum Weapons9x39
{
AS_VAL = "57c44b372459772d2b39b8ce",
VSS_VINTOREZ = "57838ad32459774a17445cd2",
KBP_9A_91 = "644674a13d52156624001fbc",
VSK_94 = "645e0c6b3b381ede770e1cc9",
}
export enum Weapons762x54R
{
SVDS = "5c46fbd72e2216398b5a8c9c",
MP_18 = "61f7c9e189e6fb1a5e3ea78d",
MOSIN_INFANTRY = "5bfd297f0db834001a669119",
MOSIN_SNIPER = "5ae08f0a5acfc408fb1398a1",
SV_98 = "55801eed4bdc2d89578b4588",
AVT_40 = "6410733d5dd49d77bd07847e",
SVT_40 = "643ea5b23db6f9f57107d9fd",
PKM = "64637076203536ad5600c990",
PKP = "64ca3d3954fc657e230529cc",
}
export enum Weapons762x51
{
VPO_101 = "5c501a4d2e221602b412b540",
DT_MDR_762 = "5dcbd56fdbd3d91b3e5468d5",
SA_58 = "5b0bbe4e5acfc40dc528a72d",
SCARH_BLACK = "6183afd850224f204c1da514",
SCARH_FDE = "6165ac306ef05c2ce828ef74",
HK_G28 = "6176aca650224f204c1da3fb",
M1A = "5aafa857e5b5b00018480968",
RFB = "5f2a9575926fd9352339381f",
RSASS = "5a367e5dc4a282000e49738f",
SR_25 = "5df8ce05b11454561e39243b",
DVL_10 = "588892092459774ac91d4b11",
M700 = "5bfea6e90db834001b7347f3",
T5000M = "5df24cf80dee1b22f862e9bc",
}
export enum Weapons366TKM
{
VPO_209 = "59e6687d86f77411d949b251",
VPO_215 = "5de652c31b7e3716273428be",
}
export enum Weapons762x39
{
OP_SKS = "587e02ff24597743df3deaeb",
SKS = "574d967124597745970e7c94",
AK_103 = "5ac66d2e5acfc43b321d4b53",
AK_104 = "5ac66d725acfc43b321d4b60",
AKM = "59d6088586f774275f37482f",
AKMN = "5a0ec13bfcdbcb00165aa685",
AKMS = "59ff346386f77477562ff5e2",
AKMSN = "5abcbc27d8ce8700182eceeb",
MK47_MUTANT = "606587252535c57a13424cfd",
RD_704 = "628a60ae6b1d481ff772e9c8",
VPO_136 = "59e6152586f77473dc057aa1",
RPD = "6513ef33e06849f06c0957ca",
RPDN = "65268d8ecb944ff1e90ea385",
}
export enum Weapons762x35
{
MCX = "5fbcc1d9016cce60e8341ab3",
}
export enum Weapons556x45
{
ADAR_2_15 = "5c07c60e0db834002330051f",
AK_101 = "5ac66cb05acfc40198510a10",
AK_102 = "5ac66d015acfc400180ae6e4",
DT_MDR_556 = "5c488a752e221602b412af63",
HK_416A5 = "5bb2475ed4351e00853264e3",
HK_G36 = "623063e994fc3f7b302a9696",
M4A1 = "5447a9cd4bdc2dbd208b4567",
SCARL_BLACK = "6184055050224f204c1da540",
SCARL_FDE = "618428466ef05c2ce828f218",
TX15_DML = "5d43021ca4b9362eab4b5e25",
AUG_A1 = "62e7c4fba689e8c9c50dfc38",
AUG_A3 = "63171672192e68c5460cebc5",
}
export enum Weapons545x39
{
AK_105 = "5ac66d9b5acfc4001633997a",
AK_74 = "5bf3e03b0db834001d2c4a9c",
AK_74M = "5ac4cd105acfc40016339859",
AK_74N = "5644bd2b4bdc2d3b4c8b4572",
AKS_74 = "5bf3e0490db83400196199af",
AKS_74N = "5ab8e9fcd8ce870019439434",
AKS_74U = "57dc2fa62459775949412633",
AKS_74UB = "5839a40f24597726f856b511",
AKS_74UN = "583990e32459771419544dd2",
SAG_AK = "628b5638ad252a16da6dd245",
SAG_AK_SHORT = "628b9c37a733087d0d7fe84b",
RPK_16 = "5beed0f50db834001c062b12",
AK_12 = "6499849fc93611967b034949",
}
export enum Weapons57x28FN
{
FN_57_BLACK = "5d3eb3b0a4b93615055e84d2",
FN_57_FDE = "5d67abc1a4b93614ec50137f",
FN_P90 = "5cc82d76e24e8d00134b4b83",
}
export enum Weapons46x30HK
{
MP7A1 = "5ba26383d4351e00334c93d9",
MP7A2 = "5bd70322209c4d00d7167b8f",
}
export enum Weapons1143x23
{
M1911A1 = "5e81c3cbac2bb513793cdc75",
M45A1 = "5f36a0e5fbf956000b716b65",
USP45 = "6193a720f8ee7e52e42109ed",
UMP45 = "5fc3e272f8b6a877a729eac5",
VECTOR45 = "5fb64bc92b1b027b1f50bcf2",
}
export enum Weapons9x33R
{
CR_50DS = "61a4c8884f95bc3b2c5dc96f",
}
export enum Weapons9x21
{
SR_1MP = "59f98b4986f7746f546d2cef",
SR_2M = "62e14904c2699c0ec93adc47",
}
export enum Weapons9x19
{
GLOCK_17 = "5a7ae0c351dfba0017554310",
GLOCK_18C = "5b1fa9b25acfc40018633c01",
M9A3 = "5cadc190ae921500103bb3b6",
MP_443 = "576a581d2459771e7b1bc4f1",
P226R = "56d59856d2720bd8418b456a",
PL_15 = "602a9740da11d6478d5a06dc",
CR_200DS = "624c2e8614da335f1e034d8c",
MP5 = "5926bb2186f7744b1c6c6e60",
MP5K = "5d2f0d8048f0356c925bc3b0",
MP9 = "5e00903ae9dc277128008b87",
MP9_N = "5de7bd7bfd6b4e6e2276dc25",
MPX = "58948c8e86f77409493f7266",
PP_19_01 = "59984ab886f7743e98271174",
SAIGA_9 = "59f9cabd86f7743a10721f46",
STM_9 = "60339954d62c9b14ed777c06",
VECTOR_9MM = "5fc3f2d5900b1d5091531e57",
GLOCK_19X = "63088377b5cd696784087147",
}
export enum Weapons9x18
{
APB = "5abccb7dd8ce87001773e277",
APS = "5a17f98cfcdbcb0980087290",
PB_SILENCED = "56e0598dd2720bb5668b45a6",
PM = "5448bd6b4bdc2dfc2f8b4569",
PM_T = "579204f224597773d619e051",
PP9_KLIN = "57f4c844245977379d5c14d1",
PP91_KEDR = "57d14d2524597714373db789",
PP91_KEDRB = "57f3c6bd24597738e730fa2f",
}
export enum Weapons762x25
{
TT = "571a12c42459771f627b58a0",
TT_GOLD = "5b3b713c5acfc4330140bd8d",
PPSH_41 = "5ea03f7400685063ec28bfa8",
}
export enum Weapons12Gauge
{
M3_SUPER90 = "6259b864ebedf17603599e88",
M590A1 = "5e870397991fd70db46995c8",
M870 = "5a7828548dc32e5a9c28b516",
MP_133 = "54491c4f4bdc2db1078b4568",
MP_153 = "56dee2bdd2720bc8328b4567",
MP_155 = "606dae0ab0e443224b421bb7",
MP_43_1C = "5580223e4bdc2d1c128b457f",
MTS_255_12 = "60db29ce99594040e04c4a27",
SAIGA_12GA = "576165642459773c7a400233",
}
export enum Weapons20Gauge
{
TOZ_106 = "5a38e6bac4a2826c6e06d79b",
}
export enum Weapons23x75
{
KS_23M = "5e848cc2988a8701445df1e8",
}
export enum Weapons68x51
{
MCX_SPEAR = "65290f395ae2ae97b80fdf2d",
}
export enum Weapons40x46
{
M32A1 = "6275303a9f372d6ea97f9ec7",
FN40GL = "5e81ebcd8e146c7080625e15",
}

View File

@ -0,0 +1,143 @@
// This is an auto generated file, do not modify. Re-generate with `npm run gen:items`
export enum Weapons
{
ASSAULTCARBINE_762X39_OP_SKS = "587e02ff24597743df3deaeb",
ASSAULTCARBINE_762X39_SKS = "574d967124597745970e7c94",
ASSAULTCARBINE_762X51_VPO_101_VEPR_HUNTER = "5c501a4d2e221602b412b540",
ASSAULTCARBINE_762X54R_AVT_40 = "6410733d5dd49d77bd07847e",
ASSAULTCARBINE_762X54R_SVT_40 = "643ea5b23db6f9f57107d9fd",
ASSAULTCARBINE_9X39_9A_91 = "644674a13d52156624001fbc",
ASSAULTCARBINE_9X39_AS_VAL = "57c44b372459772d2b39b8ce",
ASSAULTCARBINE_9X39_VSK_94 = "645e0c6b3b381ede770e1cc9",
ASSAULTRIFLE_127X55_ASH_12 = "5cadfbf7ae92152ac412eeef",
ASSAULTRIFLE_366TKM_VPO_209 = "59e6687d86f77411d949b251",
ASSAULTRIFLE_545X39_AKS_74 = "5bf3e0490db83400196199af",
ASSAULTRIFLE_545X39_AKS_74N = "5ab8e9fcd8ce870019439434",
ASSAULTRIFLE_545X39_AKS_74U = "57dc2fa62459775949412633",
ASSAULTRIFLE_545X39_AKS_74UB = "5839a40f24597726f856b511",
ASSAULTRIFLE_545X39_AKS_74UN = "583990e32459771419544dd2",
ASSAULTRIFLE_545X39_AK_105 = "5ac66d9b5acfc4001633997a",
ASSAULTRIFLE_545X39_AK_12 = "6499849fc93611967b034949",
ASSAULTRIFLE_545X39_AK_545 = "628b5638ad252a16da6dd245",
ASSAULTRIFLE_545X39_AK_545_SHORT = "628b9c37a733087d0d7fe84b",
ASSAULTRIFLE_545X39_AK_74 = "5bf3e03b0db834001d2c4a9c",
ASSAULTRIFLE_545X39_AK_74M = "5ac4cd105acfc40016339859",
ASSAULTRIFLE_545X39_AK_74N = "5644bd2b4bdc2d3b4c8b4572",
ASSAULTRIFLE_556X45_ADAR_2_15 = "5c07c60e0db834002330051f",
ASSAULTRIFLE_556X45_AK_101 = "5ac66cb05acfc40198510a10",
ASSAULTRIFLE_556X45_AK_102 = "5ac66d015acfc400180ae6e4",
ASSAULTRIFLE_556X45_AUG_A1 = "62e7c4fba689e8c9c50dfc38",
ASSAULTRIFLE_556X45_AUG_A3 = "63171672192e68c5460cebc5",
ASSAULTRIFLE_556X45_G36 = "623063e994fc3f7b302a9696",
ASSAULTRIFLE_556X45_HK_416A5 = "5bb2475ed4351e00853264e3",
ASSAULTRIFLE_556X45_M4A1 = "5447a9cd4bdc2dbd208b4567",
ASSAULTRIFLE_556X45_MDR = "5c488a752e221602b412af63",
ASSAULTRIFLE_556X45_MK_16 = "6184055050224f204c1da540",
ASSAULTRIFLE_556X45_MK_16_FDE = "618428466ef05c2ce828f218",
ASSAULTRIFLE_556X45_TX_15_DML = "5d43021ca4b9362eab4b5e25",
ASSAULTRIFLE_68X51_SPEAR_68 = "65290f395ae2ae97b80fdf2d",
ASSAULTRIFLE_762X35_MCX_300_BLK = "5fbcc1d9016cce60e8341ab3",
ASSAULTRIFLE_762X39_AKM = "59d6088586f774275f37482f",
ASSAULTRIFLE_762X39_AKMN = "5a0ec13bfcdbcb00165aa685",
ASSAULTRIFLE_762X39_AKMS = "59ff346386f77477562ff5e2",
ASSAULTRIFLE_762X39_AKMSN = "5abcbc27d8ce8700182eceeb",
ASSAULTRIFLE_762X39_AK_103 = "5ac66d2e5acfc43b321d4b53",
ASSAULTRIFLE_762X39_AK_104 = "5ac66d725acfc43b321d4b60",
ASSAULTRIFLE_762X39_MK47 = "606587252535c57a13424cfd",
ASSAULTRIFLE_762X39_RD_704 = "628a60ae6b1d481ff772e9c8",
ASSAULTRIFLE_762X39_VPO_136_VEPR_KM = "59e6152586f77473dc057aa1",
ASSAULTRIFLE_762X51_MDR = "5dcbd56fdbd3d91b3e5468d5",
ASSAULTRIFLE_762X51_MK_17 = "6183afd850224f204c1da514",
ASSAULTRIFLE_762X51_MK_17_FDE = "6165ac306ef05c2ce828ef74",
ASSAULTRIFLE_762X51_SA_58 = "5b0bbe4e5acfc40dc528a72d",
ASSAULTRIFLE_9X18PM_MASTER_HAND = "5ae083b25acfc4001a5fc702",
FLARE_26X75_FLARE_WHITE = "62178be9d0050232da3485d9",
FLARE_26X75_GREEN = "6217726288ed9f0845317459",
FLARE_26X75_RED = "62178c4d4ecf221597654e3d",
FLARE_26X75_YELLOW = "624c0b3340357b5f566e8766",
GRENADELAUNCHER_40MMRU_FN40GL = "639af924d0446708ee62294e",
GRENADELAUNCHER_40X46_FN40GL = "5e81ebcd8e146c7080625e15",
GRENADELAUNCHER_40X46_FN40GL_2 = "639c3fbbd0446708ee622ee9",
MACHINEGUN_30X29_AGS_30 = "5d52cc5ba4b9367408500062",
MACHINEGUN_545X39_NSV_UTYOS = "5cdeb229d7f00c000e7ce174",
MACHINEGUN_545X39_RPK_16 = "5beed0f50db834001c062b12",
MACHINEGUN_762X39_RPD = "6513ef33e06849f06c0957ca",
MACHINEGUN_762X39_RPDN = "65268d8ecb944ff1e90ea385",
MACHINEGUN_762X54R_PKM = "64637076203536ad5600c990",
MACHINEGUN_762X54R_PKP = "64ca3d3954fc657e230529cc",
MACHINEGUN_762X54R_PKTM = "657857faeff4c850222dff1b",
MARKSMANRIFLE_762X51_G28 = "6176aca650224f204c1da3fb",
MARKSMANRIFLE_762X51_M1A = "5aafa857e5b5b00018480968",
MARKSMANRIFLE_762X51_RFB = "5f2a9575926fd9352339381f",
MARKSMANRIFLE_762X51_RSASS = "5a367e5dc4a282000e49738f",
MARKSMANRIFLE_762X51_SR_25 = "5df8ce05b11454561e39243b",
MARKSMANRIFLE_762X54R_SVDS = "5c46fbd72e2216398b5a8c9c",
MARKSMANRIFLE_86X70_MK_18_MJLNIR = "5fc22d7c187fea44d52eda44",
MARKSMANRIFLE_9X39_VSS_VINTOREZ = "57838ad32459774a17445cd2",
PISTOL_20X1MM_BLICKY = "66015072e9f84d5680039678",
PISTOL_45ACP_M1911A1 = "5e81c3cbac2bb513793cdc75",
PISTOL_45ACP_M45A1 = "5f36a0e5fbf956000b716b65",
PISTOL_45ACP_USP_45 = "6193a720f8ee7e52e42109ed",
PISTOL_57X28_FN_5_7 = "5d3eb3b0a4b93615055e84d2",
PISTOL_57X28_FN_5_7_FDE = "5d67abc1a4b93614ec50137f",
PISTOL_762X25TT_TT = "571a12c42459771f627b58a0",
PISTOL_762X25TT_TT_GOLD_GOLDEN = "5b3b713c5acfc4330140bd8d",
PISTOL_9X18PM_APB = "5abccb7dd8ce87001773e277",
PISTOL_9X18PM_APS = "5a17f98cfcdbcb0980087290",
PISTOL_9X18PM_PB = "56e0598dd2720bb5668b45a6",
PISTOL_9X18PM_PM = "5448bd6b4bdc2dfc2f8b4569",
PISTOL_9X18PM_PM_T = "579204f224597773d619e051",
PISTOL_9X19_GLOCK_17 = "5a7ae0c351dfba0017554310",
PISTOL_9X19_GLOCK_18C = "5b1fa9b25acfc40018633c01",
PISTOL_9X19_GLOCK_19X = "63088377b5cd696784087147",
PISTOL_9X19_M9A3 = "5cadc190ae921500103bb3b6",
PISTOL_9X19_MP_443_GRACH = "576a581d2459771e7b1bc4f1",
PISTOL_9X19_P226R = "56d59856d2720bd8418b456a",
PISTOL_9X19_PL_15 = "602a9740da11d6478d5a06dc",
PISTOL_9X21_SR_1MP = "59f98b4986f7746f546d2cef",
REVOLVER_127X55_RSH_12 = "633ec7c2a6918cb895019c6c",
REVOLVER_12G_MTS_255_12 = "60db29ce99594040e04c4a27",
REVOLVER_40X46_MSGL = "6275303a9f372d6ea97f9ec7",
REVOLVER_9X19_CR_200DS = "624c2e8614da335f1e034d8c",
REVOLVER_9X33R_CR_50DS = "61a4c8884f95bc3b2c5dc96f",
SHOTGUN_12G_590A1 = "5e870397991fd70db46995c8",
SHOTGUN_12G_M3_SUPER_90 = "6259b864ebedf17603599e88",
SHOTGUN_12G_M870 = "5a7828548dc32e5a9c28b516",
SHOTGUN_12G_MP_133 = "54491c4f4bdc2db1078b4568",
SHOTGUN_12G_MP_153 = "56dee2bdd2720bc8328b4567",
SHOTGUN_12G_MP_155 = "606dae0ab0e443224b421bb7",
SHOTGUN_12G_MP_43_1C = "5580223e4bdc2d1c128b457f",
SHOTGUN_12G_SAIGA_12K = "576165642459773c7a400233",
SHOTGUN_12G_SAWED_OFF = "64748cb8de82c85eaf0a273a",
SHOTGUN_20G_TOZ_106 = "5a38e6bac4a2826c6e06d79b",
SHOTGUN_23X75_KS_23M = "5e848cc2988a8701445df1e8",
SHOTGUN_762X54R_MP_18 = "61f7c9e189e6fb1a5e3ea78d",
SIGNALPISTOL_26X75_SP_81 = "620109578d82e67e7911abf2",
SMG_45ACP_UMP_45 = "5fc3e272f8b6a877a729eac5",
SMG_45ACP_VECTOR_45ACP = "5fb64bc92b1b027b1f50bcf2",
SMG_46X30_MP7A1 = "5ba26383d4351e00334c93d9",
SMG_46X30_MP7A2 = "5bd70322209c4d00d7167b8f",
SMG_57X28_P90 = "5cc82d76e24e8d00134b4b83",
SMG_762X25TT_PPSH41 = "5ea03f7400685063ec28bfa8",
SMG_9X18PMM_PP_9_KLIN = "57f4c844245977379d5c14d1",
SMG_9X18PM_PP_91_01_KEDR_B = "57f3c6bd24597738e730fa2f",
SMG_9X18PM_PP_91_KEDR = "57d14d2524597714373db789",
SMG_9X19_MP5K_N = "5d2f0d8048f0356c925bc3b0",
SMG_9X19_MP5_NAVY_3_ROUND_BURST = "5926bb2186f7744b1c6c6e60",
SMG_9X19_MP9 = "5e00903ae9dc277128008b87",
SMG_9X19_MP9_N = "5de7bd7bfd6b4e6e2276dc25",
SMG_9X19_MPX = "58948c8e86f77409493f7266",
SMG_9X19_PP_19_01 = "59984ab886f7743e98271174",
SMG_9X19_SAIGA_9 = "59f9cabd86f7743a10721f46",
SMG_9X19_STM_9 = "60339954d62c9b14ed777c06",
SMG_9X19_VECTOR_9X19 = "5fc3f2d5900b1d5091531e57",
SMG_9X21_SR_2M = "62e14904c2699c0ec93adc47",
SNIPERRIFLE_366TKM_VPO_215_GORNOSTAY = "5de652c31b7e3716273428be",
SNIPERRIFLE_762X51_DVL_10 = "588892092459774ac91d4b11",
SNIPERRIFLE_762X51_M700 = "5bfea6e90db834001b7347f3",
SNIPERRIFLE_762X51_T_5000M = "5df24cf80dee1b22f862e9bc",
SNIPERRIFLE_762X54R_MOSIN_INFANTRY = "5bfd297f0db834001a669119",
SNIPERRIFLE_762X54R_MOSIN_SNIPER = "5ae08f0a5acfc408fb1398a1",
SNIPERRIFLE_762X54R_SV_98 = "55801eed4bdc2d89578b4588",
SNIPERRIFLE_86X70_AXMC = "627e14b21713922ded6f2c15",
}

View File

@ -86,8 +86,8 @@ export class BotWeaponModLimitService
): boolean ): boolean
{ {
// If mod or mods parent is the NcSTAR MPR45 Backup mount, allow it as it looks cool // If mod or mods parent is the NcSTAR MPR45 Backup mount, allow it as it looks cool
if (modsParent._id === ItemTpl.NCSTAR_MPR45_BACKUP_MOUNT if (modsParent._id === ItemTpl.MOUNT_NCSTAR_MPR45_BACKUP
|| modTemplate._id === ItemTpl.NCSTAR_MPR45_BACKUP_MOUNT) || modTemplate._id === ItemTpl.MOUNT_NCSTAR_MPR45_BACKUP)
{ {
// If weapon already has a longer ranged scope on it, allow ncstar to be spawned // If weapon already has a longer ranged scope on it, allow ncstar to be spawned
if ( if (

View File

@ -209,7 +209,7 @@ export class InsuranceService
const globals = this.databaseService.getGlobals(); const globals = this.databaseService.getGlobals();
const hasMarkOfUnheard = this.itemHelper.hasItemWithTpl( const hasMarkOfUnheard = this.itemHelper.hasItemWithTpl(
pmcData.Inventory.items, pmcData.Inventory.items,
ItemTpl.MARK_OF_UNHEARD, ItemTpl.MARKOFUNKNOWN_MARK_OF_THE_UNHEARD,
"SpecialSlot"); "SpecialSlot");
if (hasMarkOfUnheard) if (hasMarkOfUnheard)
{ {

View File

@ -56,30 +56,30 @@ export class SeasonalEventService
protected get christmasEventItems(): string[] protected get christmasEventItems(): string[]
{ {
return [ return [
ItemTpl.FAKE_WHITE_BEARD, ItemTpl.FACECOVER_FAKE_WHITE_BEARD,
ItemTpl.CHRISTMAS_TREE_ORNAMENT_RED, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_RED,
ItemTpl.CHRISTMAS_TREE_ORNAMENT_VIOLET, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_VIOLET,
ItemTpl.CHRISTMAS_TREE_ORNAMENT_SILVER, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_SILVER,
ItemTpl.DED_MOROZ_HAT, ItemTpl.HEADWEAR_DED_MOROZ_HAT,
ItemTpl.SANTA_HAT, ItemTpl.HEADWEAR_SANTA_HAT,
ItemTpl.SANTAS_BAG, ItemTpl.BACKPACK_SANTAS_BAG,
]; ];
} }
protected get halloweenEventItems(): string[] protected get halloweenEventItems(): string[]
{ {
return [ return [
ItemTpl.SPOOKY_SKULL_MASK, ItemTpl.FACECOVER_SPOOKY_SKULL_MASK,
ItemTpl.PUMPKIN_WITH_SWEETS, ItemTpl.RANDOMLOOTCONTAINER_PUMPKIN_RAND_LOOT_CONTAINER,
ItemTpl.JACK_LANTERN_PUMPKIN_HELMET, ItemTpl.HEADWEAR_JACKOLANTERN_TACTICAL_PUMPKIN_HELMET,
ItemTpl.FACELESS_MASK, ItemTpl.FACECOVER_FACELESS_MASK,
ItemTpl.JASON_MASK, ItemTpl.FACECOVER_JASON_MASK,
ItemTpl.MISHA_MAYOROV_MASK, ItemTpl.FACECOVER_MISHA_MAYOROV_MASK,
ItemTpl.SLENDER_MASK, ItemTpl.FACECOVER_SLENDER_MASK,
ItemTpl.GHOUL_MASK, ItemTpl.FACECOVER_GHOUL_MASK,
ItemTpl.HOCKEY_CAPTAIN_MASK, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_CAPTAIN,
ItemTpl.HOCKEY_BRAWLER_MASK, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_BRAWLER,
ItemTpl.HOCKEY_QUIET_MASK, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_QUIET,
]; ];
} }
@ -547,7 +547,7 @@ export class SeasonalEventService
protected addPumpkinsToScavBackpacks(): void protected addPumpkinsToScavBackpacks(): void
{ {
this.databaseService.getBots().types.assault.inventory.items.Backpack[ItemTpl.PUMPKIN_WITH_SWEETS] = 400; this.databaseService.getBots().types.assault.inventory.items.Backpack[ItemTpl.RANDOMLOOTCONTAINER_PUMPKIN_RAND_LOOT_CONTAINER] = 400;
} }
/** /**

View File

@ -224,7 +224,7 @@ export class CustomItemService
protected addToWeaponShelf(newItemId: string): void protected addToWeaponShelf(newItemId: string): void
{ {
// Ids for wall stashes in db // Ids for wall stashes in db
const wallStashIds = [ItemTpl.WEAPON_STAND_STASH_1, ItemTpl.WEAPON_STAND_STASH_2, ItemTpl.WEAPON_STAND_STASH_3]; const wallStashIds = [ItemTpl.HIDEOUTAREACONTAINER_WEAPONSTAND_STASH_1, ItemTpl.HIDEOUTAREACONTAINER_WEAPONSTAND_STASH_2, ItemTpl.HIDEOUTAREACONTAINER_WEAPONSTAND_STASH_3];
for (const wallId of wallStashIds) for (const wallId of wallStashIds)
{ {
const wall = this.itemHelper.getItem(wallId); const wall = this.itemHelper.getItem(wallId);

View File

@ -0,0 +1,576 @@
/**
* Dynamically generate the following two files:
* - src/models/enums/ItemTpl.ts
* - src/models/enums/Weapons.ts
*
* Based on data from the assets/database folders.
*
* Usage:
* - Run this script using npm: `npm run gen:items`
*
* Notes:
* - Ensure that all necessary Node.js modules are installed before running the script: `npm install`
* - The following rules are used for determining item base names:
* -- Certain items are manually overridden by itemOverrides.ts, when the names are not unique enough
* -- Random containers, built in inserts and stashes utilize the item's _name property
* -- Ammo, ammo boxes, and magazines utilize the item's English locale ShortName property
* -- All other items utilize the item's English locale Name property
* -- In the event the above rules fail, the fallback order is the Englick locale Name property, then the item's _name property
* -- Trailing and leading whitespace is stripped, special characters are removed, and spaces are replaced with underscores
* - Item caliber data is cleaned of the words "CALIBER", "PARA" and "NATO", as well as replacing "1143x23ACP" with "45ACP" for consistency
* - Damaged ammo boxes are suffixed with "_DAMAGED"
* - The parent item type prefix is grouped more than the base item list, see "getParentName" for the rules around this
* - Finalized enum names are created as a combination of the parent name, prefix, item name, and suffix
*/
import * as fs from "fs";
import * as path from "path";
import { inject, injectAll, injectable } from "tsyringe";
import { OnLoad } from "@spt/di/OnLoad";
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
import { BaseClasses } from "@spt/models/enums/BaseClasses";
import { ItemTpl } from "@spt/models/enums/ItemTpl";
import { Weapons } from "@spt/models/enums/Weapons";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { LocaleService } from "@spt/services/LocaleService";
import * as itemTplOverrides from "@spt/tools/ItemTplGenerator/itemOverrides";
@injectable()
export class ItemTplGenerator
{
private enumDir: string;
private items: Record<string, ITemplateItem>;
private itemOverrides: Record<string, string>;
private collidedEnumKeys: string[] = [];
constructor(
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("LocaleService") protected localeService: LocaleService,
@inject("PrimaryLogger") protected logger: ILogger,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@injectAll("OnLoad") protected onLoadComponents: OnLoad[],
)
{}
async run(): Promise<void>
{
// Load all of the onload components, this gives us access to most of SPTs injections
for (const onLoad of this.onLoadComponents)
{
await onLoad.onLoad();
}
// Figure out our source and target directories
const currentDir = path.dirname(__filename);
const projectDir = path.resolve(currentDir, "..", "..", "..");
this.enumDir = path.join(projectDir, "src", "models", "enums");
this.items = this.databaseServer.getTables().templates!.items;
this.itemOverrides = itemTplOverrides.default;
// Generate an object containing all item name to ID associations
const orderedItemsObject = this.generateItemsObject();
// Log any changes to enum values, so the source can be updated as required
this.logEnumValueChanges(orderedItemsObject, "ItemTpl", ItemTpl);
const itemTplOutPath = path.join(this.enumDir, "ItemTpl.ts");
this.writeEnumsToFile(itemTplOutPath, { ItemTpl: orderedItemsObject });
// Handle the weapon type enums
const weaponsObject = this.generateWeaponsObject();
this.logEnumValueChanges(weaponsObject, "Weapons", Weapons);
const weaponTypeOutPath = path.join(this.enumDir, "Weapons.ts");
this.writeEnumsToFile(weaponTypeOutPath, { Weapons: weaponsObject });
this.logger.info("Generating items finished");
}
/**
* Return an object containing all items in the game with a generated name
* @returns An object containing a generated item name to item ID association
*/
private generateItemsObject(): Record<string, string>
{
const itemsObject = {};
for (const item of Object.values(this.items))
{
// Skip invalid items (Non-Item types, and shrapnel)
if (!this.isValidItem(item)) continue;
const itemParentName = this.getParentName(item);
const itemPrefix = this.getItemPrefix(item);
let itemName = this.getItemName(item);
const itemSuffix = this.getItemSuffix(item);
// Handle the case where the item starts with the parent category name. Avoids things like 'POCKETS_POCKETS'
if (itemParentName == itemName.substring(1, itemParentName.length + 1) && itemPrefix == "")
{
itemName = itemName.substring(itemParentName.length + 1);
if (itemName.length > 0 && itemName.at(0) != "_")
{
itemName = `_${itemName}`;
}
}
// Handle the case where the item ends with the parent category name. Avoids things like 'KEY_DORM_ROOM_103_KEY'
if (itemParentName === itemName.substring(itemName.length - itemParentName.length))
{
itemName = itemName.substring(0, itemName.length - itemParentName.length);
if (itemName.substring(itemName.length - 1) === "_")
{
itemName = itemName.substring(0, itemName.length - 1);
}
}
let itemKey = `${itemParentName}${itemPrefix}${itemName}${itemSuffix}`;
// Strip out any remaining special characters
itemKey = this.sanitizeEnumKey(itemKey);
// If the key already exists, see if we can add a suffix to both this, and the existing conflicting item
if (Object.keys(itemsObject).includes(itemKey) || this.collidedEnumKeys.includes(itemKey))
{
// Keep track, so we can handle 3+ conflicts
this.collidedEnumKeys.push(itemKey);
const itemNameSuffix = this.getItemNameSuffix(item);
if (itemNameSuffix)
{
// Try to update the old key reference if we haven't already
if (itemsObject[itemKey])
{
const oldItemId = itemsObject[itemKey];
const oldItemNameSuffix = this.getItemNameSuffix(this.items[oldItemId]);
if (oldItemNameSuffix)
{
const oldItemNewKey = this.sanitizeEnumKey(`${itemKey}_${oldItemNameSuffix}`);
delete itemsObject[itemKey];
itemsObject[oldItemNewKey] = oldItemId;
}
}
itemKey = this.sanitizeEnumKey(`${itemKey}_${itemNameSuffix}`);
// If we still collide, log an error
if (Object.keys(itemsObject).includes(itemKey))
{
this.logger.error(
`After rename, itemsObject already contains ${itemKey} ${itemsObject[itemKey]} => ${item._id}`,
);
}
}
else
{
this.logger.error(
`New itemOverride entry required: itemsObject already contains ${itemKey} ${itemsObject[itemKey]} => ${item._id}`,
);
continue;
}
}
itemsObject[itemKey] = item._id;
}
// Sort the items object
const orderedItemsObject = Object.keys(itemsObject)
.sort()
.reduce((obj, key) =>
{
obj[key] = itemsObject[key];
return obj;
}, {});
return orderedItemsObject;
}
/**
*
* @param orderedItemsObject The previously generated object of item name to item ID associations
* @returns
*/
private generateWeaponsObject(): Record<string, string>
{
const weaponsObject = {};
for (const [itemId, item] of Object.entries(this.items))
{
if (!this.itemHelper.isOfBaseclass(itemId, BaseClasses.WEAPON))
{
continue;
}
const caliber = this.cleanCaliber(item._props.ammoCaliber!.toUpperCase());
let weaponShortName = this.localeService.getLocaleDb()[`${itemId} ShortName`]?.toUpperCase();
// Special case for the weird duplicated grenade launcher
if (itemId === "639c3fbbd0446708ee622ee9")
{
weaponShortName = "FN40GL_2";
}
// Include any bracketed suffixes that exist, handles the case of colored gun variants
const weaponFullName = this.localeService.getLocaleDb()[`${itemId} Name`]?.toUpperCase();
const itemNameBracketSuffix = weaponFullName.match(/\((.+?)\)$/);
if (itemNameBracketSuffix && !weaponShortName.endsWith(itemNameBracketSuffix[1]))
{
weaponShortName += `_${itemNameBracketSuffix[1]}`;
}
const parentName = this.getParentName(item);
// Handle special characters
const weaponName = `${parentName}_${caliber}_${weaponShortName}`
.replace(/[- ]/g, "_")
.replace(/[^a-zA-Z0-9_]/g, "")
.toUpperCase();
if (weaponsObject[weaponName])
{
this.logger.error(`Error, weapon ${weaponName} already exists`);
continue;
}
weaponsObject[weaponName] = itemId;
}
// Sort the weapons object
const orderedWeaponsObject = Object.keys(weaponsObject)
.sort()
.reduce((obj, key) =>
{
obj[key] = weaponsObject[key];
return obj;
}, {});
return orderedWeaponsObject;
}
/**
* Clear any non-alpha numeric characters, and fix multiple underscores
* @param enumKey The enum key to sanitize
* @returns The sanitized enum key
*/
private sanitizeEnumKey(enumKey: string): string
{
return enumKey
.toUpperCase()
.replace(/[^A-Z0-9_]/g, "")
.replace(/_+/g, "_");
}
private getParentName(item: ITemplateItem): string
{
if (item._props.QuestItem)
{
return "QUEST";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.BARTER_ITEM))
{
return "BARTER";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.THROW_WEAPON))
{
return "GRENADE";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.STIMULATOR))
{
return "STIM";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.MAGAZINE))
{
return "MAGAZINE";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.KEY_MECHANICAL))
{
return "KEY";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.MOB_CONTAINER))
{
return "SECURE";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.SIMPLE_CONTAINER))
{
return "CONTAINER";
}
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.PORTABLE_RANGE_FINDER))
{
return "RANGEFINDER";
}
// Why are flares grenade launcher...?
else if (item._name.startsWith("weapon_rsp30"))
{
return "FLARE";
}
// This is a special case for the signal pistol, I'm not adding it as a Grenade Launcher
else if (item._id == "620109578d82e67e7911abf2")
{
return "SIGNALPISTOL";
}
const parentId = item._parent;
return this.items[parentId]._name.toUpperCase();
}
private isValidItem(item: ITemplateItem): boolean
{
const shrapnelId = "5943d9c186f7745a13413ac9";
if (item._type !== "Item")
{
return false;
}
if (item._proto === shrapnelId)
{
return false;
}
return true;
}
/**
* Generate a prefix for the passed in item
* @param item The item to generate the prefix for
* @returns The prefix of the given item
*/
private getItemPrefix(item: ITemplateItem): string
{
let prefix = "";
// Prefix ammo with its caliber
if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.AMMO))
{
prefix = this.getAmmoPrefix(item);
}
// Prefix ammo boxes with their caliber
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.AMMO_BOX))
{
prefix = this.getAmmoBoxPrefix(item);
}
// Prefix magazines with their caliber
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.MAGAZINE))
{
prefix = this.getMagazinePrefix(item);
}
// Make sure there's an underscore separator
if (prefix.length > 0 && prefix.at(0) != "_")
{
prefix = `_${prefix}`;
}
return prefix;
}
private getItemSuffix(item: ITemplateItem): string
{
let suffix = "";
// Add mag size for magazines
if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.MAGAZINE))
{
suffix = item._props.Cartridges![0]?._max_count?.toString() + "RND";
}
// Add pack size for ammo boxes
else if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.AMMO_BOX))
{
suffix = item._props.StackSlots![0]?._max_count.toString() + "RND";
}
// Add "DAMAGED" for damaged items
if (item._name.toLowerCase().includes("damaged"))
{
suffix += "_DAMAGED";
}
// Make sure there's an underscore separator
if (suffix.length > 0 && suffix.at(0) != "_")
{
suffix = `_${suffix}`;
}
return suffix;
}
private getAmmoPrefix(item: ITemplateItem): string
{
const prefix = item._props.Caliber!.toUpperCase();
return this.cleanCaliber(prefix);
}
private cleanCaliber(ammoCaliber: string): string
{
ammoCaliber = ammoCaliber.replace("CALIBER", "");
ammoCaliber = ammoCaliber.replace("PARA", "");
ammoCaliber = ammoCaliber.replace("NATO", "");
// Special case for 45ACP
ammoCaliber = ammoCaliber.replace("1143X23ACP", "45ACP");
return ammoCaliber;
}
private getAmmoBoxPrefix(item: ITemplateItem): string
{
const ammoItem = item._props.StackSlots![0]?._props.filters[0].Filter[0];
return this.getAmmoPrefix(this.items[ammoItem]);
}
private getMagazinePrefix(item: ITemplateItem): string
{
const ammoItem = item._props.Cartridges![0]?._props.filters[0].Filter[0];
return this.getAmmoPrefix(this.items[ammoItem]);
}
/**
* Return the name of the passed in item, formatted for use in an enum
* @param item The item to generate the name for
* @returns The name of the given item
*/
private getItemName(item)
{
let itemName;
// Manual item name overrides
if (this.itemOverrides[item._id])
{
itemName = this.itemOverrides[item._id].toUpperCase();
}
// For the listed types, user the item's _name property
else if (
this.itemHelper.isOfBaseclasses(item._id, [
BaseClasses.RANDOM_LOOT_CONTAINER,
BaseClasses.BUILT_IN_INSERTS,
BaseClasses.STASH,
])
)
{
itemName = item._name.toUpperCase();
}
// For the listed types, use the short name
else if (
this.itemHelper.isOfBaseclasses(item._id, [BaseClasses.AMMO, BaseClasses.AMMO_BOX, BaseClasses.MAGAZINE])
)
{
itemName = this.localeService.getLocaleDb()[`${item._id} ShortName`]?.toUpperCase();
}
// For everything else, use the full name
else
{
itemName = this.localeService.getLocaleDb()[`${item._id} Name`]?.toUpperCase();
}
// Fall back in the event we couldn't find a name
if (!itemName)
{
itemName = this.localeService.getLocaleDb()[`${item._id} Name`]?.toUpperCase();
}
if (!itemName)
{
itemName = item._name.toUpperCase();
}
if (!itemName)
{
console.log(`Unable to get shortname for ${item._id}`);
return "";
}
itemName = itemName.trim().replace(/[-.()]/g, "");
itemName = itemName.replace(/[ ]/g, "_");
return `_${itemName}`;
}
private getItemNameSuffix(item: ITemplateItem): string
{
const itemName = this.localeService.getLocaleDb()[`${item._id} Name`];
// Add grid size for lootable containers
if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.LOOT_CONTAINER))
{
return `${item._props.Grids![0]?._props.cellsH}X${item._props.Grids![0]?._props.cellsV}`;
}
// Add ammo caliber to conflicting weapons
if (this.itemHelper.isOfBaseclass(item._id, BaseClasses.WEAPON))
{
const caliber = this.cleanCaliber(item._props.ammoCaliber!.toUpperCase());
// If the item has a bracketed section at the end of its name, include that
const itemNameBracketSuffix = itemName?.match(/\((.+?)\)$/);
if (itemNameBracketSuffix)
{
return `${caliber}_${itemNameBracketSuffix[1]}`;
}
return caliber;
}
// Make sure we have a full name
if (!itemName)
{
return "";
}
// If the item has a bracketed section at the end of its name, use that
const itemNameBracketSuffix = itemName.match(/\((.+?)\)$/);
if (itemNameBracketSuffix)
{
return itemNameBracketSuffix[1];
}
// If the item has a number at the end of its name, use that
const itemNameNumberSuffix = itemName.match(/#([0-9]+)$/);
if (itemNameNumberSuffix)
{
return itemNameNumberSuffix[1];
}
return "";
}
private logEnumValueChanges(data: Record<string, string>, enumName: string, originalEnum: Record<string, string>)
{
// First generate a mapping of the original enum values to names
const originalEnumValues = {};
for (const [key, value] of Object.entries(originalEnum))
{
originalEnumValues[value as string] = key;
}
// Loop through our new data, and find any where the given ID's name doesn't match the original enum
for (const [dataKey, dataValue] of Object.entries(data))
{
if (originalEnumValues[dataValue] && originalEnumValues[dataValue] !== dataKey)
{
this.logger.warning(
`Enum ${enumName} key has changed for ${dataValue}, ${originalEnumValues[dataValue]} => ${dataKey}`,
);
}
}
}
private writeEnumsToFile(outputPath: string, enumEntries: Record<string, Record<string, string>>): void
{
let enumFileData = "// This is an auto generated file, do not modify. Re-generate with `npm run gen:items`";
for (const [enumName, data] of Object.entries(enumEntries))
{
enumFileData += `\nexport enum ${enumName}\n{\n`;
for (const [key, value] of Object.entries(data))
{
enumFileData += ` ${key} = "${value}",\n`;
}
enumFileData += "}\n";
}
fs.writeFileSync(outputPath, enumFileData, "utf-8");
}
}

View File

@ -0,0 +1,44 @@
import "reflect-metadata";
import "source-map-support/register";
import { Lifecycle, container } from "tsyringe";
import { Container } from "@spt/di/Container";
import { ErrorHandler } from "@spt/ErrorHandler";
import { ItemTplGenerator } from "@spt/tools/ItemTplGenerator/ItemTplGenerator";
export class ItemTplGeneratorProgram
{
private errorHandler: ErrorHandler;
constructor()
{
// set window properties
process.stdout.setEncoding("utf8");
process.title = "SPT ItemTplGenerator";
this.errorHandler = new ErrorHandler();
}
public async start(): Promise<void>
{
try
{
Container.registerTypes(container);
const childContainer = container.createChildContainer();
childContainer.register<ItemTplGenerator>("ItemTplGenerator", ItemTplGenerator, {
lifecycle: Lifecycle.Singleton,
});
Container.registerListTypes(childContainer);
Container.registerPostLoadTypes(container, childContainer);
await childContainer.resolve<ItemTplGenerator>("ItemTplGenerator").run();
}
catch (err: any)
{
this.errorHandler.handleCriticalError(err instanceof Error ? err : new Error(err));
}
// Kill the process, something holds it open so we need to manually kill it
process.exit();
}
}
const program = new ItemTplGeneratorProgram();
program.start();

View File

@ -0,0 +1,93 @@
/* eslint-disable @typescript-eslint/naming-convention */
/**
* An object containing item name overrides for use with ItemTplGenerator
*/
export default {
// Stashes
"5963866b86f7747bfa1c4462": "QuestOffline",
"5963866286f7747bf429b572": "QuestRaid",
// Usables
"614451b71e5874611e2c7ae5": "Bottle of Tarkovskaya vodka (Bad)",
// Special
"557ffd194bdc2d28148b457f": "Pockets 1x4",
"627a4e6b255f7527fb05a0f6": "Pockets 1x4 Special",
"65e080be269cbd5c5005e529": "Pockets 1x4 TUE",
"64cbd95a29b9b4283e216ff5": "Pockets 2x3",
"665ee77ccf2d642e98220bca": "Secure container Gamma TUE",
// Misc
"6662e9f37fa79a6d83730fa0": "Dogtag USEC EOD",
"6662ea05f6259762c56f3189": "Dogtag USEC TUE",
"6662e9aca7e0b43baa3d5f74": "Dogtag BEAR EOD",
"6662e9cda7e0b43baa3d5f76": "Dogtag BEAR TUE",
// Quest Items
"590de92486f77423d9312a33": "Watch Gold",
"5937fc6786f7742cab753590": "Watch Silver",
"5937fd0086f7742bf33fc198": "Watch Bronze",
"638cbb3ba63f1b49be6a300e": "FlashDrive Car Service",
"5eff135be0d3331e9d282b7b": "FlashDrive TerraGroup Employee",
"638e9d5536b3b72c944e2fc7": "FlashDrive Watching You",
"5a29357286f77409c705e025": "Flashdrive Wet Job Pt 4",
"5939e9b286f77462a709572c": "Letter Terragroup",
"591093bb86f7747caa7bb2ee": "Letter Postman Pat",
"61904c9df62c89219a56e034": "Letter Hermit",
"638cbc68a63f1b49be6a3010": "Letter Got Mail",
"619252352be33f26043400a7": "Toughbook Seaside Vacation",
"5a29284f86f77463ef3db363": "Toughbook Cargo",
"64b91627dd13d43b9d01d6d1": "Toughbook Terragroup",
"5a29276886f77435ed1b117c": "HDD Wet Job",
"628393620d8524273e7eb028": "HDD Top Secret",
"638dfc803083a019d447768e": "HDD Surveillance",
"638e0057ab150a5f56238960": "Housing Journal",
"6393262086e646067c176aa2": "Medical Record",
"63989ced706b793c7d60cfef": "Informant Journal",
"6398a072e301557ae24cec92": "Lightkeeper Intel",
"6398a0861c712b1e1d4dadf1": "Lightkeeper Intel (Forged)",
"638e0752ab150a5f56238962": "Lightkeeper Intel 2",
"638e06c4b8bac37a110ed56d": "Lightkeeper Intel 2 (Forged)",
"63927b29c115f907b14700b9": "ChemCont Samples",
"5939e5a786f77461f11c0098": "Secure Folder 0013",
"591092ef86f7747bb8703422": "Secure Folder 0022",
"5938188786f77474f723e87f": "Secure Folder 0031",
"593965cf86f774087a77e1b6": "Secure Folder 0048",
"5938878586f7741b797c562f": "Secure Folder 0052",
"5a6860d886f77411cd3a9e47": "Secure Folder 0060",
"638cbb0f7f97256fac16167a": "Accountant's journal",
"664a5775f3d3570fba06be64": "Arena Poster Bison",
"664b69c5a082271bc46c4e11": "Arena Poster Killa",
"664b69e8e1238e506d3630af": "Arena Poster Easy Money",
"6575a6ca8778e96ded05a802": "Hard Drive TerraGroup Scientist",
"6614217b6d9d5abcad0ff098": "Unheard phone",
"6614230055afee107f05e998": "Unheard phone 2",
"661421c7c1f2f548c50ee649": "Unheard laptop",
"661423200d240a5f5d0f679b": "Unheard laptop 2",
// Weapon Parts
"5d1340b3d7ad1a0b52682ed7": "GEN M3 FDE",
"55802d5f4bdc2dac148b458e": "GEN M3 Window",
"5d1340cad7ad1a0b0b249869": "GEN M3 Window FDE",
"5d1340bdd7ad1a0e8d245aab": "GEN M3 FDE",
"5cbdaf89ae9215000e5b9c94": "6L23 Plum",
"5e21a3c67e40bd02257a008a": "GEN M3 Banana",
// Lootable Containers
"5914944186f774189e5e76c2": "Jacket 204Key",
"5937ef2b86f77408a47244b3": "Jacket MachineryKey",
"59387ac686f77401442ddd61": "Jacket 114Key",
"61aa1e9a32a4743c3453d2cf": "Duffle bag Adv",
"61aa1ead84ea0800645777fd": "Medbag SMU06 Adv",
// Storage Containers
"5b6d9ce188a4501afc1b2b25": "THICC Weapon case",
"5c0a840b86f7742ffa4f2482": "THICC Item case",
"5d235bb686f77443f4331278": "SICC",
// Grenade launchers, they are weird, so just number them
"5e81ebcd8e146c7080625e15": "FN40GL 01",
"639c3fbbd0446708ee622ee9": "FN40GL 02",
"639af924d0446708ee62294e": "FN40GL 03",
};