0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-12 17:10:43 -05:00

Apply Biome Formatting

This is the result of running `npm run format` which applies the Biome formatting rules. Rejoice!
This commit is contained in:
Refringe 2024-07-23 11:12:53 -04:00
parent 7c76342ee2
commit 5740774a46
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
755 changed files with 22787 additions and 31002 deletions

View File

@ -15,14 +15,10 @@
"keepClassNames": true, "keepClassNames": true,
"baseUrl": "./", "baseUrl": "./",
"paths": { "paths": {
"@spt/*": [ "@spt/*": ["src/*"]
"src/*"
]
} }
}, },
"exclude": [ "exclude": ["node_modules/"],
"node_modules/"
],
"module": { "module": {
"type": "commonjs", "type": "commonjs",
"strict": false, "strict": false,

View File

@ -104,12 +104,7 @@
"max": 50 "max": 50
} }
}, },
"armorLevelWhitelist": [ "armorLevelWhitelist": [0, 4, 5, 6],
0,
4,
5,
6
],
"allowBossItems": false "allowBossItems": false
}, },
"weaponArmor": { "weaponArmor": {
@ -181,13 +176,7 @@
"max": 50 "max": 50
} }
}, },
"armorLevelWhitelist": [ "armorLevelWhitelist": [0, 3, 4, 5, 6],
0,
3,
4,
5,
6
],
"allowBossItems": false "allowBossItems": false
}, },
"foodMedical": { "foodMedical": {
@ -267,9 +256,7 @@
"max": 50 "max": 50
} }
}, },
"armorLevelWhitelist": [ "armorLevelWhitelist": [0],
0
],
"allowBossItems": false "allowBossItems": false
}, },
"barter": { "barter": {
@ -350,10 +337,8 @@
"max": 50 "max": 50
} }
}, },
"armorLevelWhitelist": [ "armorLevelWhitelist": [0],
0
],
"allowBossItems": false "allowBossItems": false
} }
} }
} }

View File

@ -47,8 +47,8 @@
"sectactPriestEvent": 10, "sectactPriestEvent": 10,
"pmcUSEC": 15, "pmcUSEC": 15,
"pmcBEAR": 15, "pmcBEAR": 15,
"peacemaker": 10, "peacemaker": 10,
"skier": 10 "skier": 10
}, },
"bosses": [ "bosses": [
"bossBully", "bossBully",
@ -277,12 +277,9 @@
}, },
"chanceAssaultScavHasPlayerScavName": 10, "chanceAssaultScavHasPlayerScavName": 10,
"secureContainerAmmoStackCount": 20, "secureContainerAmmoStackCount": 20,
"botRolesWithDogTags": [ "botRolesWithDogTags": ["pmcbear", "pmcusec"],
"pmcbear",
"pmcusec"
],
"revenge": { "revenge": {
"pmcBot": ["pmcBot","gifter"], "pmcBot": ["pmcBot", "gifter"],
"arenaFighter": ["pmcBot", "gifter"], "arenaFighter": ["pmcBot", "gifter"],
"arenaFighterEvent": ["pmcBot", "gifter"], "arenaFighterEvent": ["pmcBot", "gifter"],
"bossKnight": ["exUsec", "gifter", "bossKnight", "followerBigPipe", "followerBirdEye"], "bossKnight": ["exUsec", "gifter", "bossKnight", "followerBigPipe", "followerBirdEye"],
@ -430,8 +427,8 @@
"544fb37f4bdc2dee738b4567": 2 "544fb37f4bdc2dee738b4567": 2
}, },
"shooterbtr": {}, "shooterbtr": {},
"skier": {}, "skier": {},
"peacemaker": {} "peacemaker": {}
}, },
"equipment": { "equipment": {
"assault": { "assault": {
@ -495,9 +492,7 @@
"lightIsActiveNightChancePercent": 90, "lightIsActiveNightChancePercent": 90,
"laserIsActiveChancePercent": 85, "laserIsActiveChancePercent": 85,
"forceStock": true, "forceStock": true,
"weaponSlotIdsToMakeRequired": [ "weaponSlotIdsToMakeRequired": ["mod_reciever"]
"mod_reciever"
]
}, },
"bossbully": { "bossbully": {
"nvgIsActiveChanceDayPercent": 10, "nvgIsActiveChanceDayPercent": 10,
@ -776,11 +771,9 @@
"scopeLimit": 1, "scopeLimit": 1,
"lightLaserLimit": 1 "lightLaserLimit": 1
}, },
"weaponSlotIdsToMakeRequired": [ "weaponSlotIdsToMakeRequired": ["mod_reciever"]
"mod_reciever"
]
}, },
"peacemaker": { "peacemaker": {
"nvgIsActiveChanceDayPercent": 10, "nvgIsActiveChanceDayPercent": 10,
"nvgIsActiveChanceNightPercent": 95, "nvgIsActiveChanceNightPercent": 95,
"faceShieldIsActiveChancePercent": 100, "faceShieldIsActiveChancePercent": 100,
@ -793,7 +786,7 @@
"lightLaserLimit": 1 "lightLaserLimit": 1
} }
}, },
"skier": { "skier": {
"nvgIsActiveChanceDayPercent": 10, "nvgIsActiveChanceDayPercent": 10,
"nvgIsActiveChanceNightPercent": 95, "nvgIsActiveChanceNightPercent": 95,
"faceShieldIsActiveChancePercent": 100, "faceShieldIsActiveChancePercent": 100,
@ -926,11 +919,7 @@
"laserIsActiveChancePercent": 85, "laserIsActiveChancePercent": 85,
"forceOnlyArmoredRigWhenNoArmor": true, "forceOnlyArmoredRigWhenNoArmor": true,
"filterPlatesByLevel": true, "filterPlatesByLevel": true,
"weaponSlotIdsToMakeRequired": [ "weaponSlotIdsToMakeRequired": ["mod_reciever", "mod_stock", "mod_muzzle"],
"mod_reciever",
"mod_stock",
"mod_muzzle"
],
"randomisation": [ "randomisation": [
{ {
"levelRange": { "levelRange": {
@ -1148,11 +1137,7 @@
"Scabbard": 100, "Scabbard": 100,
"TacticalVest": 90 "TacticalVest": 90
}, },
"randomisedArmorSlots": [ "randomisedArmorSlots": ["Headwear", "ArmorVest", "TacticalVest"],
"Headwear",
"ArmorVest",
"TacticalVest"
],
"randomisedWeaponModSlots": [ "randomisedWeaponModSlots": [
"mod_scope", "mod_scope",
"mod_scope_000", "mod_scope_000",
@ -1250,11 +1235,7 @@
"SecondPrimaryWeapon": 10, "SecondPrimaryWeapon": 10,
"FaceCover": 50 "FaceCover": 50
}, },
"randomisedArmorSlots": [ "randomisedArmorSlots": ["Headwear", "TacticalVest", "ArmorVest"],
"Headwear",
"TacticalVest",
"ArmorVest"
],
"randomisedWeaponModSlots": [ "randomisedWeaponModSlots": [
"mod_scope", "mod_scope",
"mod_scope_000", "mod_scope_000",
@ -1321,11 +1302,7 @@
"mod_muzzle_000": 95, "mod_muzzle_000": 95,
"mod_muzzle_001": 95 "mod_muzzle_001": 95
}, },
"randomisedArmorSlots": [ "randomisedArmorSlots": ["Headwear", "TacticalVest", "ArmorVest"],
"Headwear",
"TacticalVest",
"ArmorVest"
],
"randomisedWeaponModSlots": [ "randomisedWeaponModSlots": [
"mod_scope", "mod_scope",
"mod_scope_000", "mod_scope_000",
@ -1397,30 +1374,15 @@
"65392f611406374f82152ba5", "65392f611406374f82152ba5",
"653931da5db71d30ab1d6296" "653931da5db71d30ab1d6296"
], ],
"mod_nvg": [ "mod_nvg": ["5c11046cd174af02a012e42b"],
"5c11046cd174af02a012e42b" "mod_reciever": ["5d4405aaa4b9361e6a4e6bd3"],
], "mod_stock": ["5cde739cd7f00c0010373bd3"],
"mod_reciever": [ "mod_rear_sight": ["5a0ed824fcdbcb0176308b0d"],
"5d4405aaa4b9361e6a4e6bd3" "mod_front_sight": ["5a0f096dfcdbcb0176308b15"]
],
"mod_stock": [
"5cde739cd7f00c0010373bd3"
],
"mod_rear_sight": [
"5a0ed824fcdbcb0176308b0d"
],
"mod_front_sight": [
"5a0f096dfcdbcb0176308b15"
]
}, },
"cartridge": { "cartridge": {
"Caliber23x75": [ "Caliber23x75": ["5e85a9f4add9fe03027d9bf1"],
"5e85a9f4add9fe03027d9bf1" "Caliber127x55": ["5cadf6e5ae921500113bb973", "5cadf6ddae9215051e1c23b2"]
],
"Caliber127x55": [
"5cadf6e5ae921500113bb973",
"5cadf6ddae9215051e1c23b2"
]
} }
} }
], ],
@ -2677,10 +2639,7 @@
"569668774bdc2da2298b4568": 0, "569668774bdc2da2298b4568": 0,
"5696686a4bdc2da3298b456a": 0 "5696686a4bdc2da3298b456a": 0
}, },
"walletTplPool": [ "walletTplPool": ["5783c43d2459774bbe137486", "60b0f6c058e0b0481a09ad11"]
"5783c43d2459774bbe137486",
"60b0f6c058e0b0481a09ad11"
]
}, },
"currencyStackSize": { "currencyStackSize": {
"default": { "default": {
@ -2705,7 +2664,7 @@
"1": 8, "1": 8,
"2": 4, "2": 4,
"5": 4, "5": 4,
"10": 1 "10": 1
} }
}, },
"assault": { "assault": {
@ -2735,7 +2694,14 @@
} }
} }
}, },
"lowProfileGasBlockTpls": ["61702f1b67085e45ef140b26", "5dfa3d45dfc58d14537c20b0", "5bb20dcad4351e3bac1212da", "56eabcd4d2720b66698b4574", "6065dc8a132d4d12c81fd8e3", "55d4af3a4bdc2d972f8b456f"], "lowProfileGasBlockTpls": [
"61702f1b67085e45ef140b26",
"5dfa3d45dfc58d14537c20b0",
"5bb20dcad4351e3bac1212da",
"56eabcd4d2720b66698b4574",
"6065dc8a132d4d12c81fd8e3",
"55d4af3a4bdc2d972f8b456f"
],
"disableLootOnBotTypes": [], "disableLootOnBotTypes": [],
"assaultToBossConversion": { "assaultToBossConversion": {
"bossConvertEnabled": false, "bossConvertEnabled": false,
@ -2758,4 +2724,4 @@
} }
} }
} }
} }

View File

@ -27,11 +27,11 @@
"commandoFeatures": { "commandoFeatures": {
"giveCommandEnabled": true "giveCommandEnabled": true
}, },
"commandUseLimits": { "commandUseLimits": {
"StashRows": 15 "StashRows": 15
} }
}, },
"createNewProfileTypesBlacklist": [] "createNewProfileTypesBlacklist": []
}, },
"customWatermarkLocaleKeys": [] "customWatermarkLocaleKeys": []
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,4 +7,4 @@
"health": true, "health": true,
"effects": true "effects": true
} }
} }

View File

@ -8,5 +8,5 @@
"expCraftAmount": 10, "expCraftAmount": 10,
"overrideCraftTimeSeconds": -1, "overrideCraftTimeSeconds": -1,
"overrideBuildTimeSeconds": -1, "overrideBuildTimeSeconds": -1,
"updateProfileHideoutWhenActiveWithinMinutes": 90 "updateProfileHideoutWhenActiveWithinMinutes": 90
} }

View File

@ -1,9 +1,9 @@
{ {
"ip": "127.0.0.1", "ip": "127.0.0.1",
"port": 6969, "port": 6969,
"backendIp": "127.0.0.1", "backendIp": "127.0.0.1",
"backendPort": 6969, "backendPort": 6969,
"webSocketPingDelayMs": 90000, "webSocketPingDelayMs": 90000,
"logRequests": true, "logRequests": true,
"serverImagePathOverride": {} "serverImagePathOverride": {}
} }

View File

@ -13,15 +13,7 @@
"save": { "save": {
"loot": true "loot": true
}, },
"carExtracts": [ "carExtracts": ["Dorms V-Ex", "PP Exfil", "V-Ex_light", "South V-Ex", "E7_car", "Sandbox_VExit", "Shorl_V-Ex"],
"Dorms V-Ex",
"PP Exfil",
"V-Ex_light",
"South V-Ex",
"E7_car",
"Sandbox_VExit",
"Shorl_V-Ex"
],
"coopExtracts": [ "coopExtracts": [
"Interchange Cooperation", "Interchange Cooperation",
"tunnel_shared", "tunnel_shared",
@ -37,4 +29,4 @@
"pmcKillProbabilityForScavGain": 0.2, "pmcKillProbabilityForScavGain": 0.2,
"keepFiRSecureContainerOnDeath": false, "keepFiRSecureContainerOnDeath": false,
"playerScavHostileChancePercent": 15 "playerScavHostileChancePercent": 15
} }

View File

@ -3,17 +3,10 @@
"54cb50c76803fa8b248b4571": 75, "54cb50c76803fa8b248b4571": 75,
"54cb57776803fa99248b456e": 85 "54cb57776803fa99248b456e": 85
}, },
"blacklistedEquipment": [ "blacklistedEquipment": ["SpecialSlot1", "SpecialSlot2", "SpecialSlot3"],
"SpecialSlot1", "slotIdsToAlwaysRemove": ["cartridges", "patron_in_weapon"],
"SpecialSlot2",
"SpecialSlot3"
],
"slotIdsToAlwaysRemove": [
"cartridges",
"patron_in_weapon"
],
"returnTimeOverrideSeconds": 0, "returnTimeOverrideSeconds": 0,
"runIntervalSeconds": 600, "runIntervalSeconds": 600,
"minAttachmentRoublePriceToBeTaken": 2000, "minAttachmentRoublePriceToBeTaken": 2000,
"chanceNoAttachmentsTakenPercent": 10 "chanceNoAttachmentsTakenPercent": 10
} }

View File

@ -49,10 +49,7 @@
"6241c2c2117ad530666a5108", "6241c2c2117ad530666a5108",
"5580239d4bdc2de7118b4583" "5580239d4bdc2de7118b4583"
], ],
"lootableItemBlacklist": [ "lootableItemBlacklist": ["660bbc47c38b837877075e47", "660bc341c38b837877075e4c"],
"660bbc47c38b837877075e47",
"660bc341c38b837877075e4c"
],
"rewardItemBlacklist": [ "rewardItemBlacklist": [
"58ac60eb86f77401897560ff", "58ac60eb86f77401897560ff",
"5e997f0b86f7741ac73993e2", "5e997f0b86f7741ac73993e2",
@ -101,7 +98,7 @@
"6662e9f37fa79a6d83730fa0", "6662e9f37fa79a6d83730fa0",
"6662ea05f6259762c56f3189", "6662ea05f6259762c56f3189",
"6638a5474e92f038531e210e", "6638a5474e92f038531e210e",
"65ddcc9cfa85b9f17d0dfb07" "65ddcc9cfa85b9f17d0dfb07"
], ],
"bossItems": [ "bossItems": [
"6275303a9f372d6ea97f9ec7", "6275303a9f372d6ea97f9ec7",
@ -148,4 +145,4 @@
"63a898a328e385334e0640a5": 5000, "63a898a328e385334e0640a5": 5000,
"63a897c6b1ff6e29734fcc95": 10000 "63a897c6b1ff6e29734fcc95": 10000
} }
} }

View File

@ -35,4 +35,4 @@
"zh-*": "zh-cn", "zh-*": "zh-cn",
"es-*": "es-es" "es-*": "es-es"
} }
} }

View File

@ -16,7 +16,7 @@
"tarkovstreets": 3, "tarkovstreets": 3,
"terminal": 1, "terminal": 1,
"sandbox": 2.8, "sandbox": 2.8,
"sandbox_high": 2.8, "sandbox_high": 2.8,
"town": 0 "town": 0
}, },
"staticLootMultiplier": { "staticLootMultiplier": {
@ -36,7 +36,7 @@
"tarkovstreets": 1, "tarkovstreets": 1,
"terminal": 1, "terminal": 1,
"sandbox": 1, "sandbox": 1,
"sandbox_high": 1, "sandbox_high": 1,
"town": 1 "town": 1
}, },
"customWaves": { "customWaves": {
@ -56,9 +56,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -79,9 +77,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -104,9 +100,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -127,9 +121,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -152,9 +144,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -175,9 +165,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -198,9 +186,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -221,9 +207,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -244,9 +228,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -267,9 +249,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -292,9 +272,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -315,9 +293,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -338,9 +314,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -361,9 +335,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -384,9 +356,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -407,9 +377,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -432,9 +400,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -455,9 +421,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -478,9 +442,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "0" "BossEscortAmount": "0"
} }
], ],
@ -501,9 +463,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -524,9 +484,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -547,9 +505,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "0" "BossEscortAmount": "0"
} }
], ],
@ -572,9 +528,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -595,9 +549,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -620,9 +572,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -643,9 +593,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -668,9 +616,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -691,9 +637,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -714,9 +658,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -737,9 +679,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "1" "BossEscortAmount": "1"
} }
], ],
@ -762,9 +702,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -785,9 +723,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -795,7 +731,7 @@
"ChanceGroup": 0 "ChanceGroup": 0
} }
], ],
"sandbox_high": [ "sandbox_high": [
{ {
"sptId": "pmcBEARSandboxNormalSpawn", "sptId": "pmcBEARSandboxNormalSpawn",
"BossName": "pmcBEAR", "BossName": "pmcBEAR",
@ -810,9 +746,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcBEAR", "BossEscortType": "pmcBEAR",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -833,9 +767,7 @@
"Supports": [ "Supports": [
{ {
"BossEscortType": "pmcUSEC", "BossEscortType": "pmcUSEC",
"BossEscortDifficult": [ "BossEscortDifficult": ["normal"],
"normal"
],
"BossEscortAmount": "2" "BossEscortAmount": "2"
} }
], ],
@ -1047,36 +979,29 @@
} }
}, },
"openZones": { "openZones": {
"bigmap": [ "bigmap": ["ZoneScavBase"]
"ZoneScavBase"
]
}, },
"forcedLootSingleSpawnById": { "forcedLootSingleSpawnById": {
"bigmap": [ "bigmap": [
"5ac620eb86f7743a8e6e0da0", "5ac620eb86f7743a8e6e0da0",
"5939e5a786f77461f11c0098", "5939e5a786f77461f11c0098",
"64e74a3d4d49d23b2c39d319", "64e74a3d4d49d23b2c39d319",
"6614230055afee107f05e998" "6614230055afee107f05e998"
],
"interchange": [
"64e74a5ac2b4f829615ec336"
], ],
"interchange": ["64e74a5ac2b4f829615ec336"],
"lighthouse": [ "lighthouse": [
"6331bb0d1aa9f42b804997a6", "6331bb0d1aa9f42b804997a6",
"6398a0861c712b1e1d4dadf1", "6398a0861c712b1e1d4dadf1",
"6399f54b0a36db13c823ad21", "6399f54b0a36db13c823ad21",
"64e74a64aac4cd0a7264ecdf", "64e74a64aac4cd0a7264ecdf",
"661666458c2aa9cb1602503b" "661666458c2aa9cb1602503b"
],
"rezervbase": [
"64e74a4baac4cd0a7264ecdd",
"6398a072e301557ae24cec92"
], ],
"rezervbase": ["64e74a4baac4cd0a7264ecdd", "6398a072e301557ae24cec92"],
"shoreline": [ "shoreline": [
"64e74a534d49d23b2c39d31b", "64e74a534d49d23b2c39d31b",
"661421c7c1f2f548c50ee649", "661421c7c1f2f548c50ee649",
"6614217b6d9d5abcad0ff098", "6614217b6d9d5abcad0ff098",
"661423200d240a5f5d0f679b" "661423200d240a5f5d0f679b"
], ],
"tarkovstreets": [ "tarkovstreets": [
"638df4cc7b560b03794a18d2", "638df4cc7b560b03794a18d2",
@ -1101,14 +1026,8 @@
"657acb2ac900be5902191ac9", "657acb2ac900be5902191ac9",
"6582dbf0b8d7830efc45016f" "6582dbf0b8d7830efc45016f"
], ],
"laboratory": [ "laboratory": ["6398a4cfb5992f573c6562b3", "64e74a44c2b4f829615ec334"],
"6398a4cfb5992f573c6562b3", "sandbox": ["6575a6ca8778e96ded05a802", "6582bd252b50c61c565828e2"]
"64e74a44c2b4f829615ec334"
],
"sandbox": [
"6575a6ca8778e96ded05a802",
"6582bd252b50c61c565828e2"
]
}, },
"splitWaveIntoSingleSpawnsSettings": { "splitWaveIntoSingleSpawnsSettings": {
"enabled": true, "enabled": true,
@ -1131,15 +1050,7 @@
}, },
"fixEmptyBotWavesSettings": { "fixEmptyBotWavesSettings": {
"enabled": true, "enabled": true,
"ignoreMaps": [ "ignoreMaps": ["base", "develop", "hideout", "privatearea", "suburbs", "terminal", "town"]
"base",
"develop",
"hideout",
"privatearea",
"suburbs",
"terminal",
"town"
]
}, },
"fitLootIntoContainerAttempts": 3, "fitLootIntoContainerAttempts": 3,
"addOpenZonesToAllMaps": true, "addOpenZonesToAllMaps": true,
@ -1201,7 +1112,7 @@
"minFillStaticMagazinePercent": 50, "minFillStaticMagazinePercent": 50,
"allowDuplicateItemsInStaticContainers": true, "allowDuplicateItemsInStaticContainers": true,
"magazineLootHasAmmoChancePercent": 50, "magazineLootHasAmmoChancePercent": 50,
"staticMagazineLootHasAmmoChancePercent": 0, "staticMagazineLootHasAmmoChancePercent": 0,
"looseLootBlacklist": {}, "looseLootBlacklist": {},
"scavRaidTimeSettings": { "scavRaidTimeSettings": {
"settings": { "settings": {
@ -1424,4 +1335,4 @@
"mod_equipment": 5 "mod_equipment": 5
} }
} }
} }

View File

@ -9,4 +9,4 @@
"rezervbase": {}, "rezervbase": {},
"tarkovstreets": {} "tarkovstreets": {}
} }
} }

View File

@ -18,4 +18,4 @@
}, },
"questItems": true, "questItems": true,
"specialSlotItems": false "specialSlotItems": false
} }

View File

@ -12,4 +12,4 @@
"laboratory": false, "laboratory": false,
"rezervbase": false "rezervbase": false
} }
} }

View File

@ -856,19 +856,10 @@
"botTypeForLoot": "assault", "botTypeForLoot": "assault",
"equipmentBlacklist": { "equipmentBlacklist": {
"Headwear": [], "Headwear": [],
"ArmorVest": [ "ArmorVest": ["5df8a2ca86f7740bfe6df777"],
"5df8a2ca86f7740bfe6df777" "TacticalVest": ["572b7adb24597762ae139821"],
], "Backpack": ["56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
"TacticalVest": [ "FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
"572b7adb24597762ae139821"
],
"Backpack": [
"56e33634d2720bd8058b456b",
"5ab8ee7786f7742d8f33f0b9"
],
"FirstPrimaryWeapon": [
"5a38e6bac4a2826c6e06d79b"
],
"Holster": [], "Holster": [],
"Scabbard": [] "Scabbard": []
}, },
@ -955,22 +946,10 @@
"botTypeForLoot": "assault", "botTypeForLoot": "assault",
"equipmentBlacklist": { "equipmentBlacklist": {
"Headwear": [], "Headwear": [],
"ArmorVest": [ "ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
"59e7635f86f7742cbf2c1095", "TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
"5df8a2ca86f7740bfe6df777" "Backpack": ["56e33680d2720be2748b4576", "56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
], "FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
"TacticalVest": [
"5fd4c5477a8d854fa0105061",
"572b7adb24597762ae139821"
],
"Backpack": [
"56e33680d2720be2748b4576",
"56e33634d2720bd8058b456b",
"5ab8ee7786f7742d8f33f0b9"
],
"FirstPrimaryWeapon": [
"5a38e6bac4a2826c6e06d79b"
],
"Holster": [], "Holster": [],
"Scabbard": [] "Scabbard": []
}, },
@ -1056,22 +1035,10 @@
"botTypeForLoot": "assault", "botTypeForLoot": "assault",
"equipmentBlacklist": { "equipmentBlacklist": {
"Headwear": [], "Headwear": [],
"ArmorVest": [ "ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
"59e7635f86f7742cbf2c1095", "TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
"5df8a2ca86f7740bfe6df777" "Backpack": ["56e33680d2720be2748b4576", "56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
], "FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
"TacticalVest": [
"5fd4c5477a8d854fa0105061",
"572b7adb24597762ae139821"
],
"Backpack": [
"56e33680d2720be2748b4576",
"56e33634d2720bd8058b456b",
"5ab8ee7786f7742d8f33f0b9"
],
"FirstPrimaryWeapon": [
"5a38e6bac4a2826c6e06d79b"
],
"Holster": [], "Holster": [],
"Scabbard": [] "Scabbard": []
}, },
@ -1168,14 +1135,8 @@
"5f60e7788adaa7100c3adb49", "5f60e7788adaa7100c3adb49",
"5aa2a7e8e5b5b00016327c16" "5aa2a7e8e5b5b00016327c16"
], ],
"ArmorVest": [ "ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
"59e7635f86f7742cbf2c1095", "TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
"5df8a2ca86f7740bfe6df777"
],
"TacticalVest": [
"5fd4c5477a8d854fa0105061",
"572b7adb24597762ae139821"
],
"Backpack": [ "Backpack": [
"56e33680d2720be2748b4576", "56e33680d2720be2748b4576",
"56e33634d2720bd8058b456b", "56e33634d2720bd8058b456b",
@ -1183,14 +1144,8 @@
"5ab8f04f86f774585f4237d8", "5ab8f04f86f774585f4237d8",
"5f5e45cc5021ce62144be7aa" "5f5e45cc5021ce62144be7aa"
], ],
"FirstPrimaryWeapon": [ "FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
"5a38e6bac4a2826c6e06d79b" "Holster": ["5448bd6b4bdc2dfc2f8b4569", "579204f224597773d619e051", "571a12c42459771f627b58a0"],
],
"Holster": [
"5448bd6b4bdc2dfc2f8b4569",
"579204f224597773d619e051",
"571a12c42459771f627b58a0"
],
"Scabbard": [] "Scabbard": []
}, },
"modifiers": { "modifiers": {
@ -1271,4 +1226,4 @@
} }
} }
} }
} }

View File

@ -66,7 +66,7 @@
"6391fcf5744e45201147080f", "6391fcf5744e45201147080f",
"614451b71e5874611e2c7ae5", "614451b71e5874611e2c7ae5",
"6540d2162ae6d96b540afcaf", "6540d2162ae6d96b540afcaf",
"65ca457b4aafb5d7fc0dcb5d" "65ca457b4aafb5d7fc0dcb5d"
] ]
}, },
"pocketLoot": { "pocketLoot": {
@ -120,8 +120,8 @@
"57864c8c245977548867e7f1", "57864c8c245977548867e7f1",
"6087e570b998180e9f76dc24", "6087e570b998180e9f76dc24",
"6391fcf5744e45201147080f", "6391fcf5744e45201147080f",
"65ca457b4aafb5d7fc0dcb5d", "65ca457b4aafb5d7fc0dcb5d",
"660bbc98c38b837877075e4a" "660bbc98c38b837877075e4a"
] ]
}, },
"backpackLoot": { "backpackLoot": {
@ -179,8 +179,8 @@
"6391fcf5744e45201147080f", "6391fcf5744e45201147080f",
"614451b71e5874611e2c7ae5", "614451b71e5874611e2c7ae5",
"6540d2162ae6d96b540afcaf", "6540d2162ae6d96b540afcaf",
"65ca457b4aafb5d7fc0dcb5d", "65ca457b4aafb5d7fc0dcb5d",
"660bbc98c38b837877075e4a" "660bbc98c38b837877075e4a"
] ]
}, },
"useDifficultyOverride": false, "useDifficultyOverride": false,
@ -212,7 +212,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"factory4_night": { "factory4_night": {
"bossKilla": 2, "bossKilla": 2,
@ -233,7 +233,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"bigmap": { "bigmap": {
"bossKilla": 2, "bossKilla": 2,
@ -254,7 +254,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"laboratory": { "laboratory": {
"bossKilla": 2, "bossKilla": 2,
@ -275,7 +275,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"woods": { "woods": {
"bossKilla": 2, "bossKilla": 2,
@ -297,7 +297,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"interchange": { "interchange": {
"bossKilla": 2, "bossKilla": 2,
@ -318,7 +318,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"lighthouse": { "lighthouse": {
"bossKilla": 2, "bossKilla": 2,
@ -339,7 +339,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"rezervbase": { "rezervbase": {
"bossKilla": 2, "bossKilla": 2,
@ -360,7 +360,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"shoreline": { "shoreline": {
"bossKilla": 2, "bossKilla": 2,
@ -381,7 +381,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"tarkovstreets": { "tarkovstreets": {
"bossKilla": 2, "bossKilla": 2,
@ -402,7 +402,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"sandbox": { "sandbox": {
"bossKilla": 2, "bossKilla": 2,
@ -425,7 +425,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 1, "crazyAssaultEvent": 1,
"pmcBot": 7, "pmcBot": 7,
"pmcBEAR": 2 "pmcBEAR": 2
}, },
"sandbox_high": { "sandbox_high": {
"bossKilla": 2, "bossKilla": 2,
@ -448,7 +448,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 1, "crazyAssaultEvent": 1,
"pmcBot": 7, "pmcBot": 7,
"pmcBEAR": 2 "pmcBEAR": 2
} }
}, },
"pmcusec": { "pmcusec": {
@ -471,7 +471,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"factory4_night": { "factory4_night": {
"bossKilla": 2, "bossKilla": 2,
@ -492,7 +492,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"bigmap": { "bigmap": {
"bossKilla": 0, "bossKilla": 0,
@ -513,7 +513,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 2, "crazyAssaultEvent": 2,
"pmcBot": 6, "pmcBot": 6,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"laboratory": { "laboratory": {
"bossKilla": 2, "bossKilla": 2,
@ -534,7 +534,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"woods": { "woods": {
"bossKilla": 2, "bossKilla": 2,
@ -556,7 +556,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"interchange": { "interchange": {
"bossKilla": 2, "bossKilla": 2,
@ -577,7 +577,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"lighthouse": { "lighthouse": {
"bossKilla": 2, "bossKilla": 2,
@ -598,7 +598,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"rezervbase": { "rezervbase": {
"bossKilla": 2, "bossKilla": 2,
@ -619,7 +619,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"shoreline": { "shoreline": {
"bossKilla": 2, "bossKilla": 2,
@ -640,7 +640,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"tarkovstreets": { "tarkovstreets": {
"bossKilla": 2, "bossKilla": 2,
@ -661,7 +661,7 @@
"arenaFighterEvent": 0, "arenaFighterEvent": 0,
"crazyAssaultEvent": 3, "crazyAssaultEvent": 3,
"pmcBot": 5, "pmcBot": 5,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"sandbox": { "sandbox": {
"bossKilla": 2, "bossKilla": 2,
@ -684,7 +684,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 1, "crazyAssaultEvent": 1,
"pmcBot": 7, "pmcBot": 7,
"pmcUSEC": 2 "pmcUSEC": 2
}, },
"sandbox_high": { "sandbox_high": {
"bossKilla": 2, "bossKilla": 2,
@ -707,7 +707,7 @@
"arenaFighterEvent": 1, "arenaFighterEvent": 1,
"crazyAssaultEvent": 1, "crazyAssaultEvent": 1,
"pmcBot": 7, "pmcBot": 7,
"pmcUSEC": 2 "pmcUSEC": 2
} }
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@ -276,10 +276,7 @@
}, },
"armor": { "armor": {
"removeRemovablePlateChance": 40, "removeRemovablePlateChance": 40,
"plateSlotIdToRemovePool": [ "plateSlotIdToRemovePool": ["front_plate", "back_plate"]
"front_plate",
"back_plate"
]
}, },
"itemPriceMultiplier": { "itemPriceMultiplier": {
"5737292724597765e5728562": 6, "5737292724597765e5728562": 6,
@ -303,7 +300,7 @@
"62e910aaf957f2915e0a5e36", "62e910aaf957f2915e0a5e36",
"5447e1d04bdc2dff2f8b4567", "5447e1d04bdc2dff2f8b4567",
"57bef4c42459772e8d35a53b", "57bef4c42459772e8d35a53b",
"55802f4a4bdc2ddb688b4569" "55802f4a4bdc2ddb688b4569"
], ],
"removeSeasonalItemsWhenNotInEvent": true, "removeSeasonalItemsWhenNotInEvent": true,
"blacklist": { "blacklist": {
@ -314,10 +311,7 @@
"traderItems": false, "traderItems": false,
"armorPlate": { "armorPlate": {
"maxProtectionLevel": 4, "maxProtectionLevel": 4,
"ignoreSlots": [ "ignoreSlots": ["right_side_plate", "left_side_plate"]
"right_side_plate",
"left_side_plate"
]
}, },
"enableCustomItemCategoryList": false, "enableCustomItemCategoryList": false,
"customItemCategoryList": [] "customItemCategoryList": []
@ -337,4 +331,4 @@
} }
} }
} }
} }

View File

@ -12,9 +12,9 @@
"trader": 0.6 "trader": 0.6
}, },
"weaponTreatment": { "weaponTreatment": {
"critSuccessChance": 0.10, "critSuccessChance": 0.1,
"critSuccessAmount": 4, "critSuccessAmount": 4,
"critFailureChance": 0.10, "critFailureChance": 0.1,
"critFailureAmount": 4, "critFailureAmount": 4,
"pointGainMultiplier": 0.6 "pointGainMultiplier": 0.6
}, },
@ -163,7 +163,7 @@
"MalfunctionProtections": { "MalfunctionProtections": {
"valuesMinMax": { "valuesMinMax": {
"min": 0.75, "min": 0.75,
"max": 0.90 "max": 0.9
}, },
"activeDurabilityPercentMinMax": { "activeDurabilityPercentMinMax": {
"min": 75, "min": 75,
@ -183,4 +183,4 @@
} }
} }
} }
} }

View File

@ -86,9 +86,7 @@
"ammoRewardChancePercent": 10, "ammoRewardChancePercent": 10,
"minStackSize": 10, "minStackSize": 10,
"ammoRewardBlacklist": { "ammoRewardBlacklist": {
"common": [ "common": ["59e690b686f7746c9f75e848"],
"59e690b686f7746c9f75e848"
],
"rare": [], "rare": [],
"superrare": [] "superrare": []
}, },
@ -110,4 +108,4 @@
"allowMultipleMoneyRewardsPerRarity": false, "allowMultipleMoneyRewardsPerRarity": false,
"allowMultipleAmmoRewardsPerRarity": true, "allowMultipleAmmoRewardsPerRarity": true,
"allowBossItemsAsRewards": false "allowBossItemsAsRewards": false
} }

View File

@ -721,4 +721,4 @@
"endMonth": "2" "endMonth": "2"
} }
] ]
} }

View File

@ -202,11 +202,11 @@
} }
}, },
"chancePlateExistsInArmorPercent": { "chancePlateExistsInArmorPercent": {
"3": 95, "3": 95,
"4": 75, "4": 75,
"5": 25, "5": 25,
"6": 10 "6": 10
}, },
"armorMaxDurabilityPercentMinMax": { "armorMaxDurabilityPercentMinMax": {
"current": { "current": {
"min": 50, "min": 50,

View File

@ -2,94 +2,32 @@
"acceleration": 7, "acceleration": 7,
"weather": { "weather": {
"clouds": { "clouds": {
"values": [ "values": [-1.5, -1, 0, 0.5, 1, 1.5],
-1.5, "weights": [60, 50, 15, 5, 4, 3]
-1,
0,
0.5,
1,
1.5
],
"weights": [
60,
50,
15,
5,
4,
3
]
}, },
"windSpeed": { "windSpeed": {
"values": [ "values": [0, 1, 2, 3],
0, "weights": [4, 3, 2, 1]
1,
2,
3
],
"weights": [
4,
3,
2,
1
]
}, },
"windDirection": { "windDirection": {
"values": [ "values": [1, 2, 3, 4, 5, 6, 7, 8],
1, "weights": [1, 1, 1, 1, 1, 1, 1, 1]
2,
3,
4,
5,
6,
7,
8
],
"weights": [
1,
1,
1,
1,
1,
1,
1,
1
]
}, },
"windGustiness": { "windGustiness": {
"min": 0, "min": 0,
"max": 1 "max": 1
}, },
"rain": { "rain": {
"values": [ "values": [1, 2, 3],
1, "weights": [25, 1, 1]
2,
3
],
"weights": [
25,
1,
1
]
}, },
"rainIntensity": { "rainIntensity": {
"min": 0, "min": 0,
"max": 1 "max": 1
}, },
"fog": { "fog": {
"values": [ "values": [0.002, 0.004, 0.008, 0.012, 0.087],
0.002, "weights": [20, 8, 5, 5, 1]
0.004,
0.008,
0.012,
0.087
],
"weights": [
20,
8,
5,
5,
1
]
}, },
"temp": { "temp": {
"min": 0, "min": 0,
@ -100,55 +38,55 @@
"max": 764 "max": 764
} }
}, },
"seasonDates": [ "seasonDates": [
{ {
"seasonType": 0, "seasonType": 0,
"name": "SUMMER", "name": "SUMMER",
"startDay": "2", "startDay": "2",
"startMonth": "6", "startMonth": "6",
"endDay": "1", "endDay": "1",
"endMonth": "8" "endMonth": "8"
}, },
{ {
"seasonType": 1, "seasonType": 1,
"name": "AUTUMN", "name": "AUTUMN",
"startDay": "2", "startDay": "2",
"startMonth": "8", "startMonth": "8",
"endDay": "1", "endDay": "1",
"endMonth": "11" "endMonth": "11"
}, },
{ {
"seasonType": 2, "seasonType": 2,
"name": "WINTER_END", "name": "WINTER_END",
"startDay": "2", "startDay": "2",
"startMonth": "11", "startMonth": "11",
"endDay": "31", "endDay": "31",
"endMonth": "12" "endMonth": "12"
}, },
{ {
"seasonType": 2, "seasonType": 2,
"name": "WINTER_START", "name": "WINTER_START",
"startDay": "1", "startDay": "1",
"startMonth": "1", "startMonth": "1",
"endDay": "24", "endDay": "24",
"endMonth": "3" "endMonth": "3"
}, },
{ {
"seasonType": 3, "seasonType": 3,
"name": "SPRING", "name": "SPRING",
"startDay": "25", "startDay": "25",
"startMonth": "3", "startMonth": "3",
"endDay": "1", "endDay": "1",
"endMonth": "6" "endMonth": "6"
}, },
{ {
"seasonType": 4, "seasonType": 4,
"name": "STORM", "name": "STORM",
"startDay": "24", "startDay": "24",
"startMonth": "10", "startMonth": "10",
"endDay": "4", "endDay": "4",
"endMonth": "11" "endMonth": "11"
} }
], ],
"overrideSeason": null "overrideSeason": null
} }

View File

@ -34,16 +34,16 @@
"enabled": true, "enabled": true,
"formatWithErrors": false, "formatWithErrors": false,
"ignore": [ "ignore": [
"**/.git", "**/.git",
"**/.pkg-cache", "**/.pkg-cache",
"**/.vscode", "**/.vscode",
"**/build", "**/build",
"**/node_modules", "**/node_modules",
"**/types", "**/types",
"**/tests/__cache__", "**/tests/__cache__",
"**/tests/__coverage__", "**/tests/__coverage__",
"**/.editorconfig" "**/.editorconfig"
], ],
"attributePosition": "auto", "attributePosition": "auto",
"indentStyle": "space", "indentStyle": "space",
"indentWidth": 4, "indentWidth": 4,

View File

@ -37,8 +37,7 @@ const licenseFile = "../LICENSE.md";
/** /**
* Transpile src files into Javascript with SWC * Transpile src files into Javascript with SWC
*/ */
const compile = async () => const compile = async () => {
{
// Compile TypeScript files using SWC // Compile TypeScript files using SWC
await exec("npx swc src -d obj", { stdio: "inherit" }); await exec("npx swc src -d obj", { stdio: "inherit" });
@ -46,29 +45,23 @@ const compile = async () =>
const srcDir = path.join("obj", "src"); const srcDir = path.join("obj", "src");
const destDir = path.join("obj"); const destDir = path.join("obj");
try try {
{
const entities = await fs.readdir(srcDir); const entities = await fs.readdir(srcDir);
for (const entity of entities) for (const entity of entities) {
{
const srcPath = path.join(srcDir, entity); const srcPath = path.join(srcDir, entity);
const destPath = path.join(destDir, entity); const destPath = path.join(destDir, entity);
await fs.move(srcPath, destPath, { overwrite: true }); await fs.move(srcPath, destPath, { overwrite: true });
} }
// After moving all contents, remove the now-empty /obj/src directory // After moving all contents, remove the now-empty /obj/src directory
await fs.remove(srcDir); await fs.remove(srcDir);
} } catch (error) {
catch (error)
{
console.error("An error occurred during the merge operation:", error); console.error("An error occurred during the merge operation:", error);
} }
}; };
// Packaging // Packaging
const fetchPackageImage = async () => const fetchPackageImage = async () => {
{ try {
try
{
const output = "./.pkg-cache/v3.5"; const output = "./.pkg-cache/v3.5";
const fetchedPkg = await pkgfetch.need({ const fetchedPkg = await pkgfetch.need({
arch: targetArch, arch: targetArch,
@ -79,18 +72,14 @@ const fetchPackageImage = async () =>
console.log(`fetched node binary at ${fetchedPkg}`); console.log(`fetched node binary at ${fetchedPkg}`);
const builtPkg = fetchedPkg.replace("node", "built"); const builtPkg = fetchedPkg.replace("node", "built");
await fs.copyFile(fetchedPkg, builtPkg); await fs.copyFile(fetchedPkg, builtPkg);
} } catch (e) {
catch (e)
{
console.error(`Error while fetching and patching package image: ${e.message}`); console.error(`Error while fetching and patching package image: ${e.message}`);
console.error(e.stack); console.error(e.stack);
} }
}; };
const updateBuildProperties = async () => const updateBuildProperties = async () => {
{ if (targetPlatform !== "win32") {
if (targetPlatform !== "win32")
{
// can't modify executable's resource on non-windows build // can't modify executable's resource on non-windows build
return; return;
} }
@ -110,12 +99,15 @@ const updateBuildProperties = async () =>
const vi = ResEdit.Resource.VersionInfo.fromEntries(res.entries)[0]; const vi = ResEdit.Resource.VersionInfo.fromEntries(res.entries)[0];
vi.setStringValues({ lang: 1033, codepage: 1200 }, { vi.setStringValues(
ProductName: manifest.author, { lang: 1033, codepage: 1200 },
FileDescription: manifest.description, {
CompanyName: manifest.name, ProductName: manifest.author,
LegalCopyright: manifest.license, FileDescription: manifest.description,
}); CompanyName: manifest.name,
LegalCopyright: manifest.license,
},
);
vi.removeStringValue({ lang: 1033, codepage: 1200 }, "OriginalFilename"); vi.removeStringValue({ lang: 1033, codepage: 1200 }, "OriginalFilename");
vi.removeStringValue({ lang: 1033, codepage: 1200 }, "InternalName"); vi.removeStringValue({ lang: 1033, codepage: 1200 }, "InternalName");
vi.setFileVersion(...manifest.version.split(".").map(Number)); vi.setFileVersion(...manifest.version.split(".").map(Number));
@ -129,15 +121,14 @@ const updateBuildProperties = async () =>
* Copy various asset files to the destination directory * Copy various asset files to the destination directory
*/ */
const copyAssets = () => const copyAssets = () =>
gulp.src(["assets/**/*.json", "assets/**/*.json5", "assets/**/*.png", "assets/**/*.jpg", "assets/**/*.ico"]).pipe( gulp
gulp.dest(dataDir), .src(["assets/**/*.json", "assets/**/*.json5", "assets/**/*.png", "assets/**/*.jpg", "assets/**/*.ico"])
); .pipe(gulp.dest(dataDir));
/** /**
* Download pnpm executable * Download pnpm executable
*/ */
const downloadPnpm = async () => const downloadPnpm = async () => {
{
// Please ensure that the @pnpm/exe version in devDependencies is pinned to a specific version. If it's not, the // Please ensure that the @pnpm/exe version in devDependencies is pinned to a specific version. If it's not, the
// following task will download *all* versions that are compatible with the semver range specified. // following task will download *all* versions that are compatible with the semver range specified.
const pnpmVersion = manifest.devDependencies["@pnpm/exe"]; const pnpmVersion = manifest.devDependencies["@pnpm/exe"];
@ -145,7 +136,9 @@ const downloadPnpm = async () =>
const npmResult = await exec(`npm view ${pnpmPackageName}@${pnpmVersion} dist.tarball`, { stdout: "pipe" }); const npmResult = await exec(`npm view ${pnpmPackageName}@${pnpmVersion} dist.tarball`, { stdout: "pipe" });
const pnpmLink = npmResult.stdout.trim(); const pnpmLink = npmResult.stdout.trim();
console.log(`Downloading pnpm binary from ${pnpmLink}`); console.log(`Downloading pnpm binary from ${pnpmLink}`);
download(pnpmLink).pipe(decompress({ strip: 1 })).pipe(gulp.dest(path.join(dataDir, "@pnpm", "exe"))); download(pnpmLink)
.pipe(decompress({ strip: 1 }))
.pipe(gulp.dest(path.join(dataDir, "@pnpm", "exe")));
}; };
/** /**
@ -156,10 +149,8 @@ const copyLicense = () => gulp.src([licenseFile]).pipe(rename("LICENSE-Server.tx
/** /**
* Writes the latest build data to the core.json and build.json configuration files. * Writes the latest build data to the core.json and build.json configuration files.
*/ */
const writeBuildDataToJSON = async () => const writeBuildDataToJSON = async () => {
{ try {
try
{
// Fetch the latest Git commit hash // Fetch the latest Git commit hash
const gitResult = await exec("git rev-parse HEAD", { stdout: "pipe" }); const gitResult = await exec("git rev-parse HEAD", { stdout: "pipe" });
@ -180,9 +171,7 @@ const writeBuildDataToJSON = async () =>
buildInfo.buildTime = coreParsed.buildTime; buildInfo.buildTime = coreParsed.buildTime;
buildInfo.sptVersion = coreParsed.sptVersion; buildInfo.sptVersion = coreParsed.sptVersion;
await fs.writeFile(buildJsonPath, JSON.stringify(buildInfo, null, 4)); await fs.writeFile(buildJsonPath, JSON.stringify(buildInfo, null, 4));
} } catch (error) {
catch (error)
{
throw new Error(`Failed to write commit hash to core.json: ${error.message}`); throw new Error(`Failed to write commit hash to core.json: ${error.message}`);
} }
}; };
@ -190,8 +179,7 @@ const writeBuildDataToJSON = async () =>
/** /**
* Create a hash file for asset checks * Create a hash file for asset checks
*/ */
const createHashFile = async () => const createHashFile = async () => {
{
const hashFileDir = path.resolve(dataDir, "checks.dat"); const hashFileDir = path.resolve(dataDir, "checks.dat");
const assetData = await loadRecursiveAsync("assets/"); const assetData = await loadRecursiveAsync("assets/");
const assetDataString = Buffer.from(JSON.stringify(assetData), "utf-8").toString("base64"); const assetDataString = Buffer.from(JSON.stringify(assetData), "utf-8").toString("base64");
@ -218,18 +206,13 @@ const cleanCompiled = async () => await fs.rm("./obj", { recursive: true, force:
* @param {string[]} files * @param {string[]} files
* @returns {Promise<string[]>} * @returns {Promise<string[]>}
*/ */
const getJSONFiles = async (dir, files = []) => const getJSONFiles = async (dir, files = []) => {
{
const fileList = await fs.readdir(dir); const fileList = await fs.readdir(dir);
for (const file of fileList) for (const file of fileList) {
{
const name = path.resolve(dir, file); const name = path.resolve(dir, file);
if ((await fs.stat(name)).isDirectory()) if ((await fs.stat(name)).isDirectory()) {
{
getJSONFiles(name, files); getJSONFiles(name, files);
} } else if (name.slice(-5) === ".json") {
else if (name.slice(-5) === ".json")
{
files.push(name); files.push(name);
} }
} }
@ -239,21 +222,16 @@ const getJSONFiles = async (dir, files = []) =>
/** /**
* Goes through every json file in assets and makes sure they're valid json. * Goes through every json file in assets and makes sure they're valid json.
*/ */
const validateJSONs = async () => const validateJSONs = async () => {
{
const assetsPath = path.resolve("assets"); const assetsPath = path.resolve("assets");
const jsonFileList = await getJSONFiles(assetsPath); const jsonFileList = await getJSONFiles(assetsPath);
let jsonFileInProcess = ""; let jsonFileInProcess = "";
try try {
{ for (const jsonFile of jsonFileList) {
for (const jsonFile of jsonFileList)
{
jsonFileInProcess = jsonFile; jsonFileInProcess = jsonFile;
JSON.parse(await fs.readFile(jsonFile)); JSON.parse(await fs.readFile(jsonFile));
} }
} } catch (error) {
catch (error)
{
throw new Error(`${error.message} | ${jsonFileInProcess}`); throw new Error(`${error.message} | ${jsonFileInProcess}`);
} }
}; };
@ -264,8 +242,7 @@ const validateJSONs = async () =>
* @param {crypto.BinaryLike} data * @param {crypto.BinaryLike} data
* @returns {string} * @returns {string}
*/ */
const generateHashForData = (data) => const generateHashForData = (data) => {
{
const hashSum = crypto.createHash("sha1"); const hashSum = crypto.createHash("sha1");
hashSum.update(data); hashSum.update(data);
return hashSum.digest("hex"); return hashSum.digest("hex");
@ -277,21 +254,16 @@ const generateHashForData = (data) =>
* @param {fs.PathLike} filepath * @param {fs.PathLike} filepath
* @returns {} * @returns {}
*/ */
const loadRecursiveAsync = async (filepath) => const loadRecursiveAsync = async (filepath) => {
{
const result = {}; const result = {};
const filesList = await fs.readdir(filepath); const filesList = await fs.readdir(filepath);
for (const file of filesList) for (const file of filesList) {
{
const curPath = path.parse(path.join(filepath, file)); const curPath = path.parse(path.join(filepath, file));
if ((await fs.stat(path.join(curPath.dir, curPath.base))).isDirectory()) if ((await fs.stat(path.join(curPath.dir, curPath.base))).isDirectory()) {
{
result[curPath.name] = loadRecursiveAsync(`${filepath}${file}/`); result[curPath.name] = loadRecursiveAsync(`${filepath}${file}/`);
} } else if (curPath.ext === ".json") {
else if (curPath.ext === ".json")
{
result[curPath.name] = generateHashForData(await fs.readFile(`${filepath}${file}`)); result[curPath.name] = generateHashForData(await fs.readFile(`${filepath}${file}`));
} }
} }
@ -299,8 +271,7 @@ const loadRecursiveAsync = async (filepath) =>
// set all loadRecursive to be executed asynchronously // set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result); const resEntries = Object.entries(result);
const resResolved = await Promise.all(resEntries.map((ent) => ent[1])); const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
for (let resIdx = 0; resIdx < resResolved.length; resIdx++) for (let resIdx = 0; resIdx < resResolved.length; resIdx++) {
{
resEntries[resIdx][1] = resResolved[resIdx]; resEntries[resIdx][1] = resResolved[resIdx];
} }
@ -309,8 +280,7 @@ const loadRecursiveAsync = async (filepath) =>
}; };
// Main Tasks Generation // Main Tasks Generation
const build = (packagingType) => const build = (packagingType) => {
{
const anonPackaging = () => packaging(entries[packagingType]); const anonPackaging = () => packaging(entries[packagingType]);
anonPackaging.displayName = `packaging-${packagingType}`; anonPackaging.displayName = `packaging-${packagingType}`;
const tasks = [ const tasks = [
@ -327,11 +297,9 @@ const build = (packagingType) =>
}; };
// Packaging Arguments // Packaging Arguments
const packaging = async (entry) => const packaging = async (entry) => {
{
const target = `${nodeVersion}-${targetPlatform}-${targetArch}`; const target = `${nodeVersion}-${targetPlatform}-${targetArch}`;
try try {
{
await pkg.exec([ await pkg.exec([
entry, entry,
"--compress", "--compress",
@ -344,9 +312,7 @@ const packaging = async (entry) =>
pkgConfig, pkgConfig,
"--public", "--public",
]); ]);
} } catch (error) {
catch (error)
{
console.error(`Error occurred during packaging: ${error}`); console.error(`Error occurred during packaging: ${error}`);
} }
}; };
@ -361,8 +327,7 @@ gulp.task(
"run:debug", "run:debug",
async () => await exec("ts-node-dev -r tsconfig-paths/register src/ide/TestEntry.ts", { stdio }), async () => await exec("ts-node-dev -r tsconfig-paths/register src/ide/TestEntry.ts", { stdio }),
); );
gulp.task("run:profiler", async () => gulp.task("run:profiler", async () => {
{
await cleanCompiled(); await cleanCompiled();
await compile(); await compile();
await exec("node --prof --inspect --trace-warnings obj/ide/TestEntry.js", { stdio }); await exec("node --prof --inspect --trace-warnings obj/ide/TestEntry.js", { stdio });

View File

@ -1,11 +1,6 @@
{ {
"pkg": { "pkg": {
"scripts": [ "scripts": ["obj/**/*.js"],
"obj/**/*.js" "assets": ["obj/**/*.js.map", "package.json"]
],
"assets": [
"obj/**/*.js.map",
"package.json"
]
} }
} }

View File

@ -3,23 +3,19 @@ import { ILogger } from "@spt/models/spt/utils/ILogger";
import { AsyncQueue } from "@spt/utils/AsyncQueue"; import { AsyncQueue } from "@spt/utils/AsyncQueue";
import { WinstonMainLogger } from "@spt/utils/logging/WinstonMainLogger"; import { WinstonMainLogger } from "@spt/utils/logging/WinstonMainLogger";
export class ErrorHandler export class ErrorHandler {
{
private logger: ILogger; private logger: ILogger;
private readLine: readline.Interface; private readLine: readline.Interface;
constructor() constructor() {
{
this.logger = new WinstonMainLogger(new AsyncQueue()); this.logger = new WinstonMainLogger(new AsyncQueue());
this.readLine = readline.createInterface({ input: process.stdin, output: process.stdout }); this.readLine = readline.createInterface({ input: process.stdin, output: process.stdout });
} }
public handleCriticalError(err: Error): void public handleCriticalError(err: Error): void {
{
this.logger.error("The application had a critical error and failed to run"); this.logger.error("The application had a critical error and failed to run");
this.logger.error(`Exception produced: ${err.name}`); this.logger.error(`Exception produced: ${err.name}`);
if (err.stack) if (err.stack) {
{
this.logger.error(`\nStacktrace:\n${err.stack}`); this.logger.error(`\nStacktrace:\n${err.stack}`);
} }

View File

@ -1,25 +1,21 @@
import { container } from "tsyringe";
import { Container } from "@spt/di/Container";
import { ErrorHandler } from "@spt/ErrorHandler"; import { ErrorHandler } from "@spt/ErrorHandler";
import { Container } from "@spt/di/Container";
import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
import { App } from "@spt/utils/App"; import { App } from "@spt/utils/App";
import { Watermark } from "@spt/utils/Watermark"; import { Watermark } from "@spt/utils/Watermark";
import { container } from "tsyringe";
export class Program export class Program {
{
private errorHandler: ErrorHandler; private errorHandler: ErrorHandler;
constructor() constructor() {
{
// set window properties // set window properties
process.stdout.setEncoding("utf8"); process.stdout.setEncoding("utf8");
process.title = "SPT Server"; process.title = "SPT Server";
this.errorHandler = new ErrorHandler(); this.errorHandler = new ErrorHandler();
} }
public async start(): Promise<void> public async start(): Promise<void> {
{ try {
try
{
Container.registerTypes(container); Container.registerTypes(container);
const childContainer = container.createChildContainer(); const childContainer = container.createChildContainer();
const watermark = childContainer.resolve<Watermark>("Watermark"); const watermark = childContainer.resolve<Watermark>("Watermark");
@ -31,9 +27,7 @@ export class Program
Container.registerPostLoadTypes(container, childContainer); Container.registerPostLoadTypes(container, childContainer);
childContainer.resolve<App>("App").load(); childContainer.resolve<App>("App").load();
} } catch (err: any) {
catch (err: any)
{
this.errorHandler.handleCriticalError(err instanceof Error ? err : new Error(err)); this.errorHandler.handleCriticalError(err instanceof Error ? err : new Error(err));
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { AchievementController } from "@spt/controllers/AchievementController"; import { AchievementController } from "@spt/controllers/AchievementController";
import { ProfileController } from "@spt/controllers/ProfileController"; import { ProfileController } from "@spt/controllers/ProfileController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -6,16 +5,15 @@ import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyRespo
import { ICompletedAchievementsResponse } from "@spt/models/eft/profile/ICompletedAchievementsResponse"; import { ICompletedAchievementsResponse } from "@spt/models/eft/profile/ICompletedAchievementsResponse";
import { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse"; import { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class AchievementCallbacks export class AchievementCallbacks {
{
constructor( constructor(
@inject("AchievementController") protected achievementController: AchievementController, @inject("AchievementController") protected achievementController: AchievementController,
@inject("ProfileController") protected profileController: ProfileController, @inject("ProfileController") protected profileController: ProfileController,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
) ) {}
{}
/** /**
* Handle client/achievement/list * Handle client/achievement/list
@ -24,8 +22,7 @@ export class AchievementCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetAchievementsResponse> ): IGetBodyResponseData<IGetAchievementsResponse> {
{
return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID)); return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID));
} }
@ -36,8 +33,7 @@ export class AchievementCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ICompletedAchievementsResponse> ): IGetBodyResponseData<ICompletedAchievementsResponse> {
{
return this.httpResponse.getBody(this.achievementController.getAchievementStatistics(sessionID)); return this.httpResponse.getBody(this.achievementController.getAchievementStatistics(sessionID));
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { BotController } from "@spt/controllers/BotController"; import { BotController } from "@spt/controllers/BotController";
import { IGenerateBotsRequestData } from "@spt/models/eft/bot/IGenerateBotsRequestData"; import { IGenerateBotsRequestData } from "@spt/models/eft/bot/IGenerateBotsRequestData";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -6,23 +5,21 @@ import { IBotBase } from "@spt/models/eft/common/tables/IBotBase";
import { Difficulties } from "@spt/models/eft/common/tables/IBotType"; import { Difficulties } from "@spt/models/eft/common/tables/IBotType";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class BotCallbacks export class BotCallbacks {
{
constructor( constructor(
@inject("BotController") protected botController: BotController, @inject("BotController") protected botController: BotController,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
) ) {}
{}
/** /**
* Handle singleplayer/settings/bot/limit * Handle singleplayer/settings/bot/limit
* Is called by client to define each bot roles wave limit * Is called by client to define each bot roles wave limit
* @returns string * @returns string
*/ */
public getBotLimit(url: string, info: IEmptyRequestData, sessionID: string): string public getBotLimit(url: string, info: IEmptyRequestData, sessionID: string): string {
{
const splittedUrl = url.split("/"); const splittedUrl = url.split("/");
const type = splittedUrl[splittedUrl.length - 1]; const type = splittedUrl[splittedUrl.length - 1];
return this.httpResponse.noBody(this.botController.getBotPresetGenerationLimit(type)); return this.httpResponse.noBody(this.botController.getBotPresetGenerationLimit(type));
@ -32,13 +29,11 @@ export class BotCallbacks
* Handle singleplayer/settings/bot/difficulty * Handle singleplayer/settings/bot/difficulty
* @returns string * @returns string
*/ */
public getBotDifficulty(url: string, info: IEmptyRequestData, sessionID: string): string public getBotDifficulty(url: string, info: IEmptyRequestData, sessionID: string): string {
{
const splittedUrl = url.split("/"); const splittedUrl = url.split("/");
const type = splittedUrl[splittedUrl.length - 2].toLowerCase(); const type = splittedUrl[splittedUrl.length - 2].toLowerCase();
const difficulty = splittedUrl[splittedUrl.length - 1]; const difficulty = splittedUrl[splittedUrl.length - 1];
if (difficulty === "core") if (difficulty === "core") {
{
return this.httpResponse.noBody(this.botController.getBotCoreDifficulty()); return this.httpResponse.noBody(this.botController.getBotCoreDifficulty());
} }
return this.httpResponse.noBody(this.botController.getBotDifficulty(type, difficulty)); return this.httpResponse.noBody(this.botController.getBotDifficulty(type, difficulty));
@ -52,8 +47,7 @@ export class BotCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): Record<string, Difficulties> ): Record<string, Difficulties> {
{
return this.httpResponse.noBody(this.botController.getAllBotDifficulties()); return this.httpResponse.noBody(this.botController.getAllBotDifficulties());
} }
@ -65,8 +59,7 @@ export class BotCallbacks
url: string, url: string,
info: IGenerateBotsRequestData, info: IGenerateBotsRequestData,
sessionID: string, sessionID: string,
): Promise<IGetBodyResponseData<IBotBase[]>> ): Promise<IGetBodyResponseData<IBotBase[]>> {
{
return this.httpResponse.getBody(await this.botController.generate(sessionID, info)); return this.httpResponse.getBody(await this.botController.generate(sessionID, info));
} }
@ -74,8 +67,7 @@ export class BotCallbacks
* Handle singleplayer/settings/bot/maxCap * Handle singleplayer/settings/bot/maxCap
* @returns string * @returns string
*/ */
public getBotCap(url: string, info: any, sessionID: string): string public getBotCap(url: string, info: any, sessionID: string): string {
{
const splitUrl = url.split("/"); const splitUrl = url.split("/");
const location = splitUrl[splitUrl.length - 1]; const location = splitUrl[splitUrl.length - 1];
return this.httpResponse.noBody(this.botController.getBotCap(location)); return this.httpResponse.noBody(this.botController.getBotCap(location));
@ -85,8 +77,7 @@ export class BotCallbacks
* Handle singleplayer/settings/bot/getBotBehaviours * Handle singleplayer/settings/bot/getBotBehaviours
* @returns string * @returns string
*/ */
public getBotBehaviours(): string public getBotBehaviours(): string {
{
return this.httpResponse.noBody(this.botController.getAiBotBrainTypes()); return this.httpResponse.noBody(this.botController.getAiBotBrainTypes());
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { BuildController } from "@spt/controllers/BuildController"; import { BuildController } from "@spt/controllers/BuildController";
import { ISetMagazineRequest } from "@spt/models/eft/builds/ISetMagazineRequest"; import { ISetMagazineRequest } from "@spt/models/eft/builds/ISetMagazineRequest";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -8,29 +7,26 @@ import { IPresetBuildActionRequestData } from "@spt/models/eft/presetBuild/IPres
import { IRemoveBuildRequestData } from "@spt/models/eft/presetBuild/IRemoveBuildRequestData"; import { IRemoveBuildRequestData } from "@spt/models/eft/presetBuild/IRemoveBuildRequestData";
import { IUserBuilds } from "@spt/models/eft/profile/ISptProfile"; import { IUserBuilds } from "@spt/models/eft/profile/ISptProfile";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class BuildsCallbacks export class BuildsCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("BuildController") protected buildController: BuildController, @inject("BuildController") protected buildController: BuildController,
) ) {}
{}
/** /**
* Handle client/builds/list * Handle client/builds/list
*/ */
public getBuilds(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IUserBuilds> public getBuilds(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IUserBuilds> {
{
return this.httpResponse.getBody(this.buildController.getUserBuilds(sessionID)); return this.httpResponse.getBody(this.buildController.getUserBuilds(sessionID));
} }
/** /**
* Handle client/builds/magazine/save * Handle client/builds/magazine/save
*/ */
public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData {
{
this.buildController.createMagazineTemplate(sessionID, request); this.buildController.createMagazineTemplate(sessionID, request);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
@ -39,8 +35,7 @@ export class BuildsCallbacks
/** /**
* Handle client/builds/weapon/save * Handle client/builds/weapon/save
*/ */
public setWeapon(url: string, info: IPresetBuildActionRequestData, sessionID: string): INullResponseData public setWeapon(url: string, info: IPresetBuildActionRequestData, sessionID: string): INullResponseData {
{
this.buildController.saveWeaponBuild(sessionID, info); this.buildController.saveWeaponBuild(sessionID, info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
@ -49,8 +44,7 @@ export class BuildsCallbacks
/** /**
* Handle client/builds/equipment/save * Handle client/builds/equipment/save
*/ */
public setEquipment(url: string, info: IPresetBuildActionRequestData, sessionID: string): INullResponseData public setEquipment(url: string, info: IPresetBuildActionRequestData, sessionID: string): INullResponseData {
{
this.buildController.saveEquipmentBuild(sessionID, info); this.buildController.saveEquipmentBuild(sessionID, info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
@ -59,8 +53,7 @@ export class BuildsCallbacks
/** /**
* Handle client/builds/delete * Handle client/builds/delete
*/ */
public deleteBuild(url: string, info: IRemoveBuildRequestData, sessionID: string): INullResponseData public deleteBuild(url: string, info: IRemoveBuildRequestData, sessionID: string): INullResponseData {
{
this.buildController.removeBuild(sessionID, info); this.buildController.removeBuild(sessionID, info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();

View File

@ -1,34 +1,30 @@
import { inject, injectable } from "tsyringe";
import { BundleLoader } from "@spt/loaders/BundleLoader"; import { BundleLoader } from "@spt/loaders/BundleLoader";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class BundleCallbacks export class BundleCallbacks {
{
protected httpConfig: IHttpConfig; protected httpConfig: IHttpConfig;
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("BundleLoader") protected bundleLoader: BundleLoader, @inject("BundleLoader") protected bundleLoader: BundleLoader,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
} }
/** /**
* Handle singleplayer/bundles * Handle singleplayer/bundles
*/ */
public getBundles(url: string, info: any, sessionID: string): string public getBundles(url: string, info: any, sessionID: string): string {
{
return this.httpResponse.noBody(this.bundleLoader.getBundles()); return this.httpResponse.noBody(this.bundleLoader.getBundles());
} }
public getBundle(url: string, info: any, sessionID: string): string public getBundle(url: string, info: any, sessionID: string): string {
{
return "BUNDLE"; return "BUNDLE";
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ClientLogController } from "@spt/controllers/ClientLogController"; import { ClientLogController } from "@spt/controllers/ClientLogController";
import { ModLoadOrder } from "@spt/loaders/ModLoadOrder"; import { ModLoadOrder } from "@spt/loaders/ModLoadOrder";
import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData";
@ -8,25 +7,23 @@ import { IClientLogRequest } from "@spt/models/spt/logging/IClientLogRequest";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
/** Handle client logging related events */ /** Handle client logging related events */
@injectable() @injectable()
export class ClientLogCallbacks export class ClientLogCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("ClientLogController") protected clientLogController: ClientLogController, @inject("ClientLogController") protected clientLogController: ClientLogController,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ModLoadOrder") protected modLoadOrder: ModLoadOrder, @inject("ModLoadOrder") protected modLoadOrder: ModLoadOrder,
) ) {}
{}
/** /**
* Handle /singleplayer/log * Handle /singleplayer/log
*/ */
public clientLog(url: string, info: IClientLogRequest, sessionID: string): INullResponseData public clientLog(url: string, info: IClientLogRequest, sessionID: string): INullResponseData {
{
this.clientLogController.clientLog(info); this.clientLogController.clientLog(info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -34,8 +31,7 @@ export class ClientLogCallbacks
/** /**
* Handle /singleplayer/release * Handle /singleplayer/release
*/ */
public releaseNotes(): string public releaseNotes(): string {
{
const data: IRelease = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).release; const data: IRelease = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).release;
data.betaDisclaimerText = globalThis.G_MODS_ENABLED data.betaDisclaimerText = globalThis.G_MODS_ENABLED
@ -62,8 +58,7 @@ export class ClientLogCallbacks
* Handle /singleplayer/enableBSGlogging * Handle /singleplayer/enableBSGlogging
*/ */
public bsgLogging(): string public bsgLogging(): string {
{
const data: IBsgLogging = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).bsgLogging; const data: IBsgLogging = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).bsgLogging;
return this.httpResponse.noBody(data); return this.httpResponse.noBody(data);
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { CustomizationController } from "@spt/controllers/CustomizationController"; import { CustomizationController } from "@spt/controllers/CustomizationController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
@ -10,23 +9,21 @@ import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyRespo
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class CustomizationCallbacks export class CustomizationCallbacks {
{
constructor( constructor(
@inject("CustomizationController") protected customizationController: CustomizationController, @inject("CustomizationController") protected customizationController: CustomizationController,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
) ) {}
{}
/** /**
* Handle client/trading/customization/storage * Handle client/trading/customization/storage
* @returns IGetSuitsResponse * @returns IGetSuitsResponse
*/ */
public getSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetSuitsResponse> public getSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetSuitsResponse> {
{
const result: IGetSuitsResponse = { _id: sessionID, suites: this.saveServer.getProfile(sessionID).suits }; const result: IGetSuitsResponse = { _id: sessionID, suites: this.saveServer.getProfile(sessionID).suits };
return this.httpResponse.getBody(result); return this.httpResponse.getBody(result);
} }
@ -35,8 +32,7 @@ export class CustomizationCallbacks
* Handle client/trading/customization * Handle client/trading/customization
* @returns ISuit[] * @returns ISuit[]
*/ */
public getTraderSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ISuit[]> public getTraderSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ISuit[]> {
{
const splittedUrl = url.split("/"); const splittedUrl = url.split("/");
const traderID = splittedUrl[splittedUrl.length - 2]; const traderID = splittedUrl[splittedUrl.length - 2];
@ -50,16 +46,14 @@ export class CustomizationCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IWearClothingRequestData, body: IWearClothingRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.customizationController.wearClothing(pmcData, body, sessionID); return this.customizationController.wearClothing(pmcData, body, sessionID);
} }
/** /**
* Handle CustomizationBuy event * Handle CustomizationBuy event
*/ */
public buyClothing(pmcData: IPmcData, body: IBuyClothingRequestData, sessionID: string): IItemEventRouterResponse public buyClothing(pmcData: IPmcData, body: IBuyClothingRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.customizationController.buyClothing(pmcData, body, sessionID); return this.customizationController.buyClothing(pmcData, body, sessionID);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { HideoutController } from "@spt/controllers/HideoutController"; import { HideoutController } from "@spt/controllers/HideoutController";
import { RagfairController } from "@spt/controllers/RagfairController"; import { RagfairController } from "@spt/controllers/RagfairController";
import { TraderHelper } from "@spt/helpers/TraderHelper"; import { TraderHelper } from "@spt/helpers/TraderHelper";
@ -17,13 +16,13 @@ import { ISettingsBase } from "@spt/models/spt/server/ISettingsBase";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
/** /**
* Handle client requests * Handle client requests
*/ */
@injectable() @injectable()
export class DataCallbacks export class DataCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@ -31,15 +30,13 @@ export class DataCallbacks
@inject("DatabaseService") protected databaseService: DatabaseService, @inject("DatabaseService") protected databaseService: DatabaseService,
@inject("RagfairController") protected ragfairController: RagfairController, @inject("RagfairController") protected ragfairController: RagfairController,
@inject("HideoutController") protected hideoutController: HideoutController, @inject("HideoutController") protected hideoutController: HideoutController,
) ) {}
{}
/** /**
* Handle client/settings * Handle client/settings
* @returns ISettingsBase * @returns ISettingsBase
*/ */
public getSettings(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ISettingsBase> public getSettings(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ISettingsBase> {
{
return this.httpResponse.getBody(this.databaseService.getSettings()); return this.httpResponse.getBody(this.databaseService.getSettings());
} }
@ -47,8 +44,7 @@ export class DataCallbacks
* Handle client/globals * Handle client/globals
* @returns IGlobals * @returns IGlobals
*/ */
public getGlobals(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGlobals> public getGlobals(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGlobals> {
{
const globals = this.databaseService.getGlobals(); const globals = this.databaseService.getGlobals();
globals.time = Date.now() / 1000; globals.time = Date.now() / 1000;
@ -59,8 +55,7 @@ export class DataCallbacks
* Handle client/items * Handle client/items
* @returns string * @returns string
*/ */
public getTemplateItems(url: string, info: IEmptyRequestData, sessionID: string): string public getTemplateItems(url: string, info: IEmptyRequestData, sessionID: string): string {
{
return this.httpResponse.getUnclearedBody(this.databaseService.getItems()); return this.httpResponse.getUnclearedBody(this.databaseService.getItems());
} }
@ -72,8 +67,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IHandbookBase> ): IGetBodyResponseData<IHandbookBase> {
{
return this.httpResponse.getBody(this.databaseService.getHandbook()); return this.httpResponse.getBody(this.databaseService.getHandbook());
} }
@ -85,8 +79,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<Record<string, ICustomizationItem>> ): IGetBodyResponseData<Record<string, ICustomizationItem>> {
{
return this.httpResponse.getBody(this.databaseService.getTemplates().customization); return this.httpResponse.getBody(this.databaseService.getTemplates().customization);
} }
@ -98,8 +91,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<string[]> ): IGetBodyResponseData<string[]> {
{
return this.httpResponse.getBody(this.databaseService.getTemplates().character); return this.httpResponse.getBody(this.databaseService.getTemplates().character);
} }
@ -111,8 +103,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IHideoutSettingsBase> ): IGetBodyResponseData<IHideoutSettingsBase> {
{
return this.httpResponse.getBody(this.databaseService.getHideout().settings); return this.httpResponse.getBody(this.databaseService.getHideout().settings);
} }
@ -120,8 +111,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IHideoutArea[]> ): IGetBodyResponseData<IHideoutArea[]> {
{
return this.httpResponse.getBody(this.databaseService.getHideout().areas); return this.httpResponse.getBody(this.databaseService.getHideout().areas);
} }
@ -129,8 +119,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IHideoutProduction[]> ): IGetBodyResponseData<IHideoutProduction[]> {
{
return this.httpResponse.getBody(this.databaseService.getHideout().production); return this.httpResponse.getBody(this.databaseService.getHideout().production);
} }
@ -138,8 +127,7 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IHideoutScavCase[]> ): IGetBodyResponseData<IHideoutScavCase[]> {
{
return this.httpResponse.getBody(this.databaseService.getHideout().scavcase); return this.httpResponse.getBody(this.databaseService.getHideout().scavcase);
} }
@ -150,27 +138,23 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<Record<string, string>> ): IGetBodyResponseData<Record<string, string>> {
{
return this.httpResponse.getBody(this.databaseService.getLocales().languages); return this.httpResponse.getBody(this.databaseService.getLocales().languages);
} }
/** /**
* Handle client/menu/locale * Handle client/menu/locale
*/ */
public getLocalesMenu(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<string> public getLocalesMenu(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<string> {
{
const localeId = url.replace("/client/menu/locale/", ""); const localeId = url.replace("/client/menu/locale/", "");
const locales = this.databaseService.getLocales(); const locales = this.databaseService.getLocales();
let result = locales.menu[localeId]; let result = locales.menu[localeId];
if (result === undefined) if (result === undefined) {
{
result = locales.menu.en; result = locales.menu.en;
} }
if (result === undefined) if (result === undefined) throw new Error(`Unable to determine locale for request with '${localeId}'`);
throw new Error(`Unable to determine locale for request with '${localeId}'`);
return this.httpResponse.getBody(result); return this.httpResponse.getBody(result);
} }
@ -178,14 +162,12 @@ export class DataCallbacks
/** /**
* Handle client/locale * Handle client/locale
*/ */
public getLocalesGlobal(url: string, info: IEmptyRequestData, sessionID: string): string public getLocalesGlobal(url: string, info: IEmptyRequestData, sessionID: string): string {
{
const localeId = url.replace("/client/locale/", ""); const localeId = url.replace("/client/locale/", "");
const locales = this.databaseService.getLocales(); const locales = this.databaseService.getLocales();
let result = locales.global[localeId]; let result = locales.global[localeId];
if (result === undefined) if (result === undefined) {
{
result = locales.global["en"]; result = locales.global["en"];
} }
@ -195,8 +177,7 @@ export class DataCallbacks
/** /**
* Handle client/hideout/qte/list * Handle client/hideout/qte/list
*/ */
public getQteList(url: string, info: IEmptyRequestData, sessionID: string): string public getQteList(url: string, info: IEmptyRequestData, sessionID: string): string {
{
return this.httpResponse.getUnclearedBody(this.hideoutController.getQteList(sessionID)); return this.httpResponse.getUnclearedBody(this.hideoutController.getQteList(sessionID));
} }
@ -209,16 +190,14 @@ export class DataCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetItemPricesResponse> ): IGetBodyResponseData<IGetItemPricesResponse> {
{
const traderId = url.replace("/client/items/prices/", ""); const traderId = url.replace("/client/items/prices/", "");
// All traders share same item prices, unknown how to tell what items are shown for each trader // All traders share same item prices, unknown how to tell what items are shown for each trader
// Shown items listed are likely linked to traders items_buy/category array // Shown items listed are likely linked to traders items_buy/category array
const handbookPrices = this.ragfairController.getStaticPrices(); const handbookPrices = this.ragfairController.getStaticPrices();
const response: IGetItemPricesResponse const response: IGetItemPricesResponse = {
= {
supplyNextTime: this.traderHelper.getNextUpdateTimestamp(traderId), supplyNextTime: this.traderHelper.getNextUpdateTimestamp(traderId),
prices: handbookPrices, prices: handbookPrices,
currencyCourses: { currencyCourses: {

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { DialogueController } from "@spt/controllers/DialogueController"; import { DialogueController } from "@spt/controllers/DialogueController";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -36,17 +35,16 @@ import { DialogueInfo } from "@spt/models/eft/profile/ISptProfile";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class DialogueCallbacks implements OnUpdate export class DialogueCallbacks implements OnUpdate {
{
constructor( constructor(
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("DialogueController") protected dialogueController: DialogueController, @inject("DialogueController") protected dialogueController: DialogueController,
) ) {}
{}
/** /**
* Handle client/friend/list * Handle client/friend/list
@ -56,8 +54,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetFriendListDataResponse> ): IGetBodyResponseData<IGetFriendListDataResponse> {
{
return this.httpResponse.getBody(this.dialogueController.getFriendList(sessionID)); return this.httpResponse.getBody(this.dialogueController.getFriendList(sessionID));
} }
@ -69,8 +66,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IGetChatServerListRequestData, info: IGetChatServerListRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IChatServer[]> ): IGetBodyResponseData<IChatServer[]> {
{
const chatServer: IChatServer = { const chatServer: IChatServer = {
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
RegistrationId: 20, RegistrationId: 20,
@ -91,8 +87,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IGetMailDialogListRequestData, info: IGetMailDialogListRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<DialogueInfo[]> ): IGetBodyResponseData<DialogueInfo[]> {
{
return this.httpResponse.getBody(this.dialogueController.generateDialogueList(sessionID), 0, undefined, false); return this.httpResponse.getBody(this.dialogueController.generateDialogueList(sessionID), 0, undefined, false);
} }
@ -101,8 +96,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IGetMailDialogViewRequestData, info: IGetMailDialogViewRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetMailDialogViewResponseData> ): IGetBodyResponseData<IGetMailDialogViewResponseData> {
{
return this.httpResponse.getBody( return this.httpResponse.getBody(
this.dialogueController.generateDialogueView(info, sessionID), this.dialogueController.generateDialogueView(info, sessionID),
0, 0,
@ -116,35 +110,30 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IGetMailDialogInfoRequestData, info: IGetMailDialogInfoRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<DialogueInfo> ): IGetBodyResponseData<DialogueInfo> {
{
return this.httpResponse.getBody(this.dialogueController.getDialogueInfo(info.dialogId, sessionID)); return this.httpResponse.getBody(this.dialogueController.getDialogueInfo(info.dialogId, sessionID));
} }
/** Handle client/mail/dialog/remove */ /** Handle client/mail/dialog/remove */
public removeDialog(url: string, info: IRemoveDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> public removeDialog(url: string, info: IRemoveDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
this.dialogueController.removeDialogue(info.dialogId, sessionID); this.dialogueController.removeDialogue(info.dialogId, sessionID);
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
/** Handle client/mail/dialog/pin */ /** Handle client/mail/dialog/pin */
public pinDialog(url: string, info: IPinDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> public pinDialog(url: string, info: IPinDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
this.dialogueController.setDialoguePin(info.dialogId, true, sessionID); this.dialogueController.setDialoguePin(info.dialogId, true, sessionID);
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
/** Handle client/mail/dialog/unpin */ /** Handle client/mail/dialog/unpin */
public unpinDialog(url: string, info: IPinDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> public unpinDialog(url: string, info: IPinDialogRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
this.dialogueController.setDialoguePin(info.dialogId, false, sessionID); this.dialogueController.setDialoguePin(info.dialogId, false, sessionID);
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
/** Handle client/mail/dialog/read */ /** Handle client/mail/dialog/read */
public setRead(url: string, info: ISetDialogReadRequestData, sessionID: string): IGetBodyResponseData<any[]> public setRead(url: string, info: ISetDialogReadRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
this.dialogueController.setRead(info.dialogs, sessionID); this.dialogueController.setRead(info.dialogs, sessionID);
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
@ -157,28 +146,24 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IGetAllAttachmentsRequestData, info: IGetAllAttachmentsRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetAllAttachmentsResponse | undefined> ): IGetBodyResponseData<IGetAllAttachmentsResponse | undefined> {
{
return this.httpResponse.getBody(this.dialogueController.getAllAttachments(info.dialogId, sessionID)); return this.httpResponse.getBody(this.dialogueController.getAllAttachments(info.dialogId, sessionID));
} }
/** Handle client/mail/msg/send */ /** Handle client/mail/msg/send */
public sendMessage(url: string, request: ISendMessageRequest, sessionID: string): IGetBodyResponseData<string> public sendMessage(url: string, request: ISendMessageRequest, sessionID: string): IGetBodyResponseData<string> {
{
return this.httpResponse.getBody(this.dialogueController.sendMessage(sessionID, request)); return this.httpResponse.getBody(this.dialogueController.sendMessage(sessionID, request));
} }
/** Handle client/friend/request/list/outbox */ /** Handle client/friend/request/list/outbox */
public listOutbox(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<any[]> public listOutbox(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.getBody([]); return this.httpResponse.getBody([]);
} }
/** /**
* Handle client/friend/request/list/inbox * Handle client/friend/request/list/inbox
*/ */
public listInbox(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<any[]> public listInbox(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.getBody([]); return this.httpResponse.getBody([]);
} }
@ -189,16 +174,14 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
request: IFriendRequestData, request: IFriendRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IFriendRequestSendResponse> ): IGetBodyResponseData<IFriendRequestSendResponse> {
{
return this.httpResponse.getBody(this.dialogueController.sendFriendRequest(sessionID, request)); return this.httpResponse.getBody(this.dialogueController.sendFriendRequest(sessionID, request));
} }
/** /**
* Handle client/friend/request/accept-all * Handle client/friend/request/accept-all
*/ */
public acceptAllFriendRequests(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData public acceptAllFriendRequests(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -209,8 +192,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
request: IAcceptFriendRequestData, request: IAcceptFriendRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -221,8 +203,7 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
request: IDeclineFriendRequestData, request: IDeclineFriendRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -233,41 +214,34 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
request: ICancelFriendRequestData, request: ICancelFriendRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
/** Handle client/friend/delete */ /** Handle client/friend/delete */
public deleteFriend(url: string, request: IDeleteFriendRequest, sessionID: string): INullResponseData public deleteFriend(url: string, request: IDeleteFriendRequest, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
/** Handle client/friend/ignore/set */ /** Handle client/friend/ignore/set */
public ignoreFriend(url: string, request: IUIDRequestData, sessionID: string): INullResponseData public ignoreFriend(url: string, request: IUIDRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
/** Handle client/friend/ignore/remove */ /** Handle client/friend/ignore/remove */
public unIgnoreFriend(url: string, request: IUIDRequestData, sessionID: string): INullResponseData public unIgnoreFriend(url: string, request: IUIDRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
public clearMail(url: string, request: IClearMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]> public clearMail(url: string, request: IClearMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
public removeMail(url: string, request: IRemoveMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]> public removeMail(url: string, request: IRemoveMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
public createGroupMail(url: string, info: ICreateGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]> public createGroupMail(url: string, info: ICreateGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
@ -275,13 +249,11 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IChangeGroupMailOwnerRequest, info: IChangeGroupMailOwnerRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<any[]> ): IGetBodyResponseData<any[]> {
{
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public addUserToMail(url: string, info: IAddUserGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]> public addUserToMail(url: string, info: IAddUserGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]> {
{
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
@ -289,19 +261,16 @@ export class DialogueCallbacks implements OnUpdate
url: string, url: string,
info: IRemoveUserGroupMailRequest, info: IRemoveUserGroupMailRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<any[]> ): IGetBodyResponseData<any[]> {
{
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public async onUpdate(timeSinceLastRun: number): Promise<boolean> public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
{
this.dialogueController.update(); this.dialogueController.update();
return true; return true;
} }
public getRoute(): string public getRoute(): string {
{
return "spt-dialogue"; return "spt-dialogue";
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { GameController } from "@spt/controllers/GameController"; import { GameController } from "@spt/controllers/GameController";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -21,25 +20,22 @@ import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseDat
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { Watermark } from "@spt/utils/Watermark"; import { Watermark } from "@spt/utils/Watermark";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class GameCallbacks implements OnLoad export class GameCallbacks implements OnLoad {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("Watermark") protected watermark: Watermark, @inject("Watermark") protected watermark: Watermark,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("GameController") protected gameController: GameController, @inject("GameController") protected gameController: GameController,
) ) {}
{}
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.gameController.load(); this.gameController.load();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-game"; return "spt-game";
} }
@ -47,8 +43,7 @@ export class GameCallbacks implements OnLoad
* Handle client/game/version/validate * Handle client/game/version/validate
* @returns INullResponseData * @returns INullResponseData
*/ */
public versionValidate(url: string, info: IVersionValidateRequestData, sessionID: string): INullResponseData public versionValidate(url: string, info: IVersionValidateRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -60,8 +55,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGameStartResponse> ): IGetBodyResponseData<IGameStartResponse> {
{
const today = new Date().toUTCString(); const today = new Date().toUTCString();
const startTimeStampMS = Date.parse(today); const startTimeStampMS = Date.parse(today);
this.gameController.gameStart(url, info, sessionID, startTimeStampMS); this.gameController.gameStart(url, info, sessionID, startTimeStampMS);
@ -77,8 +71,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGameLogoutResponseData> ): IGetBodyResponseData<IGameLogoutResponseData> {
{
this.saveServer.save(); this.saveServer.save();
return this.httpResponse.getBody({ status: "ok" }); return this.httpResponse.getBody({ status: "ok" });
} }
@ -91,8 +84,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IGameEmptyCrcRequestData, info: IGameEmptyCrcRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGameConfigResponse> ): IGetBodyResponseData<IGameConfigResponse> {
{
return this.httpResponse.getBody(this.gameController.getGameConfig(sessionID)); return this.httpResponse.getBody(this.gameController.getGameConfig(sessionID));
} }
@ -104,16 +96,14 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IGameModeRequestData, info: IGameModeRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGameModeResponse> ): IGetBodyResponseData<IGameModeResponse> {
{
return this.httpResponse.getBody(this.gameController.getGameMode(sessionID, info)); return this.httpResponse.getBody(this.gameController.getGameMode(sessionID, info));
} }
/** /**
* Handle client/server/list * Handle client/server/list
*/ */
public getServer(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IServerDetails[]> public getServer(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IServerDetails[]> {
{
return this.httpResponse.getBody(this.gameController.getServer(sessionID)); return this.httpResponse.getBody(this.gameController.getServer(sessionID));
} }
@ -124,8 +114,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ICurrentGroupResponse> ): IGetBodyResponseData<ICurrentGroupResponse> {
{
return this.httpResponse.getBody(this.gameController.getCurrentGroup(sessionID)); return this.httpResponse.getBody(this.gameController.getCurrentGroup(sessionID));
} }
@ -136,8 +125,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ICheckVersionResponse> ): IGetBodyResponseData<ICheckVersionResponse> {
{
return this.httpResponse.getBody(this.gameController.getValidGameVersion(sessionID)); return this.httpResponse.getBody(this.gameController.getValidGameVersion(sessionID));
} }
@ -149,8 +137,7 @@ export class GameCallbacks implements OnLoad
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGameKeepAliveResponse> ): IGetBodyResponseData<IGameKeepAliveResponse> {
{
return this.httpResponse.getBody(this.gameController.getKeepAlive(sessionID)); return this.httpResponse.getBody(this.gameController.getKeepAlive(sessionID));
} }
@ -158,8 +145,7 @@ export class GameCallbacks implements OnLoad
* Handle singleplayer/settings/version * Handle singleplayer/settings/version
* @returns string * @returns string
*/ */
public getVersion(url: string, info: IEmptyRequestData, sessionID: string): string public getVersion(url: string, info: IEmptyRequestData, sessionID: string): string {
{
return this.httpResponse.noBody({ Version: this.watermark.getInGameVersionLabel() }); return this.httpResponse.noBody({ Version: this.watermark.getInGameVersionLabel() });
} }
@ -167,8 +153,7 @@ export class GameCallbacks implements OnLoad
* Handle /client/report/send & /client/reports/lobby/send * Handle /client/report/send & /client/reports/lobby/send
* @returns INullResponseData * @returns INullResponseData
*/ */
public reportNickname(url: string, info: IUIDRequestData, sessionID: string): INullResponseData public reportNickname(url: string, info: IUIDRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -176,8 +161,7 @@ export class GameCallbacks implements OnLoad
* Handle singleplayer/settings/getRaidTime * Handle singleplayer/settings/getRaidTime
* @returns string * @returns string
*/ */
public getRaidTime(url: string, request: IGetRaidTimeRequest, sessionID: string): IGetRaidTimeResponse public getRaidTime(url: string, request: IGetRaidTimeRequest, sessionID: string): IGetRaidTimeResponse {
{
return this.httpResponse.noBody(this.gameController.getRaidTime(sessionID, request)); return this.httpResponse.noBody(this.gameController.getRaidTime(sessionID, request));
} }
@ -185,8 +169,7 @@ export class GameCallbacks implements OnLoad
* Handle /client/survey * Handle /client/survey
* @returns INullResponseData * @returns INullResponseData
*/ */
public getSurvey(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData public getSurvey(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
} }

View File

@ -1,20 +1,16 @@
import { inject, injectable } from "tsyringe";
import { HandbookController } from "@spt/controllers/HandbookController"; import { HandbookController } from "@spt/controllers/HandbookController";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HandbookCallbacks implements OnLoad export class HandbookCallbacks implements OnLoad {
{ constructor(@inject("HandbookController") protected handbookController: HandbookController) {}
constructor(@inject("HandbookController") protected handbookController: HandbookController)
{}
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.handbookController.load(); this.handbookController.load();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-handbook"; return "spt-handbook";
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { HealthController } from "@spt/controllers/HealthController"; import { HealthController } from "@spt/controllers/HealthController";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
@ -10,16 +9,15 @@ import { IWorkoutData } from "@spt/models/eft/health/IWorkoutData";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HealthCallbacks export class HealthCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("HealthController") protected healthController: HealthController, @inject("HealthController") protected healthController: HealthController,
) ) {}
{}
/** /**
* Custom spt server request found in modules/HealthSynchronizer.cs * Custom spt server request found in modules/HealthSynchronizer.cs
@ -28,8 +26,7 @@ export class HealthCallbacks
* @param sessionID session id * @param sessionID session id
* @returns empty response, no data sent back to client * @returns empty response, no data sent back to client
*/ */
public syncHealth(url: string, info: ISyncHealthRequestData, sessionID: string): IGetBodyResponseData<string> public syncHealth(url: string, info: ISyncHealthRequestData, sessionID: string): IGetBodyResponseData<string> {
{
this.healthController.saveVitality(this.profileHelper.getPmcProfile(sessionID), info, sessionID); this.healthController.saveVitality(this.profileHelper.getPmcProfile(sessionID), info, sessionID);
return this.httpResponse.emptyResponse(); return this.httpResponse.emptyResponse();
} }
@ -41,8 +38,7 @@ export class HealthCallbacks
* @param sessionID session id * @param sessionID session id
* @returns empty response, no data sent back to client * @returns empty response, no data sent back to client
*/ */
public handleWorkoutEffects(url: string, info: IWorkoutData, sessionID: string): IGetBodyResponseData<string> public handleWorkoutEffects(url: string, info: IWorkoutData, sessionID: string): IGetBodyResponseData<string> {
{
this.healthController.applyWorkoutChanges(this.profileHelper.getPmcProfile(sessionID), info, sessionID); this.healthController.applyWorkoutChanges(this.profileHelper.getPmcProfile(sessionID), info, sessionID);
return this.httpResponse.emptyResponse(); return this.httpResponse.emptyResponse();
} }
@ -51,8 +47,7 @@ export class HealthCallbacks
* Handle Eat * Handle Eat
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public offraidEat(pmcData: IPmcData, body: IOffraidEatRequestData, sessionID: string): IItemEventRouterResponse public offraidEat(pmcData: IPmcData, body: IOffraidEatRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.healthController.offraidEat(pmcData, body, sessionID); return this.healthController.offraidEat(pmcData, body, sessionID);
} }
@ -60,8 +55,7 @@ export class HealthCallbacks
* Handle Heal * Handle Heal
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public offraidHeal(pmcData: IPmcData, body: IOffraidHealRequestData, sessionID: string): IItemEventRouterResponse public offraidHeal(pmcData: IPmcData, body: IOffraidHealRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.healthController.offraidHeal(pmcData, body, sessionID); return this.healthController.offraidHeal(pmcData, body, sessionID);
} }
@ -73,8 +67,7 @@ export class HealthCallbacks
pmcData: IPmcData, pmcData: IPmcData,
info: IHealthTreatmentRequestData, info: IHealthTreatmentRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.healthController.healthTreatment(pmcData, info, sessionID); return this.healthController.healthTreatment(pmcData, info, sessionID);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { HideoutController } from "@spt/controllers/HideoutController"; import { HideoutController } from "@spt/controllers/HideoutController";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
@ -19,17 +18,16 @@ import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRo
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig"; import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HideoutCallbacks implements OnUpdate export class HideoutCallbacks implements OnUpdate {
{
protected hideoutConfig: IHideoutConfig; protected hideoutConfig: IHideoutConfig;
constructor( constructor(
@inject("HideoutController") protected hideoutController: HideoutController, // TODO: delay needed @inject("HideoutController") protected hideoutController: HideoutController, // TODO: delay needed
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
} }
@ -41,8 +39,7 @@ export class HideoutCallbacks implements OnUpdate
body: IHideoutUpgradeRequestData, body: IHideoutUpgradeRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.hideoutController.startUpgrade(pmcData, body, sessionID, output); this.hideoutController.startUpgrade(pmcData, body, sessionID, output);
return output; return output;
@ -56,8 +53,7 @@ export class HideoutCallbacks implements OnUpdate
body: IHideoutUpgradeCompleteRequestData, body: IHideoutUpgradeCompleteRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.hideoutController.upgradeComplete(pmcData, body, sessionID, output); this.hideoutController.upgradeComplete(pmcData, body, sessionID, output);
return output; return output;
@ -70,8 +66,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutPutItemInRequestData, body: IHideoutPutItemInRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.putItemsInAreaSlots(pmcData, body, sessionID); return this.hideoutController.putItemsInAreaSlots(pmcData, body, sessionID);
} }
@ -82,8 +77,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutTakeItemOutRequestData, body: IHideoutTakeItemOutRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.takeItemsFromAreaSlots(pmcData, body, sessionID); return this.hideoutController.takeItemsFromAreaSlots(pmcData, body, sessionID);
} }
@ -94,8 +88,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutToggleAreaRequestData, body: IHideoutToggleAreaRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.toggleArea(pmcData, body, sessionID); return this.hideoutController.toggleArea(pmcData, body, sessionID);
} }
@ -106,8 +99,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutSingleProductionStartRequestData, body: IHideoutSingleProductionStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.singleProductionStart(pmcData, body, sessionID); return this.hideoutController.singleProductionStart(pmcData, body, sessionID);
} }
@ -118,8 +110,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutScavCaseStartRequestData, body: IHideoutScavCaseStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.scavCaseProductionStart(pmcData, body, sessionID); return this.hideoutController.scavCaseProductionStart(pmcData, body, sessionID);
} }
@ -130,8 +121,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutContinuousProductionStartRequestData, body: IHideoutContinuousProductionStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.continuousProductionStart(pmcData, body, sessionID); return this.hideoutController.continuousProductionStart(pmcData, body, sessionID);
} }
@ -142,8 +132,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutTakeProductionRequestData, body: IHideoutTakeProductionRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.takeProduction(pmcData, body, sessionID); return this.hideoutController.takeProduction(pmcData, body, sessionID);
} }
@ -155,8 +144,7 @@ export class HideoutCallbacks implements OnUpdate
request: IHandleQTEEventRequestData, request: IHandleQTEEventRequestData,
sessionId: string, sessionId: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.hideoutController.handleQTEEventOutcome(sessionId, pmcData, request, output); this.hideoutController.handleQTEEventOutcome(sessionId, pmcData, request, output);
return output; return output;
@ -170,8 +158,7 @@ export class HideoutCallbacks implements OnUpdate
request: IRecordShootingRangePoints, request: IRecordShootingRangePoints,
sessionId: string, sessionId: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.hideoutController.recordShootingRangePoints(sessionId, pmcData, request); this.hideoutController.recordShootingRangePoints(sessionId, pmcData, request);
return output; return output;
@ -184,8 +171,7 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutImproveAreaRequestData, request: IHideoutImproveAreaRequestData,
sessionId: string, sessionId: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.improveArea(sessionId, pmcData, request); return this.hideoutController.improveArea(sessionId, pmcData, request);
} }
@ -196,23 +182,19 @@ export class HideoutCallbacks implements OnUpdate
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutCancelProductionRequestData, request: IHideoutCancelProductionRequestData,
sessionId: string, sessionId: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.hideoutController.cancelProduction(sessionId, pmcData, request); return this.hideoutController.cancelProduction(sessionId, pmcData, request);
} }
public async onUpdate(timeSinceLastRun: number): Promise<boolean> public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
{ if (timeSinceLastRun > this.hideoutConfig.runIntervalSeconds) {
if (timeSinceLastRun > this.hideoutConfig.runIntervalSeconds)
{
this.hideoutController.update(); this.hideoutController.update();
return true; return true;
} }
return false; return false;
} }
public getRoute(): string public getRoute(): string {
{
return "spt-hideout"; return "spt-hideout";
} }
} }

View File

@ -1,25 +1,20 @@
import { inject, injectable } from "tsyringe";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { HttpServer } from "@spt/servers/HttpServer"; import { HttpServer } from "@spt/servers/HttpServer";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HttpCallbacks implements OnLoad export class HttpCallbacks implements OnLoad {
{ constructor(@inject("HttpServer") protected httpServer: HttpServer) {}
constructor(@inject("HttpServer") protected httpServer: HttpServer)
{}
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.httpServer.load(); this.httpServer.load();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-http"; return "spt-http";
} }
public getImage(): string public getImage(): string {
{
return ""; return "";
} }
} }

View File

@ -1,22 +1,20 @@
import { inject, injectable } from "tsyringe";
import { InraidController } from "@spt/controllers/InraidController"; import { InraidController } from "@spt/controllers/InraidController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData";
import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData"; import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData";
import { IScavSaveRequestData } from "@spt/models/eft/inRaid/IScavSaveRequestData"; import { IScavSaveRequestData } from "@spt/models/eft/inRaid/IScavSaveRequestData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
/** /**
* Handle client requests * Handle client requests
*/ */
@injectable() @injectable()
export class InraidCallbacks export class InraidCallbacks {
{
constructor( constructor(
@inject("InraidController") protected inraidController: InraidController, @inject("InraidController") protected inraidController: InraidController,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
) ) {}
{}
/** /**
* Handle client/location/getLocalloot * Handle client/location/getLocalloot
@ -26,8 +24,7 @@ export class InraidCallbacks
* @param sessionID Session id * @param sessionID Session id
* @returns Null http response * @returns Null http response
*/ */
public registerPlayer(url: string, info: IRegisterPlayerRequestData, sessionID: string): INullResponseData public registerPlayer(url: string, info: IRegisterPlayerRequestData, sessionID: string): INullResponseData {
{
this.inraidController.addPlayer(sessionID, info); this.inraidController.addPlayer(sessionID, info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -39,8 +36,7 @@ export class InraidCallbacks
* @param sessionID Session id * @param sessionID Session id
* @returns Null http response * @returns Null http response
*/ */
public saveProgress(url: string, info: IScavSaveRequestData, sessionID: string): INullResponseData public saveProgress(url: string, info: IScavSaveRequestData, sessionID: string): INullResponseData {
{
this.inraidController.savePostRaidProfileForScav(info, sessionID); this.inraidController.savePostRaidProfileForScav(info, sessionID);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -50,8 +46,7 @@ export class InraidCallbacks
* Handle singleplayer/settings/raid/endstate * Handle singleplayer/settings/raid/endstate
* @returns * @returns
*/ */
public getRaidEndState(): string public getRaidEndState(): string {
{
return this.httpResponse.noBody(this.inraidController.getInraidConfig().MIAOnRaidEnd); return this.httpResponse.noBody(this.inraidController.getInraidConfig().MIAOnRaidEnd);
} }
@ -59,18 +54,15 @@ export class InraidCallbacks
* Handle singleplayer/settings/raid/menu * Handle singleplayer/settings/raid/menu
* @returns JSON as string * @returns JSON as string
*/ */
public getRaidMenuSettings(): string public getRaidMenuSettings(): string {
{
return this.httpResponse.noBody(this.inraidController.getInraidConfig().raidMenuSettings); return this.httpResponse.noBody(this.inraidController.getInraidConfig().raidMenuSettings);
} }
public getTraitorScavHostileChance(url: string, info: IEmptyRequestData, sessionId: string): string public getTraitorScavHostileChance(url: string, info: IEmptyRequestData, sessionId: string): string {
{
return this.httpResponse.noBody(this.inraidController.getTraitorScavHostileChance(url, sessionId)); return this.httpResponse.noBody(this.inraidController.getTraitorScavHostileChance(url, sessionId));
} }
public getBossConvertSettings(url: string, info: IEmptyRequestData, sessionId: string): string public getBossConvertSettings(url: string, info: IEmptyRequestData, sessionId: string): string {
{
return this.httpResponse.noBody(this.inraidController.getBossConvertSettings(url, sessionId)); return this.httpResponse.noBody(this.inraidController.getBossConvertSettings(url, sessionId));
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { InsuranceController } from "@spt/controllers/InsuranceController"; import { InsuranceController } from "@spt/controllers/InsuranceController";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
@ -12,18 +11,17 @@ import { IInsuranceConfig } from "@spt/models/spt/config/IInsuranceConfig";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { InsuranceService } from "@spt/services/InsuranceService"; import { InsuranceService } from "@spt/services/InsuranceService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class InsuranceCallbacks implements OnUpdate export class InsuranceCallbacks implements OnUpdate {
{
protected insuranceConfig: IInsuranceConfig; protected insuranceConfig: IInsuranceConfig;
constructor( constructor(
@inject("InsuranceController") protected insuranceController: InsuranceController, @inject("InsuranceController") protected insuranceController: InsuranceController,
@inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InsuranceService") protected insuranceService: InsuranceService,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
} }
@ -35,8 +33,7 @@ export class InsuranceCallbacks implements OnUpdate
url: string, url: string,
info: IGetInsuranceCostRequestData, info: IGetInsuranceCostRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetInsuranceCostResponseData> ): IGetBodyResponseData<IGetInsuranceCostResponseData> {
{
return this.httpResponse.getBody(this.insuranceController.cost(info, sessionID)); return this.httpResponse.getBody(this.insuranceController.cost(info, sessionID));
} }
@ -44,24 +41,20 @@ export class InsuranceCallbacks implements OnUpdate
* Handle Insure event * Handle Insure event
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public insure(pmcData: IPmcData, body: IInsureRequestData, sessionID: string): IItemEventRouterResponse public insure(pmcData: IPmcData, body: IInsureRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.insuranceController.insure(pmcData, body, sessionID); return this.insuranceController.insure(pmcData, body, sessionID);
} }
public async onUpdate(secondsSinceLastRun: number): Promise<boolean> public async onUpdate(secondsSinceLastRun: number): Promise<boolean> {
{
// People edit the config value to be 0 and break it, force value to no lower than 1 // People edit the config value to be 0 and break it, force value to no lower than 1
if (secondsSinceLastRun > Math.max(this.insuranceConfig.runIntervalSeconds, 1)) if (secondsSinceLastRun > Math.max(this.insuranceConfig.runIntervalSeconds, 1)) {
{
this.insuranceController.processReturn(); this.insuranceController.processReturn();
return true; return true;
} }
return false; return false;
} }
public getRoute(): string public getRoute(): string {
{
return "spt-insurance"; return "spt-insurance";
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { InventoryController } from "@spt/controllers/InventoryController"; import { InventoryController } from "@spt/controllers/InventoryController";
import { QuestController } from "@spt/controllers/QuestController"; import { QuestController } from "@spt/controllers/QuestController";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
@ -23,15 +22,14 @@ import { IRedeemProfileRequestData } from "@spt/models/eft/inventory/IRedeemProf
import { ISetFavoriteItems } from "@spt/models/eft/inventory/ISetFavoriteItems"; import { ISetFavoriteItems } from "@spt/models/eft/inventory/ISetFavoriteItems";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IFailQuestRequestData } from "@spt/models/eft/quests/IFailQuestRequestData"; import { IFailQuestRequestData } from "@spt/models/eft/quests/IFailQuestRequestData";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class InventoryCallbacks export class InventoryCallbacks {
{
constructor( constructor(
@inject("InventoryController") protected inventoryController: InventoryController, @inject("InventoryController") protected inventoryController: InventoryController,
@inject("QuestController") protected questController: QuestController, @inject("QuestController") protected questController: QuestController,
) ) {}
{}
/** Handle client/game/profile/items/moving Move event */ /** Handle client/game/profile/items/moving Move event */
public moveItem( public moveItem(
@ -39,8 +37,7 @@ export class InventoryCallbacks
body: IInventoryMoveRequestData, body: IInventoryMoveRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.moveItem(pmcData, body, sessionID, output); this.inventoryController.moveItem(pmcData, body, sessionID, output);
return output; return output;
@ -52,8 +49,7 @@ export class InventoryCallbacks
body: IInventoryRemoveRequestData, body: IInventoryRemoveRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.discardItem(pmcData, body, sessionID, output); this.inventoryController.discardItem(pmcData, body, sessionID, output);
return output; return output;
@ -65,8 +61,7 @@ export class InventoryCallbacks
body: IInventorySplitRequestData, body: IInventorySplitRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.splitItem(pmcData, body, sessionID, output); return this.inventoryController.splitItem(pmcData, body, sessionID, output);
} }
@ -75,8 +70,7 @@ export class InventoryCallbacks
body: IInventoryMergeRequestData, body: IInventoryMergeRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.mergeItem(pmcData, body, sessionID, output); return this.inventoryController.mergeItem(pmcData, body, sessionID, output);
} }
@ -85,20 +79,17 @@ export class InventoryCallbacks
request: IInventoryTransferRequestData, request: IInventoryTransferRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.transferItem(pmcData, request, sessionID, output); return this.inventoryController.transferItem(pmcData, request, sessionID, output);
} }
/** Handle Swap */ /** Handle Swap */
// TODO: how is this triggered // TODO: how is this triggered
public swapItem(pmcData: IPmcData, body: IInventorySwapRequestData, sessionID: string): IItemEventRouterResponse public swapItem(pmcData: IPmcData, body: IInventorySwapRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.inventoryController.swapItem(pmcData, body, sessionID); return this.inventoryController.swapItem(pmcData, body, sessionID);
} }
public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse public foldItem(pmcData: IPmcData, body: IInventoryFoldRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.inventoryController.foldItem(pmcData, body, sessionID); return this.inventoryController.foldItem(pmcData, body, sessionID);
} }
@ -106,13 +97,11 @@ export class InventoryCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IInventoryToggleRequestData, body: IInventoryToggleRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.toggleItem(pmcData, body, sessionID); return this.inventoryController.toggleItem(pmcData, body, sessionID);
} }
public tagItem(pmcData: IPmcData, body: IInventoryTagRequestData, sessionID: string): IItemEventRouterResponse public tagItem(pmcData: IPmcData, body: IInventoryTagRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.inventoryController.tagItem(pmcData, body, sessionID); return this.inventoryController.tagItem(pmcData, body, sessionID);
} }
@ -121,8 +110,7 @@ export class InventoryCallbacks
body: IInventoryBindRequestData, body: IInventoryBindRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.bindItem(pmcData, body, sessionID); this.inventoryController.bindItem(pmcData, body, sessionID);
return output; return output;
@ -133,8 +121,7 @@ export class InventoryCallbacks
body: IInventoryBindRequestData, body: IInventoryBindRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.unbindItem(pmcData, body, sessionID, output); this.inventoryController.unbindItem(pmcData, body, sessionID, output);
return output; return output;
@ -145,8 +132,7 @@ export class InventoryCallbacks
body: IInventoryExamineRequestData, body: IInventoryExamineRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.examineItem(pmcData, body, sessionID, output); return this.inventoryController.examineItem(pmcData, body, sessionID, output);
} }
@ -155,8 +141,7 @@ export class InventoryCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IInventoryReadEncyclopediaRequestData, body: IInventoryReadEncyclopediaRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.inventoryController.readEncyclopedia(pmcData, body, sessionID); return this.inventoryController.readEncyclopedia(pmcData, body, sessionID);
} }
@ -166,8 +151,7 @@ export class InventoryCallbacks
body: IInventorySortRequestData, body: IInventorySortRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.sortInventory(pmcData, body, sessionID); this.inventoryController.sortInventory(pmcData, body, sessionID);
return output; return output;
@ -178,8 +162,7 @@ export class InventoryCallbacks
body: IInventoryCreateMarkerRequestData, body: IInventoryCreateMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.createMapMarker(pmcData, body, sessionID, output); this.inventoryController.createMapMarker(pmcData, body, sessionID, output);
return output; return output;
@ -190,8 +173,7 @@ export class InventoryCallbacks
body: IInventoryDeleteMarkerRequestData, body: IInventoryDeleteMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.deleteMapMarker(pmcData, body, sessionID, output); this.inventoryController.deleteMapMarker(pmcData, body, sessionID, output);
return output; return output;
@ -202,8 +184,7 @@ export class InventoryCallbacks
body: IInventoryEditMarkerRequestData, body: IInventoryEditMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.editMapMarker(pmcData, body, sessionID, output); this.inventoryController.editMapMarker(pmcData, body, sessionID, output);
return output; return output;
@ -215,8 +196,7 @@ export class InventoryCallbacks
body: IOpenRandomLootContainerRequestData, body: IOpenRandomLootContainerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.openRandomLootContainer(pmcData, body, sessionID, output); this.inventoryController.openRandomLootContainer(pmcData, body, sessionID, output);
return output; return output;
@ -227,8 +207,7 @@ export class InventoryCallbacks
body: IRedeemProfileRequestData, body: IRedeemProfileRequestData,
sessionId: string, sessionId: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.redeemProfileReward(pmcData, body, sessionId); this.inventoryController.redeemProfileReward(pmcData, body, sessionId);
return output; return output;
@ -239,8 +218,7 @@ export class InventoryCallbacks
body: ISetFavoriteItems, body: ISetFavoriteItems,
sessionId: string, sessionId: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.inventoryController.setFavoriteItem(pmcData, body, sessionId); this.inventoryController.setFavoriteItem(pmcData, body, sessionId);
return output; return output;
@ -255,8 +233,7 @@ export class InventoryCallbacks
request: IFailQuestRequestData, request: IFailQuestRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.questController.failQuest(pmcData, request, sessionID, output); return this.questController.failQuest(pmcData, request, sessionID, output);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { Warning } from "@spt/models/eft/itemEvent/IItemEventRouterBase"; import { Warning } from "@spt/models/eft/itemEvent/IItemEventRouterBase";
import { IItemEventRouterRequest } from "@spt/models/eft/itemEvent/IItemEventRouterRequest"; import { IItemEventRouterRequest } from "@spt/models/eft/itemEvent/IItemEventRouterRequest";
@ -6,29 +5,27 @@ import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRo
import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes"; import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes";
import { ItemEventRouter } from "@spt/routers/ItemEventRouter"; import { ItemEventRouter } from "@spt/routers/ItemEventRouter";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class ItemEventCallbacks export class ItemEventCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("ItemEventRouter") protected itemEventRouter: ItemEventRouter, @inject("ItemEventRouter") protected itemEventRouter: ItemEventRouter,
) ) {}
{}
public async handleEvents( public async handleEvents(
url: string, url: string,
info: IItemEventRouterRequest, info: IItemEventRouterRequest,
sessionID: string, sessionID: string,
): Promise<IGetBodyResponseData<IItemEventRouterResponse>> ): Promise<IGetBodyResponseData<IItemEventRouterResponse>> {
{
const eventResponse = await this.itemEventRouter.handleEvents(info, sessionID); const eventResponse = await this.itemEventRouter.handleEvents(info, sessionID);
const result = this.isCriticalError(eventResponse.warnings) const result = this.isCriticalError(eventResponse.warnings)
? this.httpResponse.getBody( ? this.httpResponse.getBody(
eventResponse, eventResponse,
this.getErrorCode(eventResponse.warnings), this.getErrorCode(eventResponse.warnings),
eventResponse.warnings[0].errmsg, eventResponse.warnings[0].errmsg,
) )
: this.httpResponse.getBody(eventResponse); : this.httpResponse.getBody(eventResponse);
return result; return result;
@ -39,15 +36,12 @@ export class ItemEventCallbacks
* @param warnings The list of warnings to check for critical errors * @param warnings The list of warnings to check for critical errors
* @returns * @returns
*/ */
private isCriticalError(warnings: Warning[]): boolean private isCriticalError(warnings: Warning[]): boolean {
{
// List of non-critical error codes, we return true if any error NOT included is passed in // List of non-critical error codes, we return true if any error NOT included is passed in
const nonCriticalErrorCodes: BackendErrorCodes[] = [BackendErrorCodes.NOTENOUGHSPACE]; const nonCriticalErrorCodes: BackendErrorCodes[] = [BackendErrorCodes.NOTENOUGHSPACE];
for (const warning of warnings) for (const warning of warnings) {
{ if (!nonCriticalErrorCodes.includes(+(warning?.code ?? "0"))) {
if (!nonCriticalErrorCodes.includes(+(warning?.code ?? "0")))
{
return true; return true;
} }
} }
@ -55,10 +49,8 @@ export class ItemEventCallbacks
return false; return false;
} }
protected getErrorCode(warnings: Warning[]): number protected getErrorCode(warnings: Warning[]): number {
{ if (warnings[0]?.code) {
if (warnings[0]?.code)
{
return Number(warnings[0].code); return Number(warnings[0].code);
} }
return BackendErrorCodes.UNKNOWN_ERROR; return BackendErrorCodes.UNKNOWN_ERROR;

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { LauncherController } from "@spt/controllers/LauncherController"; import { LauncherController } from "@spt/controllers/LauncherController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { IChangeRequestData } from "@spt/models/eft/launcher/IChangeRequestData"; import { IChangeRequestData } from "@spt/models/eft/launcher/IChangeRequestData";
@ -8,86 +7,72 @@ import { IRemoveProfileData } from "@spt/models/eft/launcher/IRemoveProfileData"
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { Watermark } from "@spt/utils/Watermark"; import { Watermark } from "@spt/utils/Watermark";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class LauncherCallbacks export class LauncherCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("LauncherController") protected launcherController: LauncherController, @inject("LauncherController") protected launcherController: LauncherController,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("Watermark") protected watermark: Watermark, @inject("Watermark") protected watermark: Watermark,
) ) {}
{}
public connect(): string public connect(): string {
{
return this.httpResponse.noBody(this.launcherController.connect()); return this.httpResponse.noBody(this.launcherController.connect());
} }
public login(url: string, info: ILoginRequestData, sessionID: string): string public login(url: string, info: ILoginRequestData, sessionID: string): string {
{
const output = this.launcherController.login(info); const output = this.launcherController.login(info);
return !output ? "FAILED" : output; return !output ? "FAILED" : output;
} }
public register(url: string, info: IRegisterData, sessionID: string): "FAILED" | "OK" public register(url: string, info: IRegisterData, sessionID: string): "FAILED" | "OK" {
{
const output = this.launcherController.register(info); const output = this.launcherController.register(info);
return !output ? "FAILED" : "OK"; return !output ? "FAILED" : "OK";
} }
public get(url: string, info: ILoginRequestData, sessionID: string): string public get(url: string, info: ILoginRequestData, sessionID: string): string {
{
const output = this.launcherController.find(this.launcherController.login(info)); const output = this.launcherController.find(this.launcherController.login(info));
return this.httpResponse.noBody(output); return this.httpResponse.noBody(output);
} }
public changeUsername(url: string, info: IChangeRequestData, sessionID: string): "FAILED" | "OK" public changeUsername(url: string, info: IChangeRequestData, sessionID: string): "FAILED" | "OK" {
{
const output = this.launcherController.changeUsername(info); const output = this.launcherController.changeUsername(info);
return !output ? "FAILED" : "OK"; return !output ? "FAILED" : "OK";
} }
public changePassword(url: string, info: IChangeRequestData, sessionID: string): "FAILED" | "OK" public changePassword(url: string, info: IChangeRequestData, sessionID: string): "FAILED" | "OK" {
{
const output = this.launcherController.changePassword(info); const output = this.launcherController.changePassword(info);
return !output ? "FAILED" : "OK"; return !output ? "FAILED" : "OK";
} }
public wipe(url: string, info: IRegisterData, sessionID: string): "FAILED" | "OK" public wipe(url: string, info: IRegisterData, sessionID: string): "FAILED" | "OK" {
{
const output = this.launcherController.wipe(info); const output = this.launcherController.wipe(info);
return !output ? "FAILED" : "OK"; return !output ? "FAILED" : "OK";
} }
public getServerVersion(): string public getServerVersion(): string {
{
return this.httpResponse.noBody(this.watermark.getVersionTag()); return this.httpResponse.noBody(this.watermark.getVersionTag());
} }
public ping(url: string, info: IEmptyRequestData, sessionID: string): string public ping(url: string, info: IEmptyRequestData, sessionID: string): string {
{
return this.httpResponse.noBody("pong!"); return this.httpResponse.noBody("pong!");
} }
public removeProfile(url: string, info: IRemoveProfileData, sessionID: string): string public removeProfile(url: string, info: IRemoveProfileData, sessionID: string): string {
{
return this.httpResponse.noBody(this.saveServer.removeProfile(sessionID)); return this.httpResponse.noBody(this.saveServer.removeProfile(sessionID));
} }
public getCompatibleTarkovVersion(): string public getCompatibleTarkovVersion(): string {
{
return this.httpResponse.noBody(this.launcherController.getCompatibleTarkovVersion()); return this.httpResponse.noBody(this.launcherController.getCompatibleTarkovVersion());
} }
public getLoadedServerMods(): string public getLoadedServerMods(): string {
{
return this.httpResponse.noBody(this.launcherController.getLoadedServerMods()); return this.httpResponse.noBody(this.launcherController.getLoadedServerMods());
} }
public getServerModsProfileUsed(url: string, info: IEmptyRequestData, sessionId: string): string public getServerModsProfileUsed(url: string, info: IEmptyRequestData, sessionId: string): string {
{
return this.httpResponse.noBody(this.launcherController.getServerModsProfileUsed(sessionId)); return this.httpResponse.noBody(this.launcherController.getServerModsProfileUsed(sessionId));
} }
} }

View File

@ -1,27 +1,24 @@
import { inject, injectable } from "tsyringe";
import { LocationController } from "@spt/controllers/LocationController"; import { LocationController } from "@spt/controllers/LocationController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase"; import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse"; import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class LocationCallbacks export class LocationCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("LocationController") protected locationController: LocationController, @inject("LocationController") protected locationController: LocationController,
) ) {}
{}
/** Handle client/locations */ /** Handle client/locations */
public getLocationData( public getLocationData(
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ILocationsGenerateAllResponse> ): IGetBodyResponseData<ILocationsGenerateAllResponse> {
{
return this.httpResponse.getBody(this.locationController.generateAll(sessionID)); return this.httpResponse.getBody(this.locationController.generateAll(sessionID));
} }
@ -30,8 +27,7 @@ export class LocationCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetAirdropLootResponse> ): IGetBodyResponseData<IGetAirdropLootResponse> {
{
return this.httpResponse.getBody(this.locationController.getAirdropLoot()); return this.httpResponse.getBody(this.locationController.getAirdropLoot());
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { MatchController } from "@spt/controllers/MatchController"; import { MatchController } from "@spt/controllers/MatchController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
@ -22,33 +21,29 @@ import { IUpdatePingRequestData } from "@spt/models/eft/match/IUpdatePingRequest
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { JsonUtil } from "@spt/utils/JsonUtil"; import { JsonUtil } from "@spt/utils/JsonUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class MatchCallbacks export class MatchCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("MatchController") protected matchController: MatchController, @inject("MatchController") protected matchController: MatchController,
@inject("DatabaseService") protected databaseService: DatabaseService, @inject("DatabaseService") protected databaseService: DatabaseService,
) ) {}
{}
/** Handle client/match/updatePing */ /** Handle client/match/updatePing */
public updatePing(url: string, info: IUpdatePingRequestData, sessionID: string): INullResponseData public updatePing(url: string, info: IUpdatePingRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
// Handle client/match/exit // Handle client/match/exit
public exitMatch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData public exitMatch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
/** Handle client/match/group/exit_from_menu */ /** Handle client/match/group/exit_from_menu */
public exitToMenu(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData public exitToMenu(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -56,18 +51,15 @@ export class MatchCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IMatchGroupCurrentResponse> ): IGetBodyResponseData<IMatchGroupCurrentResponse> {
{
return this.httpResponse.getBody({ squad: [] }); return this.httpResponse.getBody({ squad: [] });
} }
public startGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData public startGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
public stopGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData public stopGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -76,8 +68,7 @@ export class MatchCallbacks
url: string, url: string,
info: IMatchGroupInviteSendRequest, info: IMatchGroupInviteSendRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<string> ): IGetBodyResponseData<string> {
{
return this.httpResponse.getBody("2427943f23698ay9f2863735"); return this.httpResponse.getBody("2427943f23698ay9f2863735");
} }
@ -86,8 +77,7 @@ export class MatchCallbacks
url: string, url: string,
info: IRequestIdRequest, info: IRequestIdRequest,
sessionId: string, sessionId: string,
): IGetBodyResponseData<IGroupCharacter[]> ): IGetBodyResponseData<IGroupCharacter[]> {
{
const result = []; const result = [];
result.push({}); result.push({});
@ -95,14 +85,12 @@ export class MatchCallbacks
} }
/** Handle client/match/group/invite/decline */ /** Handle client/match/group/invite/decline */
public declineGroupInvite(url: string, info: IRequestIdRequest, sessionId: string): IGetBodyResponseData<boolean> public declineGroupInvite(url: string, info: IRequestIdRequest, sessionId: string): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
/** Handle client/match/group/invite/cancel */ /** Handle client/match/group/invite/cancel */
public cancelGroupInvite(url: string, info: IRequestIdRequest, sessionID: string): IGetBodyResponseData<boolean> public cancelGroupInvite(url: string, info: IRequestIdRequest, sessionID: string): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -111,8 +99,7 @@ export class MatchCallbacks
url: string, url: string,
info: IMatchGroupTransferRequest, info: IMatchGroupTransferRequest,
sessionId: string, sessionId: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -121,19 +108,16 @@ export class MatchCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionId: string, sessionId: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
public putMetrics(url: string, request: IPutMetricsRequestData, sessionId: string): INullResponseData public putMetrics(url: string, request: IPutMetricsRequestData, sessionId: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
// Handle client/match/available // Handle client/match/available
public serverAvailable(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> public serverAvailable(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> {
{
const output = this.matchController.getEnabled(); const output = this.matchController.getEnabled();
return this.httpResponse.getBody(output); return this.httpResponse.getBody(output);
@ -144,14 +128,12 @@ export class MatchCallbacks
url: string, url: string,
info: IMatchGroupStartGameRequest, info: IMatchGroupStartGameRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IProfileStatusResponse> ): IGetBodyResponseData<IProfileStatusResponse> {
{
return this.httpResponse.getBody(this.matchController.joinMatch(info, sessionID)); return this.httpResponse.getBody(this.matchController.joinMatch(info, sessionID));
} }
/** Handle client/getMetricsConfig */ /** Handle client/getMetricsConfig */
public getMetrics(url: string, info: any, sessionID: string): IGetBodyResponseData<string> public getMetrics(url: string, info: any, sessionID: string): IGetBodyResponseData<string> {
{
return this.httpResponse.getBody(this.jsonUtil.serialize(this.databaseService.getMatch().metrics)); return this.httpResponse.getBody(this.jsonUtil.serialize(this.databaseService.getMatch().metrics));
} }
@ -164,21 +146,18 @@ export class MatchCallbacks
url: string, url: string,
info: IMatchGroupStatusRequest, info: IMatchGroupStatusRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IMatchGroupStatusResponse> ): IGetBodyResponseData<IMatchGroupStatusResponse> {
{
return this.httpResponse.getBody(this.matchController.getGroupStatus(info)); return this.httpResponse.getBody(this.matchController.getGroupStatus(info));
} }
/** Handle client/match/group/delete */ /** Handle client/match/group/delete */
public deleteGroup(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<boolean> public deleteGroup(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<boolean> {
{
this.matchController.deleteGroup(info); this.matchController.deleteGroup(info);
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
// Handle client/match/group/leave // Handle client/match/group/leave
public leaveGroup(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<boolean> public leaveGroup(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -187,8 +166,7 @@ export class MatchCallbacks
url: string, url: string,
info: IMatchGroupPlayerRemoveRequest, info: IMatchGroupPlayerRemoveRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
@ -197,18 +175,12 @@ export class MatchCallbacks
url: string, url: string,
info: IStartLocalRaidRequestData, info: IStartLocalRaidRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IStartLocalRaidResponseData> ): IGetBodyResponseData<IStartLocalRaidResponseData> {
{
return this.httpResponse.getBody(this.matchController.startLocalRaid(sessionID, info)); return this.httpResponse.getBody(this.matchController.startLocalRaid(sessionID, info));
} }
/** Handle client/match/local/end */ /** Handle client/match/local/end */
public endLocalRaid( public endLocalRaid(url: string, info: IEndLocalRaidRequestData, sessionID: string): INullResponseData {
url: string,
info: IEndLocalRaidRequestData,
sessionID: string,
): INullResponseData
{
this.matchController.endLocalRaid(sessionID, info); this.matchController.endLocalRaid(sessionID, info);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -218,8 +190,7 @@ export class MatchCallbacks
url: string, url: string,
info: IGetRaidConfigurationRequestData, info: IGetRaidConfigurationRequestData,
sessionID: string, sessionID: string,
): INullResponseData ): INullResponseData {
{
this.matchController.configureOfflineRaid(info, sessionID); this.matchController.configureOfflineRaid(info, sessionID);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -229,20 +200,17 @@ export class MatchCallbacks
url: string, url: string,
info: IGetRaidConfigurationRequestData, info: IGetRaidConfigurationRequestData,
sessionID: string, sessionID: string,
): INullResponseData ): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
/** Handle client/match/group/raid/ready */ /** Handle client/match/group/raid/ready */
public raidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> public raidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
/** Handle client/match/group/raid/not-ready */ /** Handle client/match/group/raid/not-ready */
public notRaidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> public notRaidReady(url: string, info: IEmptyRequestData, sessionId: string): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(true); return this.httpResponse.getBody(true);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { PostSptModLoader } from "@spt/loaders/PostSptModLoader"; import { PostSptModLoader } from "@spt/loaders/PostSptModLoader";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
@ -8,10 +7,10 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { HttpFileUtil } from "@spt/utils/HttpFileUtil"; import { HttpFileUtil } from "@spt/utils/HttpFileUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class ModCallbacks implements OnLoad export class ModCallbacks implements OnLoad {
{
protected httpConfig: IHttpConfig; protected httpConfig: IHttpConfig;
constructor( constructor(
@ -21,21 +20,17 @@ export class ModCallbacks implements OnLoad
@inject("PostSptModLoader") protected postSptModLoader: PostSptModLoader, @inject("PostSptModLoader") protected postSptModLoader: PostSptModLoader,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
} }
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{ if (globalThis.G_MODS_ENABLED) {
if (globalThis.G_MODS_ENABLED)
{
await this.postSptModLoader.load(); await this.postSptModLoader.load();
} }
} }
public getRoute(): string public getRoute(): string {
{
return "spt-mods"; return "spt-mods";
} }
} }

View File

@ -1,30 +1,25 @@
import { inject, injectable } from "tsyringe";
import { NoteController } from "@spt/controllers/NoteController"; import { NoteController } from "@spt/controllers/NoteController";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { INoteActionData } from "@spt/models/eft/notes/INoteActionData"; import { INoteActionData } from "@spt/models/eft/notes/INoteActionData";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class NoteCallbacks export class NoteCallbacks {
{ constructor(@inject("NoteController") protected noteController: NoteController) {}
constructor(@inject("NoteController") protected noteController: NoteController)
{}
/** Handle AddNote event */ /** Handle AddNote event */
public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
return this.noteController.addNote(pmcData, body, sessionID); return this.noteController.addNote(pmcData, body, sessionID);
} }
/** Handle EditNote event */ /** Handle EditNote event */
public editNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public editNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
return this.noteController.editNote(pmcData, body, sessionID); return this.noteController.editNote(pmcData, body, sessionID);
} }
/** Handle DeleteNote event */ /** Handle DeleteNote event */
public deleteNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public deleteNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
return this.noteController.deleteNote(pmcData, body, sessionID); return this.noteController.deleteNote(pmcData, body, sessionID);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { NotifierController } from "@spt/controllers/NotifierController"; import { NotifierController } from "@spt/controllers/NotifierController";
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -8,17 +7,16 @@ import { INotifierChannel } from "@spt/models/eft/notifier/INotifier";
import { ISelectProfileResponse } from "@spt/models/eft/notifier/ISelectProfileResponse"; import { ISelectProfileResponse } from "@spt/models/eft/notifier/ISelectProfileResponse";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { JsonUtil } from "@spt/utils/JsonUtil"; import { JsonUtil } from "@spt/utils/JsonUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class NotifierCallbacks export class NotifierCallbacks {
{
constructor( constructor(
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper, @inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("NotifierController") protected notifierController: NotifierController, @inject("NotifierController") protected notifierController: NotifierController,
) ) {}
{}
/** /**
* If we don't have anything to send, it's ok to not send anything back * If we don't have anything to send, it's ok to not send anything back
@ -26,8 +24,7 @@ export class NotifierCallbacks
* until we actually have something to send because otherwise we'd spam the client * until we actually have something to send because otherwise we'd spam the client
* and the client would abort the connection due to spam. * and the client would abort the connection due to spam.
*/ */
public sendNotification(sessionID: string, req: any, resp: any, data: any): void public sendNotification(sessionID: string, req: any, resp: any, data: any): void {
{
const splittedUrl = req.url.split("/"); const splittedUrl = req.url.split("/");
const tmpSessionID = splittedUrl[splittedUrl.length - 1].split("?last_id")[0]; const tmpSessionID = splittedUrl[splittedUrl.length - 1].split("?last_id")[0];
@ -44,8 +41,7 @@ export class NotifierCallbacks
/** Handle push/notifier/get */ /** Handle push/notifier/get */
/** Handle push/notifier/getwebsocket */ /** Handle push/notifier/getwebsocket */
// TODO: removed from client? // TODO: removed from client?
public getNotifier(url: string, info: any, sessionID: string): IGetBodyResponseData<any[]> public getNotifier(url: string, info: any, sessionID: string): IGetBodyResponseData<any[]> {
{
return this.httpResponse.emptyArrayResponse(); return this.httpResponse.emptyArrayResponse();
} }
@ -54,8 +50,7 @@ export class NotifierCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<INotifierChannel> ): IGetBodyResponseData<INotifierChannel> {
{
return this.httpResponse.getBody(this.notifierController.getChannel(sessionID)); return this.httpResponse.getBody(this.notifierController.getChannel(sessionID));
} }
@ -67,13 +62,11 @@ export class NotifierCallbacks
url: string, url: string,
info: IUIDRequestData, info: IUIDRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ISelectProfileResponse> ): IGetBodyResponseData<ISelectProfileResponse> {
{
return this.httpResponse.getBody({ status: "ok" }); return this.httpResponse.getBody({ status: "ok" });
} }
public notify(url: string, info: any, sessionID: string): string public notify(url: string, info: any, sessionID: string): string {
{
return "NOTIFY"; return "NOTIFY";
} }
} }

View File

@ -1,20 +1,16 @@
import { inject, injectable } from "tsyringe";
import { PresetController } from "@spt/controllers/PresetController"; import { PresetController } from "@spt/controllers/PresetController";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class PresetCallbacks implements OnLoad export class PresetCallbacks implements OnLoad {
{ constructor(@inject("PresetController") protected presetController: PresetController) {}
constructor(@inject("PresetController") protected presetController: PresetController)
{}
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.presetController.initialize(); this.presetController.initialize();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-presets"; return "spt-presets";
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ProfileController } from "@spt/controllers/ProfileController"; import { ProfileController } from "@spt/controllers/ProfileController";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -19,18 +18,17 @@ import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResp
import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData"; import { IValidateNicknameRequestData } from "@spt/models/eft/profile/IValidateNicknameRequestData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
/** Handle profile related client events */ /** Handle profile related client events */
@injectable() @injectable()
export class ProfileCallbacks export class ProfileCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("ProfileController") protected profileController: ProfileController, @inject("ProfileController") protected profileController: ProfileController,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) ) {}
{}
/** /**
* Handle client/game/profile/create * Handle client/game/profile/create
@ -39,8 +37,7 @@ export class ProfileCallbacks
url: string, url: string,
info: IProfileCreateRequestData, info: IProfileCreateRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ICreateProfileResponse> ): IGetBodyResponseData<ICreateProfileResponse> {
{
const id = this.profileController.createProfile(info, sessionID); const id = this.profileController.createProfile(info, sessionID);
return this.httpResponse.getBody({ uid: id }); return this.httpResponse.getBody({ uid: id });
} }
@ -49,8 +46,7 @@ export class ProfileCallbacks
* Handle client/game/profile/list * Handle client/game/profile/list
* Get the complete player profile (scav + pmc character) * Get the complete player profile (scav + pmc character)
*/ */
public getProfileData(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IPmcData[]> public getProfileData(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IPmcData[]> {
{
return this.httpResponse.getBody(this.profileController.getCompleteProfile(sessionID)); return this.httpResponse.getBody(this.profileController.getCompleteProfile(sessionID));
} }
@ -63,16 +59,14 @@ export class ProfileCallbacks
* @param sessionID Session id * @param sessionID Session id
* @returns Profile object * @returns Profile object
*/ */
public regenerateScav(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IPmcData[]> public regenerateScav(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IPmcData[]> {
{
return this.httpResponse.getBody([this.profileController.generatePlayerScav(sessionID)]); return this.httpResponse.getBody([this.profileController.generatePlayerScav(sessionID)]);
} }
/** /**
* Handle client/game/profile/voice/change event * Handle client/game/profile/voice/change event
*/ */
public changeVoice(url: string, info: IProfileChangeVoiceRequestData, sessionID: string): INullResponseData public changeVoice(url: string, info: IProfileChangeVoiceRequestData, sessionID: string): INullResponseData {
{
this.profileController.changeVoice(info, sessionID); this.profileController.changeVoice(info, sessionID);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -85,17 +79,14 @@ export class ProfileCallbacks
url: string, url: string,
info: IProfileChangeNicknameRequestData, info: IProfileChangeNicknameRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<any> ): IGetBodyResponseData<any> {
{
const output = this.profileController.changeNickname(info, sessionID); const output = this.profileController.changeNickname(info, sessionID);
if (output === "taken") if (output === "taken") {
{
return this.httpResponse.getBody(undefined, 255, "The nickname is already in use"); return this.httpResponse.getBody(undefined, 255, "The nickname is already in use");
} }
if (output === "tooshort") if (output === "tooshort") {
{
return this.httpResponse.getBody(undefined, 1, "The nickname is too short"); return this.httpResponse.getBody(undefined, 1, "The nickname is too short");
} }
@ -109,17 +100,14 @@ export class ProfileCallbacks
url: string, url: string,
info: IValidateNicknameRequestData, info: IValidateNicknameRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<any> ): IGetBodyResponseData<any> {
{
const output = this.profileController.validateNickname(info, sessionID); const output = this.profileController.validateNickname(info, sessionID);
if (output === "taken") if (output === "taken") {
{
return this.httpResponse.getBody(undefined, 255, "225 - "); return this.httpResponse.getBody(undefined, 255, "225 - ");
} }
if (output === "tooshort") if (output === "tooshort") {
{
return this.httpResponse.getBody(undefined, 256, "256 - "); return this.httpResponse.getBody(undefined, 256, "256 - ");
} }
@ -129,8 +117,7 @@ export class ProfileCallbacks
/** /**
* Handle client/game/profile/nickname/reserved * Handle client/game/profile/nickname/reserved
*/ */
public getReservedNickname(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<string> public getReservedNickname(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<string> {
{
return this.httpResponse.getBody("SPTarkov"); return this.httpResponse.getBody("SPTarkov");
} }
@ -142,8 +129,7 @@ export class ProfileCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<GetProfileStatusResponseData> ): IGetBodyResponseData<GetProfileStatusResponseData> {
{
return this.httpResponse.getBody(this.profileController.getProfileStatus(sessionID)); return this.httpResponse.getBody(this.profileController.getProfileStatus(sessionID));
} }
@ -155,8 +141,7 @@ export class ProfileCallbacks
url: string, url: string,
request: IGetOtherProfileRequest, request: IGetOtherProfileRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetOtherProfileResponse> ): IGetBodyResponseData<IGetOtherProfileResponse> {
{
return this.httpResponse.getBody(this.profileController.getOtherProfile(sessionID, request)); return this.httpResponse.getBody(this.profileController.getOtherProfile(sessionID, request));
} }
@ -167,8 +152,7 @@ export class ProfileCallbacks
url: string, url: string,
info: IGetProfileSettingsRequest, info: IGetProfileSettingsRequest,
sessionId: string, sessionId: string,
): IGetBodyResponseData<boolean> ): IGetBodyResponseData<boolean> {
{
return this.httpResponse.getBody(this.profileController.setChosenProfileIcon(sessionId, info)); return this.httpResponse.getBody(this.profileController.setChosenProfileIcon(sessionId, info));
} }
@ -179,24 +163,21 @@ export class ProfileCallbacks
url: string, url: string,
info: ISearchFriendRequestData, info: ISearchFriendRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ISearchFriendResponse[]> ): IGetBodyResponseData<ISearchFriendResponse[]> {
{
return this.httpResponse.getBody(this.profileController.getFriends(info, sessionID)); return this.httpResponse.getBody(this.profileController.getFriends(info, sessionID));
} }
/** /**
* Handle launcher/profile/info * Handle launcher/profile/info
*/ */
public getMiniProfile(url: string, info: IGetMiniProfileRequestData, sessionID: string): string public getMiniProfile(url: string, info: IGetMiniProfileRequestData, sessionID: string): string {
{
return this.httpResponse.noBody(this.profileController.getMiniProfile(sessionID)); return this.httpResponse.noBody(this.profileController.getMiniProfile(sessionID));
} }
/** /**
* Handle /launcher/profiles * Handle /launcher/profiles
*/ */
public getAllMiniProfiles(url: string, info: IEmptyRequestData, sessionID: string): string public getAllMiniProfiles(url: string, info: IEmptyRequestData, sessionID: string): string {
{
return this.httpResponse.noBody(this.profileController.getMiniProfiles()); return this.httpResponse.noBody(this.profileController.getMiniProfiles());
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { QuestController } from "@spt/controllers/QuestController"; import { QuestController } from "@spt/controllers/QuestController";
import { RepeatableQuestController } from "@spt/controllers/RepeatableQuestController"; import { RepeatableQuestController } from "@spt/controllers/RepeatableQuestController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
@ -13,16 +12,15 @@ import { IHandoverQuestRequestData } from "@spt/models/eft/quests/IHandoverQuest
import { IListQuestsRequestData } from "@spt/models/eft/quests/IListQuestsRequestData"; import { IListQuestsRequestData } from "@spt/models/eft/quests/IListQuestsRequestData";
import { IRepeatableQuestChangeRequest } from "@spt/models/eft/quests/IRepeatableQuestChangeRequest"; import { IRepeatableQuestChangeRequest } from "@spt/models/eft/quests/IRepeatableQuestChangeRequest";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class QuestCallbacks export class QuestCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("QuestController") protected questController: QuestController, @inject("QuestController") protected questController: QuestController,
@inject("RepeatableQuestController") protected repeatableQuestController: RepeatableQuestController, @inject("RepeatableQuestController") protected repeatableQuestController: RepeatableQuestController,
) ) {}
{}
/** /**
* Handle RepeatableQuestChange event * Handle RepeatableQuestChange event
@ -31,18 +29,15 @@ export class QuestCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IRepeatableQuestChangeRequest, body: IRepeatableQuestChangeRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.repeatableQuestController.changeRepeatableQuest(pmcData, body, sessionID); return this.repeatableQuestController.changeRepeatableQuest(pmcData, body, sessionID);
} }
/** /**
* Handle QuestAccept event * Handle QuestAccept event
*/ */
public acceptQuest(pmcData: IPmcData, body: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse public acceptQuest(pmcData: IPmcData, body: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse {
{ if (body.type === "repeatable") {
if (body.type === "repeatable")
{
return this.questController.acceptRepeatableQuest(pmcData, body, sessionID); return this.questController.acceptRepeatableQuest(pmcData, body, sessionID);
} }
return this.questController.acceptQuest(pmcData, body, sessionID); return this.questController.acceptQuest(pmcData, body, sessionID);
@ -55,8 +50,7 @@ export class QuestCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: ICompleteQuestRequestData, body: ICompleteQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.questController.completeQuest(pmcData, body, sessionID); return this.questController.completeQuest(pmcData, body, sessionID);
} }
@ -67,16 +61,14 @@ export class QuestCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IHandoverQuestRequestData, body: IHandoverQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.questController.handoverQuest(pmcData, body, sessionID); return this.questController.handoverQuest(pmcData, body, sessionID);
} }
/** /**
* Handle client/quest/list * Handle client/quest/list
*/ */
public listQuests(url: string, info: IListQuestsRequestData, sessionID: string): IGetBodyResponseData<IQuest[]> public listQuests(url: string, info: IListQuestsRequestData, sessionID: string): IGetBodyResponseData<IQuest[]> {
{
return this.httpResponse.getBody(this.questController.getClientQuests(sessionID)); return this.httpResponse.getBody(this.questController.getClientQuests(sessionID));
} }
@ -87,8 +79,7 @@ export class QuestCallbacks
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IPmcDataRepeatableQuest[]> ): IGetBodyResponseData<IPmcDataRepeatableQuest[]> {
{
return this.httpResponse.getBody(this.repeatableQuestController.getClientRepeatableQuests(sessionID)); return this.httpResponse.getBody(this.repeatableQuestController.getClientRepeatableQuests(sessionID));
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { RagfairController } from "@spt/controllers/RagfairController"; import { RagfairController } from "@spt/controllers/RagfairController";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
@ -24,13 +23,13 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
import { RagfairServer } from "@spt/servers/RagfairServer"; import { RagfairServer } from "@spt/servers/RagfairServer";
import { RagfairTaxService } from "@spt/services/RagfairTaxService"; import { RagfairTaxService } from "@spt/services/RagfairTaxService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
/** /**
* Handle ragfair related callback events * Handle ragfair related callback events
*/ */
@injectable() @injectable()
export class RagfairCallbacks implements OnLoad, OnUpdate export class RagfairCallbacks implements OnLoad, OnUpdate {
{
protected ragfairConfig: IRagfairConfig; protected ragfairConfig: IRagfairConfig;
constructor( constructor(
@ -39,25 +38,20 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
@inject("RagfairController") protected ragfairController: RagfairController, @inject("RagfairController") protected ragfairController: RagfairController,
@inject("RagfairTaxService") protected ragfairTaxService: RagfairTaxService, @inject("RagfairTaxService") protected ragfairTaxService: RagfairTaxService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
} }
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
await this.ragfairServer.load(); await this.ragfairServer.load();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-ragfair"; return "spt-ragfair";
} }
public async onUpdate(timeSinceLastRun: number): Promise<boolean> public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
{ if (timeSinceLastRun > this.ragfairConfig.runIntervalSeconds) {
if (timeSinceLastRun > this.ragfairConfig.runIntervalSeconds)
{
// There is a flag inside this class that only makes it run once. // There is a flag inside this class that only makes it run once.
this.ragfairServer.addPlayerOffers(); this.ragfairServer.addPlayerOffers();
@ -76,8 +70,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
* Handle client/ragfair/search * Handle client/ragfair/search
* Handle client/ragfair/find * Handle client/ragfair/find
*/ */
public search(url: string, info: ISearchRequestData, sessionID: string): IGetBodyResponseData<IGetOffersResult> public search(url: string, info: ISearchRequestData, sessionID: string): IGetBodyResponseData<IGetOffersResult> {
{
return this.httpResponse.getBody(this.ragfairController.getOffers(sessionID, info)); return this.httpResponse.getBody(this.ragfairController.getOffers(sessionID, info));
} }
@ -86,26 +79,22 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
url: string, url: string,
info: IGetMarketPriceRequestData, info: IGetMarketPriceRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IGetItemPriceResult> ): IGetBodyResponseData<IGetItemPriceResult> {
{
return this.httpResponse.getBody(this.ragfairController.getItemMinAvgMaxFleaPriceValues(info)); return this.httpResponse.getBody(this.ragfairController.getItemMinAvgMaxFleaPriceValues(info));
} }
/** Handle RagFairAddOffer event */ /** Handle RagFairAddOffer event */
public addOffer(pmcData: IPmcData, info: IAddOfferRequestData, sessionID: string): IItemEventRouterResponse public addOffer(pmcData: IPmcData, info: IAddOfferRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.ragfairController.addPlayerOffer(pmcData, info, sessionID); return this.ragfairController.addPlayerOffer(pmcData, info, sessionID);
} }
/** Handle RagFairRemoveOffer event */ /** Handle RagFairRemoveOffer event */
public removeOffer(pmcData: IPmcData, info: IRemoveOfferRequestData, sessionID: string): IItemEventRouterResponse public removeOffer(pmcData: IPmcData, info: IRemoveOfferRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.ragfairController.removeOffer(info, sessionID); return this.ragfairController.removeOffer(info, sessionID);
} }
/** Handle RagFairRenewOffer event */ /** Handle RagFairRenewOffer event */
public extendOffer(pmcData: IPmcData, info: IExtendOfferRequestData, sessionID: string): IItemEventRouterResponse public extendOffer(pmcData: IPmcData, info: IExtendOfferRequestData, sessionID: string): IItemEventRouterResponse {
{
return this.ragfairController.extendOffer(info, sessionID); return this.ragfairController.extendOffer(info, sessionID);
} }
@ -117,14 +106,12 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
url: string, url: string,
request: IEmptyRequestData, request: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<Record<string, number>> ): IGetBodyResponseData<Record<string, number>> {
{
return this.httpResponse.getBody(this.ragfairController.getAllFleaPrices()); return this.httpResponse.getBody(this.ragfairController.getAllFleaPrices());
} }
/** Handle client/reports/ragfair/send */ /** Handle client/reports/ragfair/send */
public sendReport(url: string, info: ISendRagfairReportRequestData, sessionID: string): INullResponseData public sendReport(url: string, info: ISendRagfairReportRequestData, sessionID: string): INullResponseData {
{
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -132,8 +119,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
url: string, url: string,
request: IStorePlayerOfferTaxAmountRequestData, request: IStorePlayerOfferTaxAmountRequestData,
sessionId: string, sessionId: string,
): INullResponseData ): INullResponseData {
{
this.ragfairTaxService.storeClientOfferTaxValue(sessionId, request); this.ragfairTaxService.storeClientOfferTaxValue(sessionId, request);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }
@ -143,8 +129,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
url: string, url: string,
request: IGetRagfairOfferByIdRequest, request: IGetRagfairOfferByIdRequest,
sessionID: string, sessionID: string,
): IGetBodyResponseData<IRagfairOffer> ): IGetBodyResponseData<IRagfairOffer> {
{
return this.httpResponse.getBody(this.ragfairController.getOfferById(sessionID, request)); return this.httpResponse.getBody(this.ragfairController.getOfferById(sessionID, request));
} }
} }

View File

@ -1,15 +1,13 @@
import { inject, injectable } from "tsyringe";
import { RepairController } from "@spt/controllers/RepairController"; import { RepairController } from "@spt/controllers/RepairController";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IRepairActionDataRequest } from "@spt/models/eft/repair/IRepairActionDataRequest"; import { IRepairActionDataRequest } from "@spt/models/eft/repair/IRepairActionDataRequest";
import { ITraderRepairActionDataRequest } from "@spt/models/eft/repair/ITraderRepairActionDataRequest"; import { ITraderRepairActionDataRequest } from "@spt/models/eft/repair/ITraderRepairActionDataRequest";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class RepairCallbacks export class RepairCallbacks {
{ constructor(@inject("RepairController") protected repairController: RepairController) {}
constructor(@inject("RepairController") protected repairController: RepairController)
{}
/** /**
* Handle TraderRepair event * Handle TraderRepair event
@ -23,8 +21,7 @@ export class RepairCallbacks
pmcData: IPmcData, pmcData: IPmcData,
traderRepairRequest: ITraderRepairActionDataRequest, traderRepairRequest: ITraderRepairActionDataRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.repairController.traderRepair(sessionID, traderRepairRequest, pmcData); return this.repairController.traderRepair(sessionID, traderRepairRequest, pmcData);
} }
@ -40,8 +37,7 @@ export class RepairCallbacks
pmcData: IPmcData, pmcData: IPmcData,
repairRequest: IRepairActionDataRequest, repairRequest: IRepairActionDataRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.repairController.repairWithKit(sessionID, repairRequest, pmcData); return this.repairController.repairWithKit(sessionID, repairRequest, pmcData);
} }
} }

View File

@ -1,39 +1,33 @@
import { inject, injectable } from "tsyringe";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class SaveCallbacks implements OnLoad, OnUpdate export class SaveCallbacks implements OnLoad, OnUpdate {
{
protected coreConfig: ICoreConfig; protected coreConfig: ICoreConfig;
constructor( constructor(
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
} }
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.saveServer.load(); this.saveServer.load();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-save"; return "spt-save";
} }
public async onUpdate(secondsSinceLastRun: number): Promise<boolean> public async onUpdate(secondsSinceLastRun: number): Promise<boolean> {
{
// run every 15 seconds // run every 15 seconds
if (secondsSinceLastRun > this.coreConfig.profileSaveIntervalSeconds) if (secondsSinceLastRun > this.coreConfig.profileSaveIntervalSeconds) {
{
this.saveServer.save(); this.saveServer.save();
return true; return true;
} }

View File

@ -1,16 +1,14 @@
import { inject, injectable } from "tsyringe";
import { TradeController } from "@spt/controllers/TradeController"; import { TradeController } from "@spt/controllers/TradeController";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData"; import { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData";
import { IProcessRagfairTradeRequestData } from "@spt/models/eft/trade/IProcessRagfairTradeRequestData"; import { IProcessRagfairTradeRequestData } from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData"; import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class TradeCallbacks export class TradeCallbacks {
{ constructor(@inject("TradeController") protected tradeController: TradeController) {}
constructor(@inject("TradeController") protected tradeController: TradeController)
{}
/** /**
* Handle client/game/profile/items/moving TradingConfirm event * Handle client/game/profile/items/moving TradingConfirm event
@ -19,8 +17,7 @@ export class TradeCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IProcessBaseTradeRequestData, body: IProcessBaseTradeRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// body can be IProcessBuyTradeRequestData or IProcessSellTradeRequestData // body can be IProcessBuyTradeRequestData or IProcessSellTradeRequestData
return this.tradeController.confirmTrading(pmcData, body, sessionID); return this.tradeController.confirmTrading(pmcData, body, sessionID);
} }
@ -30,8 +27,7 @@ export class TradeCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: IProcessRagfairTradeRequestData, body: IProcessRagfairTradeRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.tradeController.confirmRagfairTrading(pmcData, body, sessionID); return this.tradeController.confirmRagfairTrading(pmcData, body, sessionID);
} }
@ -40,8 +36,7 @@ export class TradeCallbacks
pmcData: IPmcData, pmcData: IPmcData,
body: ISellScavItemsToFenceRequestData, body: ISellScavItemsToFenceRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.tradeController.sellScavItemsToFence(pmcData, body, sessionID); return this.tradeController.sellScavItemsToFence(pmcData, body, sessionID);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { TraderController } from "@spt/controllers/TraderController"; import { TraderController } from "@spt/controllers/TraderController";
import { OnLoad } from "@spt/di/OnLoad"; import { OnLoad } from "@spt/di/OnLoad";
import { OnUpdate } from "@spt/di/OnUpdate"; import { OnUpdate } from "@spt/di/OnUpdate";
@ -6,28 +5,24 @@ import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { ITraderAssort, ITraderBase } from "@spt/models/eft/common/tables/ITrader"; import { ITraderAssort, ITraderBase } from "@spt/models/eft/common/tables/ITrader";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class TraderCallbacks implements OnLoad, OnUpdate export class TraderCallbacks implements OnLoad, OnUpdate {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, // TODO: delay required @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, // TODO: delay required
@inject("TraderController") protected traderController: TraderController, @inject("TraderController") protected traderController: TraderController,
) ) {}
{}
public async onLoad(): Promise<void> public async onLoad(): Promise<void> {
{
this.traderController.load(); this.traderController.load();
} }
public async onUpdate(): Promise<boolean> public async onUpdate(): Promise<boolean> {
{
return this.traderController.update(); return this.traderController.update();
} }
public getRoute(): string public getRoute(): string {
{
return "spt-traders"; return "spt-traders";
} }
@ -36,21 +31,18 @@ export class TraderCallbacks implements OnLoad, OnUpdate
url: string, url: string,
info: IEmptyRequestData, info: IEmptyRequestData,
sessionID: string, sessionID: string,
): IGetBodyResponseData<ITraderBase[]> ): IGetBodyResponseData<ITraderBase[]> {
{
return this.httpResponse.getBody(this.traderController.getAllTraders(sessionID)); return this.httpResponse.getBody(this.traderController.getAllTraders(sessionID));
} }
/** Handle client/trading/api/getTrader */ /** Handle client/trading/api/getTrader */
public getTrader(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ITraderBase> public getTrader(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ITraderBase> {
{
const traderID = url.replace("/client/trading/api/getTrader/", ""); const traderID = url.replace("/client/trading/api/getTrader/", "");
return this.httpResponse.getBody(this.traderController.getTrader(sessionID, traderID)); return this.httpResponse.getBody(this.traderController.getTrader(sessionID, traderID));
} }
/** Handle client/trading/api/getTraderAssort */ /** Handle client/trading/api/getTraderAssort */
public getAssort(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ITraderAssort> public getAssort(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ITraderAssort> {
{
const traderID = url.replace("/client/trading/api/getTraderAssort/", ""); const traderID = url.replace("/client/trading/api/getTraderAssort/", "");
return this.httpResponse.getBody(this.traderController.getAssort(sessionID, traderID)); return this.httpResponse.getBody(this.traderController.getAssort(sessionID, traderID));
} }

View File

@ -1,31 +1,31 @@
import { inject, injectable } from "tsyringe";
import { WeatherController } from "@spt/controllers/WeatherController"; import { WeatherController } from "@spt/controllers/WeatherController";
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData"; import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
import { IWeatherData } from "@spt/models/eft/weather/IWeatherData"; import { IWeatherData } from "@spt/models/eft/weather/IWeatherData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData"; import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class WeatherCallbacks export class WeatherCallbacks {
{
constructor( constructor(
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("WeatherController") protected weatherController: WeatherController, @inject("WeatherController") protected weatherController: WeatherController,
) ) {}
{}
/** /**
* Handle client/weather * Handle client/weather
* @returns IWeatherData * @returns IWeatherData
*/ */
public getWeather(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IWeatherData> public getWeather(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IWeatherData> {
{
return this.httpResponse.getBody(this.weatherController.generate()); return this.httpResponse.getBody(this.weatherController.generate());
} }
public getLocalWeather(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetLocalWeatherResponseData> public getLocalWeather(
{ url: string,
info: IEmptyRequestData,
sessionID: string,
): IGetBodyResponseData<IGetLocalWeatherResponseData> {
return this.httpResponse.getBody(this.weatherController.generateLocal(sessionID)); return this.httpResponse.getBody(this.weatherController.generateLocal(sessionID));
} }
} }

View File

@ -1,24 +1,21 @@
import { inject, injectable } from "tsyringe";
import { WishlistController } from "@spt/controllers/WishlistController"; import { WishlistController } from "@spt/controllers/WishlistController";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest"; import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest";
import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest"; import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest";
import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest"; import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class WishlistCallbacks export class WishlistCallbacks {
{ constructor(@inject("WishlistController") protected wishlistController: WishlistController) {}
constructor(@inject("WishlistController") protected wishlistController: WishlistController)
{}
/** Handle AddToWishList event */ /** Handle AddToWishList event */
public addToWishlist( public addToWishlist(
pmcData: IPmcData, pmcData: IPmcData,
request: IAddToWishlistRequest, request: IAddToWishlistRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.wishlistController.addToWishList(pmcData, request, sessionID); return this.wishlistController.addToWishList(pmcData, request, sessionID);
} }
@ -27,8 +24,7 @@ export class WishlistCallbacks
pmcData: IPmcData, pmcData: IPmcData,
request: IRemoveFromWishlistRequest, request: IRemoveFromWishlistRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.wishlistController.removeFromWishList(pmcData, request, sessionID); return this.wishlistController.removeFromWishList(pmcData, request, sessionID);
} }
@ -37,8 +33,7 @@ export class WishlistCallbacks
pmcData: IPmcData, pmcData: IPmcData,
request: IChangeWishlistItemCategoryRequest, request: IChangeWishlistItemCategoryRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
return this.wishlistController.changeWishlistItemCategory(pmcData, request, sessionID); return this.wishlistController.changeWishlistItemCategory(pmcData, request, sessionID);
} }
} }

View File

@ -1,11 +1,10 @@
import { injectable } from "tsyringe";
import { ContextVariable } from "@spt/context/ContextVariable"; import { ContextVariable } from "@spt/context/ContextVariable";
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
import { LinkedList } from "@spt/utils/collections/lists/LinkedList"; import { LinkedList } from "@spt/utils/collections/lists/LinkedList";
import { injectable } from "tsyringe";
@injectable() @injectable()
export class ApplicationContext export class ApplicationContext {
{
private variables = new Map<ContextVariableType, LinkedList<ContextVariable>>(); private variables = new Map<ContextVariableType, LinkedList<ContextVariable>>();
private static holderMaxSize = 10; private static holderMaxSize = 10;
@ -19,23 +18,18 @@ export class ApplicationContext
* const matchInfo = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>(); * const matchInfo = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>();
* ``` * ```
*/ */
public getLatestValue(type: ContextVariableType): ContextVariable | undefined public getLatestValue(type: ContextVariableType): ContextVariable | undefined {
{ if (this.variables.has(type)) {
if (this.variables.has(type))
{
return this.variables.get(type)?.getTail(); return this.variables.get(type)?.getTail();
} }
return undefined; return undefined;
} }
public getValues(type: ContextVariableType): ContextVariable[] | undefined public getValues(type: ContextVariableType): ContextVariable[] | undefined {
{ if (this.variables.has(type)) {
if (this.variables.has(type))
{
const res: ContextVariable[] = []; const res: ContextVariable[] = [];
for (const value of this.variables.get(type)!.values()) for (const value of this.variables.get(type)!.values()) {
{
res.push(value); res.push(value);
} }
@ -44,20 +38,15 @@ export class ApplicationContext
return undefined; return undefined;
} }
public addValue(type: ContextVariableType, value: any): void public addValue(type: ContextVariableType, value: any): void {
{
let list: LinkedList<ContextVariable>; let list: LinkedList<ContextVariable>;
if (this.variables.has(type)) if (this.variables.has(type)) {
{
list = this.variables.get(type)!; list = this.variables.get(type)!;
} } else {
else
{
list = new LinkedList<ContextVariable>(); list = new LinkedList<ContextVariable>();
} }
if (list.length >= ApplicationContext.holderMaxSize) if (list.length >= ApplicationContext.holderMaxSize) {
{
list.shift(); list.shift();
} }
@ -65,10 +54,8 @@ export class ApplicationContext
this.variables.set(type, list); this.variables.set(type, list);
} }
public clearValues(type: ContextVariableType): void public clearValues(type: ContextVariableType): void {
{ if (this.variables.has(type)) {
if (this.variables.has(type))
{
this.variables.delete(type); this.variables.delete(type);
} }
} }

View File

@ -1,30 +1,25 @@
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
export class ContextVariable export class ContextVariable {
{
private value: any; private value: any;
private timestamp: Date; private timestamp: Date;
private type: ContextVariableType; private type: ContextVariableType;
constructor(value: any, type: ContextVariableType) constructor(value: any, type: ContextVariableType) {
{
this.value = value; this.value = value;
this.timestamp = new Date(); this.timestamp = new Date();
this.type = type; this.type = type;
} }
public getValue<T>(): T public getValue<T>(): T {
{
return this.value; return this.value;
} }
public getTimestamp(): Date public getTimestamp(): Date {
{
return this.timestamp; return this.timestamp;
} }
public getType(): ContextVariableType public getType(): ContextVariableType {
{
return this.type; return this.type;
} }
} }

View File

@ -1,5 +1,4 @@
export enum ContextVariableType export enum ContextVariableType {
{
/** Logged in users session id */ /** Logged in users session id */
SESSION_ID = 0, SESSION_ID = 0,
/** Currently acive raid information */ /** Currently acive raid information */

View File

@ -1,27 +1,24 @@
import { inject, injectable } from "tsyringe";
import { ICompletedAchievementsResponse } from "@spt/models/eft/profile/ICompletedAchievementsResponse"; import { ICompletedAchievementsResponse } from "@spt/models/eft/profile/ICompletedAchievementsResponse";
import { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse"; import { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { inject, injectable } from "tsyringe";
/** /**
* Logic for handling In Raid callbacks * Logic for handling In Raid callbacks
*/ */
@injectable() @injectable()
export class AchievementController export class AchievementController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("DatabaseService") protected databaseService: DatabaseService, @inject("DatabaseService") protected databaseService: DatabaseService,
) ) {}
{}
/** /**
* Get base achievements * Get base achievements
* @param sessionID Session id * @param sessionID Session id
*/ */
public getAchievements(sessionID: string): IGetAchievementsResponse public getAchievements(sessionID: string): IGetAchievementsResponse {
{
return { elements: this.databaseService.getAchievements() }; return { elements: this.databaseService.getAchievements() };
} }
@ -30,13 +27,11 @@ export class AchievementController
* @param sessionId Session id * @param sessionId Session id
* @returns ICompletedAchievementsResponse * @returns ICompletedAchievementsResponse
*/ */
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse {
{
const achievements = this.databaseService.getAchievements(); const achievements = this.databaseService.getAchievements();
const stats = {}; const stats = {};
for (const achievement of achievements) for (const achievement of achievements) {
{
stats[achievement.id] = 0; stats[achievement.id] = 0;
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt/context/ApplicationContext"; import { ApplicationContext } from "@spt/context/ApplicationContext";
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
import { BotGenerator } from "@spt/generators/BotGenerator"; import { BotGenerator } from "@spt/generators/BotGenerator";
@ -25,12 +24,12 @@ import { DatabaseService } from "@spt/services/DatabaseService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService"; import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService"; import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class BotController export class BotController {
{
protected botConfig: IBotConfig; protected botConfig: IBotConfig;
protected pmcConfig: IPmcConfig; protected pmcConfig: IPmcConfig;
@ -50,8 +49,7 @@ export class BotController
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("RandomUtil") protected randomUtil: RandomUtil, @inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC); this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
} }
@ -61,12 +59,10 @@ export class BotController
* @param type bot Type we want the load-out gen count for * @param type bot Type we want the load-out gen count for
* @returns number of bots to generate * @returns number of bots to generate
*/ */
public getBotPresetGenerationLimit(type: string): number public getBotPresetGenerationLimit(type: string): number {
{
const value = this.botConfig.presetBatch[type === "assaultGroup" ? "assault" : type]; const value = this.botConfig.presetBatch[type === "assaultGroup" ? "assault" : type];
if (!value) if (!value) {
{
this.logger.warning(this.localisationService.getText("bot-bot_preset_count_value_missing", type)); this.logger.warning(this.localisationService.getText("bot-bot_preset_count_value_missing", type));
return 30; return 30;
@ -80,8 +76,7 @@ export class BotController
* Get the core.json difficulty settings from database/bots * Get the core.json difficulty settings from database/bots
* @returns IBotCore * @returns IBotCore
*/ */
public getBotCoreDifficulty(): IBotCore public getBotCoreDifficulty(): IBotCore {
{
return this.databaseService.getBots().core; return this.databaseService.getBots().core;
} }
@ -93,15 +88,13 @@ export class BotController
* @param ignoreRaidSettings should raid settings chosen pre-raid be ignored * @param ignoreRaidSettings should raid settings chosen pre-raid be ignored
* @returns Difficulty object * @returns Difficulty object
*/ */
public getBotDifficulty(type: string, diffLevel: string, ignoreRaidSettings = false): Difficulty public getBotDifficulty(type: string, diffLevel: string, ignoreRaidSettings = false): Difficulty {
{
let difficulty = diffLevel.toLowerCase(); let difficulty = diffLevel.toLowerCase();
const raidConfig = this.applicationContext const raidConfig = this.applicationContext
.getLatestValue(ContextVariableType.RAID_CONFIGURATION) .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.getValue<IGetRaidConfigurationRequestData>(); ?.getValue<IGetRaidConfigurationRequestData>();
if (!(raidConfig || ignoreRaidSettings)) if (!(raidConfig || ignoreRaidSettings)) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"), this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"),
); );
@ -110,16 +103,14 @@ export class BotController
// Check value chosen in pre-raid difficulty dropdown // Check value chosen in pre-raid difficulty dropdown
// If value is not 'asonline', change requested difficulty to be what was chosen in dropdown // If value is not 'asonline', change requested difficulty to be what was chosen in dropdown
const botDifficultyDropDownValue = raidConfig?.wavesSettings.botDifficulty.toLowerCase() ?? "asonline"; const botDifficultyDropDownValue = raidConfig?.wavesSettings.botDifficulty.toLowerCase() ?? "asonline";
if (botDifficultyDropDownValue !== "asonline") if (botDifficultyDropDownValue !== "asonline") {
{ difficulty =
difficulty this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
= this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
} }
let difficultySettings: Difficulty; let difficultySettings: Difficulty;
const lowercasedBotType = type.toLowerCase(); const lowercasedBotType = type.toLowerCase();
switch (lowercasedBotType) switch (lowercasedBotType) {
{
case this.pmcConfig.bearType.toLowerCase(): case this.pmcConfig.bearType.toLowerCase():
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings( difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
"bear", "bear",
@ -144,14 +135,12 @@ export class BotController
return difficultySettings; return difficultySettings;
} }
public getAllBotDifficulties(): Record<string, any> public getAllBotDifficulties(): Record<string, any> {
{
const result = {}; const result = {};
const botTypesDb = this.databaseService.getBots().types; const botTypesDb = this.databaseService.getBots().types;
const botTypes = Object.keys(WildSpawnTypeNumber).filter((v) => Number.isNaN(Number(v))); const botTypes = Object.keys(WildSpawnTypeNumber).filter((v) => Number.isNaN(Number(v)));
for (let botType of botTypes) for (let botType of botTypes) {
{
const enumType = botType.toLowerCase(); const enumType = botType.toLowerCase();
// pmcBEAR/pmcUSEC need to be converted into `usec`/`bear` so we can read difficulty settings from bots/types // pmcBEAR/pmcUSEC need to be converted into `usec`/`bear` so we can read difficulty settings from bots/types
botType = this.botHelper.isBotPmc(botType) botType = this.botHelper.isBotPmc(botType)
@ -159,15 +148,13 @@ export class BotController
: botType.toLowerCase(); : botType.toLowerCase();
const botDetails = botTypesDb[botType]; const botDetails = botTypesDb[botType];
if (!botDetails?.difficulty) if (!botDetails?.difficulty) {
{
continue; continue;
} }
const botDifficulties = Object.keys(botDetails.difficulty); const botDifficulties = Object.keys(botDetails.difficulty);
result[enumType] = {}; result[enumType] = {};
for (const difficulty of botDifficulties) for (const difficulty of botDifficulties) {
{
result[enumType][difficulty] = this.getBotDifficulty(enumType, difficulty, true); result[enumType][difficulty] = this.getBotDifficulty(enumType, difficulty, true);
} }
} }
@ -181,14 +168,12 @@ export class BotController
* @param info bot generation request info * @param info bot generation request info
* @returns IBotBase array * @returns IBotBase array
*/ */
public async generate(sessionId: string, info: IGenerateBotsRequestData): Promise<IBotBase[]> public async generate(sessionId: string, info: IGenerateBotsRequestData): Promise<IBotBase[]> {
{
const pmcProfile = this.profileHelper.getPmcProfile(sessionId); const pmcProfile = this.profileHelper.getPmcProfile(sessionId);
// Use this opportunity to create and cache bots for later retreval // Use this opportunity to create and cache bots for later retreval
const multipleBotTypesRequested = info.conditions.length > 1; const multipleBotTypesRequested = info.conditions.length > 1;
if (multipleBotTypesRequested) if (multipleBotTypesRequested) {
{
return this.generateMultipleBotsAndCache(info, pmcProfile, sessionId); return this.generateMultipleBotsAndCache(info, pmcProfile, sessionId);
} }
@ -206,26 +191,23 @@ export class BotController
request: IGenerateBotsRequestData, request: IGenerateBotsRequestData,
pmcProfile: IPmcData, pmcProfile: IPmcData,
sessionId: string, sessionId: string,
): Promise<IBotBase[]> ): Promise<IBotBase[]> {
{
const raidSettings = this.applicationContext const raidSettings = this.applicationContext
.getLatestValue(ContextVariableType.RAID_CONFIGURATION) .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.getValue<IGetRaidConfigurationRequestData>(); ?.getValue<IGetRaidConfigurationRequestData>();
if (raidSettings === undefined) if (raidSettings === undefined) {
{
// throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext")); // throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext"));
} }
const pmcLevelRangeForMap const pmcLevelRangeForMap =
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings?.location.toLowerCase()]; this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings?.location.toLowerCase()];
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100( const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance, this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
); );
const conditionPromises: Promise<void>[] = []; const conditionPromises: Promise<void>[] = [];
for (const condition of request.conditions) for (const condition of request.conditions) {
{
const botGenerationDetails = this.getBotGenerationDetailsForWave( const botGenerationDetails = this.getBotGenerationDetailsForWave(
condition, condition,
pmcProfile, pmcProfile,
@ -260,8 +242,7 @@ export class BotController
pmcLevelRangeForMap: MinMax, pmcLevelRangeForMap: MinMax,
botCountToGenerate: number, botCountToGenerate: number,
generateAsPmc: boolean, generateAsPmc: boolean,
): BotGenerationDetails ): BotGenerationDetails {
{
return { return {
isPmc: generateAsPmc, isPmc: generateAsPmc,
side: "Savage", side: "Savage",
@ -283,8 +264,7 @@ export class BotController
* @param pmcProfile Profile to get level from * @param pmcProfile Profile to get level from
* @returns Level as number * @returns Level as number
*/ */
protected getPlayerLevelFromProfile(pmcProfile: IPmcData): number protected getPlayerLevelFromProfile(pmcProfile: IPmcData): number {
{
return pmcProfile.Info.Level; return pmcProfile.Info.Level;
} }
@ -299,11 +279,9 @@ export class BotController
condition: Condition, condition: Condition,
botGenerationDetails: BotGenerationDetails, botGenerationDetails: BotGenerationDetails,
sessionId: string, sessionId: string,
): Promise<void> ): Promise<void> {
{
const isEventBot = condition.Role.toLowerCase().includes("event"); const isEventBot = condition.Role.toLowerCase().includes("event");
if (isEventBot) if (isEventBot) {
{
// Add eventRole data + reassign role property to be base type // Add eventRole data + reassign role property to be base type
botGenerationDetails.eventRole = condition.Role; botGenerationDetails.eventRole = condition.Role;
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot( botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
@ -313,8 +291,7 @@ export class BotController
// Custom map waves can have spt roles in them // Custom map waves can have spt roles in them
// Is bot type pmcUSEC/pmcBEAR, set is pmc true and set side // Is bot type pmcUSEC/pmcBEAR, set is pmc true and set side
if (this.botHelper.botRoleIsPmc(condition.Role)) if (this.botHelper.botRoleIsPmc(condition.Role)) {
{
botGenerationDetails.isPmc = true; botGenerationDetails.isPmc = true;
botGenerationDetails.side = this.botHelper.getPmcSideByRole(condition.Role); botGenerationDetails.side = this.botHelper.getPmcSideByRole(condition.Role);
} }
@ -328,20 +305,17 @@ export class BotController
// Get number of bots we have in cache // Get number of bots we have in cache
const botCacheCount = this.botGenerationCacheService.getCachedBotCount(cacheKey); const botCacheCount = this.botGenerationCacheService.getCachedBotCount(cacheKey);
const botPromises: Promise<void>[] = []; const botPromises: Promise<void>[] = [];
if (botCacheCount > botGenerationDetails.botCountToGenerate) if (botCacheCount > botGenerationDetails.botCountToGenerate) {
{
return; return;
} }
// We're below desired count, add bots to cache // We're below desired count, add bots to cache
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) {
{
const detailsClone = this.cloner.clone(botGenerationDetails); const detailsClone = this.cloner.clone(botGenerationDetails);
botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey)); botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey));
} }
return Promise.all(botPromises).then(() => return Promise.all(botPromises).then(() => {
{
this.logger.debug( this.logger.debug(
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${ `Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
botGenerationDetails.eventRole ?? "" botGenerationDetails.eventRole ?? ""
@ -361,8 +335,7 @@ export class BotController
botGenerationDetails: BotGenerationDetails, botGenerationDetails: BotGenerationDetails,
sessionId: string, sessionId: string,
cacheKey: string, cacheKey: string,
): Promise<void> ): Promise<void> {
{
const botToCache = this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails); const botToCache = this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
this.botGenerationCacheService.storeBots(cacheKey, [botToCache]); this.botGenerationCacheService.storeBots(cacheKey, [botToCache]);
@ -379,8 +352,7 @@ export class BotController
protected async returnSingleBotFromCache( protected async returnSingleBotFromCache(
sessionId: string, sessionId: string,
request: IGenerateBotsRequestData, request: IGenerateBotsRequestData,
): Promise<IBotBase[]> ): Promise<IBotBase[]> {
{
const pmcProfile = this.profileHelper.getPmcProfile(sessionId); const pmcProfile = this.profileHelper.getPmcProfile(sessionId);
const requestedBot = request.conditions[0]; const requestedBot = request.conditions[0];
@ -388,12 +360,11 @@ export class BotController
.getLatestValue(ContextVariableType.RAID_CONFIGURATION) .getLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.getValue<IGetRaidConfigurationRequestData>(); ?.getValue<IGetRaidConfigurationRequestData>();
if (raidSettings === undefined) if (raidSettings === undefined) {
{
throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext")); throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext"));
} }
const pmcLevelRangeForMap const pmcLevelRangeForMap =
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()]; this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
// Create gen request for when cache is empty // Create gen request for when cache is empty
const condition: Condition = { const condition: Condition = {
@ -412,8 +383,7 @@ export class BotController
// Event bots need special actions to occur, set data up for them // Event bots need special actions to occur, set data up for them
const isEventBot = requestedBot.Role.toLowerCase().includes("event"); const isEventBot = requestedBot.Role.toLowerCase().includes("event");
if (isEventBot) if (isEventBot) {
{
// Add eventRole data + reassign role property // Add eventRole data + reassign role property
botGenerationDetails.eventRole = requestedBot.Role; botGenerationDetails.eventRole = requestedBot.Role;
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot( botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
@ -421,20 +391,17 @@ export class BotController
); );
} }
if (this.botHelper.isBotPmc(botGenerationDetails.role)) if (this.botHelper.isBotPmc(botGenerationDetails.role)) {
{
botGenerationDetails.isPmc = true; botGenerationDetails.isPmc = true;
botGenerationDetails.side = this.botHelper.getPmcSideByRole(requestedBot.Role); botGenerationDetails.side = this.botHelper.getPmcSideByRole(requestedBot.Role);
} }
// Roll chance to be pmc if type is allowed to be one // Roll chance to be pmc if type is allowed to be one
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[requestedBot.Role.toLowerCase()]; const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[requestedBot.Role.toLowerCase()];
if (botConvertRateMinMax) if (botConvertRateMinMax) {
{
// Should bot become PMC // Should bot become PMC
const convertToPmc = this.botHelper.rollChanceToBePmc(requestedBot.Role, botConvertRateMinMax); const convertToPmc = this.botHelper.rollChanceToBePmc(requestedBot.Role, botConvertRateMinMax);
if (convertToPmc) if (convertToPmc) {
{
botGenerationDetails.isPmc = true; botGenerationDetails.isPmc = true;
botGenerationDetails.role = this.botHelper.getRandomizedPmcRole(); botGenerationDetails.role = this.botHelper.getRandomizedPmcRole();
botGenerationDetails.side = this.botHelper.getPmcSideByRole(botGenerationDetails.role); botGenerationDetails.side = this.botHelper.getPmcSideByRole(botGenerationDetails.role);
@ -443,22 +410,16 @@ export class BotController
} }
} }
// Only convert to boss when not already converted to PMC & Boss Convert is enabled // Only convert to boss when not already converted to PMC & Boss Convert is enabled
const { const { bossConvertEnabled, bossConvertMinMax, bossesToConvertToWeights } =
bossConvertEnabled, this.botConfig.assaultToBossConversion;
bossConvertMinMax, if (bossConvertEnabled && !botGenerationDetails.isPmc) {
bossesToConvertToWeights } = this.botConfig.assaultToBossConversion;
if (bossConvertEnabled && !botGenerationDetails.isPmc)
{
const bossConvertPercent = bossConvertMinMax[requestedBot.Role.toLowerCase()]; const bossConvertPercent = bossConvertMinMax[requestedBot.Role.toLowerCase()];
if (bossConvertPercent) if (bossConvertPercent) {
{
// Roll a percentage check if we should convert scav to boss // Roll a percentage check if we should convert scav to boss
if (this.randomUtil.getChance100( if (
this.randomUtil.getInt(bossConvertPercent.min, bossConvertPercent.max))) this.randomUtil.getChance100(this.randomUtil.getInt(bossConvertPercent.min, bossConvertPercent.max))
{ ) {
this.updateBotGenerationDetailsToRandomBoss( this.updateBotGenerationDetailsToRandomBoss(botGenerationDetails, bossesToConvertToWeights);
botGenerationDetails,
bossesToConvertToWeights);
} }
} }
} }
@ -466,20 +427,18 @@ export class BotController
// Create a compound key to store bots in cache against // Create a compound key to store bots in cache against
const cacheKey = this.botGenerationCacheService.createCacheKey( const cacheKey = this.botGenerationCacheService.createCacheKey(
botGenerationDetails.eventRole ?? botGenerationDetails.role, botGenerationDetails.eventRole ?? botGenerationDetails.role,
botGenerationDetails.botDifficulty); botGenerationDetails.botDifficulty,
);
// Check cache for bot using above key // Check cache for bot using above key
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey)) if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey)) {
{
const botPromises: Promise<void>[] = []; const botPromises: Promise<void>[] = [];
// No bot in cache, generate new and return one // No bot in cache, generate new and return one
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) {
{
botPromises.push(this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey)); botPromises.push(this.generateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey));
} }
await Promise.all(botPromises).then(() => await Promise.all(botPromises).then(() => {
{
this.logger.debug( this.logger.debug(
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${ `Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
botGenerationDetails.eventRole ?? "" botGenerationDetails.eventRole ?? ""
@ -496,11 +455,10 @@ export class BotController
protected updateBotGenerationDetailsToRandomBoss( protected updateBotGenerationDetailsToRandomBoss(
botGenerationDetails: BotGenerationDetails, botGenerationDetails: BotGenerationDetails,
possibleBossTypeWeights: Record<string, number>): void possibleBossTypeWeights: Record<string, number>,
{ ): void {
// Seems Actual bosses have the same Brain issues like PMC gaining Boss Brains We cant use all bosses // Seems Actual bosses have the same Brain issues like PMC gaining Boss Brains We cant use all bosses
botGenerationDetails.role botGenerationDetails.role = this.weightedRandomHelper.getWeightedValue(possibleBossTypeWeights);
= this.weightedRandomHelper.getWeightedValue(possibleBossTypeWeights);
// Bosses are only ever 'normal' // Bosses are only ever 'normal'
botGenerationDetails.botDifficulty = "normal"; botGenerationDetails.botDifficulty = "normal";
@ -512,16 +470,13 @@ export class BotController
* @param requestedDifficulty * @param requestedDifficulty
* @returns * @returns
*/ */
public getPMCDifficulty(requestedDifficulty: string): string public getPMCDifficulty(requestedDifficulty: string): string {
{
// Maybe return a random difficulty... // Maybe return a random difficulty...
if (this.pmcConfig.difficulty.toLowerCase() === "asonline") if (this.pmcConfig.difficulty.toLowerCase() === "asonline") {
{
return requestedDifficulty; return requestedDifficulty;
} }
if (this.pmcConfig.difficulty.toLowerCase() === "random") if (this.pmcConfig.difficulty.toLowerCase() === "random") {
{
return this.botDifficultyHelper.chooseRandomDifficulty(); return this.botDifficultyHelper.chooseRandomDifficulty();
} }
@ -534,24 +489,18 @@ export class BotController
* @param location The map location cap was requested for * @param location The map location cap was requested for
* @returns cap number * @returns cap number
*/ */
public getBotCap(location: string): number public getBotCap(location: string): number {
{
const botCap = this.botConfig.maxBotCap[location.toLowerCase()]; const botCap = this.botConfig.maxBotCap[location.toLowerCase()];
if (location === "default") if (location === "default") {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText( this.localisationService.getText("bot-no_bot_cap_found_for_location", location.toLowerCase()),
"bot-no_bot_cap_found_for_location",
location.toLowerCase(),
),
); );
} }
return botCap; return botCap;
} }
public getAiBotBrainTypes(): any public getAiBotBrainTypes(): any {
{
return { return {
pmc: this.pmcConfig.pmcType, pmc: this.pmcConfig.pmcType,
assault: this.botConfig.assaultBrainType, assault: this.botConfig.assaultBrainType,

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { ISetMagazineRequest } from "@spt/models/eft/builds/ISetMagazineRequest"; import { ISetMagazineRequest } from "@spt/models/eft/builds/ISetMagazineRequest";
@ -11,12 +10,12 @@ import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class BuildController export class BuildController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@ -27,16 +26,13 @@ export class BuildController
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {}
{}
/** Handle client/handbook/builds/my/list */ /** Handle client/handbook/builds/my/list */
public getUserBuilds(sessionID: string): IUserBuilds public getUserBuilds(sessionID: string): IUserBuilds {
{
const secureContainerSlotId = "SecuredContainer"; const secureContainerSlotId = "SecuredContainer";
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
if (!profile.userbuilds) if (!profile.userbuilds) {
{
profile.userbuilds = { equipmentBuilds: [], weaponBuilds: [], magazineBuilds: [] }; profile.userbuilds = { equipmentBuilds: [], weaponBuilds: [], magazineBuilds: [] };
} }
@ -50,15 +46,12 @@ export class BuildController
const firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone[0]?.Items?.find( const firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone[0]?.Items?.find(
(x) => x.slotId === secureContainerSlotId, (x) => x.slotId === secureContainerSlotId,
); );
if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl) if (playerSecureContainer && playerSecureContainer?._tpl !== firstDefaultItemsSecureContainer?._tpl) {
{
// Default equipment presets' secure container tpl doesn't match players secure container tpl // Default equipment presets' secure container tpl doesn't match players secure container tpl
for (const defaultPreset of defaultEquipmentPresetsClone) for (const defaultPreset of defaultEquipmentPresetsClone) {
{
// Find presets secure container // Find presets secure container
const secureContainer = defaultPreset.Items.find((item) => item.slotId === secureContainerSlotId); const secureContainer = defaultPreset.Items.find((item) => item.slotId === secureContainerSlotId);
if (secureContainer) if (secureContainer) {
{
secureContainer._tpl = playerSecureContainer._tpl; secureContainer._tpl = playerSecureContainer._tpl;
} }
} }
@ -72,8 +65,7 @@ export class BuildController
} }
/** Handle client/builds/weapon/save */ /** Handle client/builds/weapon/save */
public saveWeaponBuild(sessionId: string, body: IPresetBuildActionRequestData): void public saveWeaponBuild(sessionId: string, body: IPresetBuildActionRequestData): void {
{
const pmcData = this.profileHelper.getPmcProfile(sessionId); const pmcData = this.profileHelper.getPmcProfile(sessionId);
// Replace duplicate Id's. The first item is the base item. // Replace duplicate Id's. The first item is the base item.
@ -86,28 +78,24 @@ export class BuildController
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds; const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id); const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
if (existingBuild) if (existingBuild) {
{
// exists, replace // exists, replace
this.saveServer this.saveServer
.getProfile(sessionId) .getProfile(sessionId)
.userbuilds.weaponBuilds.splice(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild); .userbuilds.weaponBuilds.splice(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild);
} } else {
else
{
// Add fresh // Add fresh
this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.push(newBuild); this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.push(newBuild);
} }
} }
/** Handle client/builds/equipment/save event */ /** Handle client/builds/equipment/save event */
public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void {
{
const buildType = "equipmentBuilds"; const buildType = "equipmentBuilds";
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const existingSavedEquipmentBuilds: IEquipmentBuild[] const existingSavedEquipmentBuilds: IEquipmentBuild[] =
= this.saveServer.getProfile(sessionID).userbuilds[buildType]; this.saveServer.getProfile(sessionID).userbuilds[buildType];
// Replace duplicate ID's. The first item is the base item. // Replace duplicate ID's. The first item is the base item.
// Root ID and the base item ID need to match. // Root ID and the base item ID need to match.
@ -124,28 +112,23 @@ export class BuildController
const existingBuild = existingSavedEquipmentBuilds.find( const existingBuild = existingSavedEquipmentBuilds.find(
(build) => build.Name === request.Name || build.Id === request.Id, (build) => build.Name === request.Name || build.Id === request.Id,
); );
if (existingBuild) if (existingBuild) {
{
// Already exists, replace // Already exists, replace
this.saveServer this.saveServer
.getProfile(sessionID) .getProfile(sessionID)
.userbuilds[buildType].splice(existingSavedEquipmentBuilds.indexOf(existingBuild), 1, newBuild); .userbuilds[buildType].splice(existingSavedEquipmentBuilds.indexOf(existingBuild), 1, newBuild);
} } else {
else
{
// Fresh, add new // Fresh, add new
this.saveServer.getProfile(sessionID).userbuilds[buildType].push(newBuild); this.saveServer.getProfile(sessionID).userbuilds[buildType].push(newBuild);
} }
} }
/** Handle client/builds/delete */ /** Handle client/builds/delete */
public removeBuild(sessionID: string, request: IRemoveBuildRequestData): void public removeBuild(sessionID: string, request: IRemoveBuildRequestData): void {
{
this.removePlayerBuild(request.id, sessionID); this.removePlayerBuild(request.id, sessionID);
} }
protected removePlayerBuild(idToRemove: string, sessionID: string): void protected removePlayerBuild(idToRemove: string, sessionID: string): void {
{
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
const weaponBuilds = profile.userbuilds.weaponBuilds; const weaponBuilds = profile.userbuilds.weaponBuilds;
const equipmentBuilds = profile.userbuilds.equipmentBuilds; const equipmentBuilds = profile.userbuilds.equipmentBuilds;
@ -153,8 +136,7 @@ export class BuildController
// Check for id in weapon array first // Check for id in weapon array first
const matchingWeaponBuild = weaponBuilds.find((weaponBuild) => weaponBuild.Id === idToRemove); const matchingWeaponBuild = weaponBuilds.find((weaponBuild) => weaponBuild.Id === idToRemove);
if (matchingWeaponBuild) if (matchingWeaponBuild) {
{
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1); weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
return; return;
@ -162,8 +144,7 @@ export class BuildController
// Id not found in weapons, try equipment // Id not found in weapons, try equipment
const matchingEquipmentBuild = equipmentBuilds.find((equipmentBuild) => equipmentBuild.Id === idToRemove); const matchingEquipmentBuild = equipmentBuilds.find((equipmentBuild) => equipmentBuild.Id === idToRemove);
if (matchingEquipmentBuild) if (matchingEquipmentBuild) {
{
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1); equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
return; return;
@ -171,8 +152,7 @@ export class BuildController
// Id not found in weapons/equipment, try mags // Id not found in weapons/equipment, try mags
const matchingMagazineBuild = magazineBuilds.find((magBuild) => magBuild.Id === idToRemove); const matchingMagazineBuild = magazineBuilds.find((magBuild) => magBuild.Id === idToRemove);
if (matchingMagazineBuild) if (matchingMagazineBuild) {
{
magazineBuilds.splice(magazineBuilds.indexOf(matchingMagazineBuild), 1); magazineBuilds.splice(magazineBuilds.indexOf(matchingMagazineBuild), 1);
return; return;
@ -185,8 +165,7 @@ export class BuildController
/** /**
* Handle client/builds/magazine/save * Handle client/builds/magazine/save
*/ */
public createMagazineTemplate(sessionId: string, request: ISetMagazineRequest): void public createMagazineTemplate(sessionId: string, request: ISetMagazineRequest): void {
{
const result: IMagazineBuild = { const result: IMagazineBuild = {
Id: request.Id, Id: request.Id,
Name: request.Name, Name: request.Name,
@ -201,12 +180,9 @@ export class BuildController
profile.userbuilds.magazineBuilds ||= []; profile.userbuilds.magazineBuilds ||= [];
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name); const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name);
if (existingArrayId === -1) if (existingArrayId === -1) {
{
profile.userbuilds.magazineBuilds.push(result); profile.userbuilds.magazineBuilds.push(result);
} } else {
else
{
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result); profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result);
} }
} }

View File

@ -1,21 +1,18 @@
import { inject, injectable } from "tsyringe";
import { IClientLogRequest } from "@spt/models/spt/logging/IClientLogRequest"; import { IClientLogRequest } from "@spt/models/spt/logging/IClientLogRequest";
import { LogBackgroundColor } from "@spt/models/spt/logging/LogBackgroundColor"; import { LogBackgroundColor } from "@spt/models/spt/logging/LogBackgroundColor";
import { LogLevel } from "@spt/models/spt/logging/LogLevel"; import { LogLevel } from "@spt/models/spt/logging/LogLevel";
import { LogTextColor } from "@spt/models/spt/logging/LogTextColor"; import { LogTextColor } from "@spt/models/spt/logging/LogTextColor";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class ClientLogController export class ClientLogController {
{ constructor(@inject("PrimaryLogger") protected logger: ILogger) {}
constructor(@inject("PrimaryLogger") protected logger: ILogger)
{}
/** /**
* Handle /singleplayer/log * Handle /singleplayer/log
*/ */
public clientLog(logRequest: IClientLogRequest): void public clientLog(logRequest: IClientLogRequest): void {
{
const message = `[${logRequest.Source}] ${logRequest.Message}`; const message = `[${logRequest.Source}] ${logRequest.Message}`;
const color = logRequest.Color ?? LogTextColor.WHITE; const color = logRequest.Color ?? LogTextColor.WHITE;
const backgroundColor = logRequest.BackgroundColor ?? LogBackgroundColor.DEFAULT; const backgroundColor = logRequest.BackgroundColor ?? LogBackgroundColor.DEFAULT;
@ -23,13 +20,11 @@ export class ClientLogController
// Allow supporting either string or enum levels // Allow supporting either string or enum levels
// Required due to the C# modules serializing enums as their name // Required due to the C# modules serializing enums as their name
let level = logRequest.Level; let level = logRequest.Level;
if (typeof level === "string") if (typeof level === "string") {
{
level = LogLevel[level.toUpperCase() as keyof typeof LogLevel]; level = LogLevel[level.toUpperCase() as keyof typeof LogLevel];
} }
switch (level) switch (level) {
{
case LogLevel.ERROR: case LogLevel.ERROR:
this.logger.error(message); this.logger.error(message);
break; break;

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { ISuit } from "@spt/models/eft/common/tables/ITrader"; import { ISuit } from "@spt/models/eft/common/tables/ITrader";
@ -10,10 +9,10 @@ import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class CustomizationController export class CustomizationController {
{
protected readonly clothingIds = { protected readonly clothingIds = {
lowerParentId: "5cd944d01388ce000a659df9", lowerParentId: "5cd944d01388ce000a659df9",
upperParentId: "5cd944ca1388ce03a44dc2a4", upperParentId: "5cd944ca1388ce03a44dc2a4",
@ -26,8 +25,7 @@ export class CustomizationController
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) ) {}
{}
/** /**
* Get purchasable clothing items from trader that match players side (usec/bear) * Get purchasable clothing items from trader that match players side (usec/bear)
@ -35,8 +33,7 @@ export class CustomizationController
* @param sessionID Session id * @param sessionID Session id
* @returns ISuit array * @returns ISuit array
*/ */
public getTraderSuits(traderID: string, sessionID: string): ISuit[] public getTraderSuits(traderID: string, sessionID: string): ISuit[] {
{
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const clothing = this.databaseService.getCustomization(); const clothing = this.databaseService.getCustomization();
const suits = this.databaseService.getTrader(traderID).suits; const suits = this.databaseService.getTrader(traderID).suits;
@ -45,7 +42,9 @@ export class CustomizationController
const matchingSuits = suits?.filter((suit) => suit.suiteId in clothing); const matchingSuits = suits?.filter((suit) => suit.suiteId in clothing);
// Return all suits that have a side array containing the players side (usec/bear) // Return all suits that have a side array containing the players side (usec/bear)
const matchedSuits = matchingSuits?.filter((matchingSuit) => clothing[matchingSuit.suiteId]._props.Side.includes(pmcData.Info.Side)); const matchedSuits = matchingSuits?.filter((matchingSuit) =>
clothing[matchingSuit.suiteId]._props.Side.includes(pmcData.Info.Side),
);
if (matchingSuits === undefined) if (matchingSuits === undefined)
throw new Error(this.localisationService.getText("customisation-unable_to_get_trader_suits", traderID)); throw new Error(this.localisationService.getText("customisation-unable_to_get_trader_suits", traderID));
@ -60,22 +59,18 @@ export class CustomizationController
pmcData: IPmcData, pmcData: IPmcData,
wearClothingRequest: IWearClothingRequestData, wearClothingRequest: IWearClothingRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{ for (const suitId of wearClothingRequest.suites) {
for (const suitId of wearClothingRequest.suites)
{
// Find desired clothing item in db // Find desired clothing item in db
const dbSuit = this.databaseService.getCustomization()[suitId]; const dbSuit = this.databaseService.getCustomization()[suitId];
// Legs // Legs
if (dbSuit._parent === this.clothingIds.lowerParentId) if (dbSuit._parent === this.clothingIds.lowerParentId) {
{
pmcData.Customization.Feet = dbSuit._props.Feet; pmcData.Customization.Feet = dbSuit._props.Feet;
} }
// Torso // Torso
if (dbSuit._parent === this.clothingIds.upperParentId) if (dbSuit._parent === this.clothingIds.upperParentId) {
{
pmcData.Customization.Body = dbSuit._props.Body; pmcData.Customization.Body = dbSuit._props.Body;
pmcData.Customization.Hands = dbSuit._props.Hands; pmcData.Customization.Hands = dbSuit._props.Hands;
} }
@ -96,13 +91,11 @@ export class CustomizationController
pmcData: IPmcData, pmcData: IPmcData,
buyClothingRequest: IBuyClothingRequestData, buyClothingRequest: IBuyClothingRequestData,
sessionId: string, sessionId: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer); const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
if (!traderOffer) if (!traderOffer) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer), this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer),
); );
@ -111,8 +104,7 @@ export class CustomizationController
} }
const suitId = traderOffer.suiteId; const suitId = traderOffer.suiteId;
if (this.outfitAlreadyPurchased(suitId, sessionId)) if (this.outfitAlreadyPurchased(suitId, sessionId)) {
{
const suitDetails = this.databaseService.getCustomization()[suitId]; const suitDetails = this.databaseService.getCustomization()[suitId];
this.logger.error( this.logger.error(
this.localisationService.getText("customisation-item_already_purchased", { this.localisationService.getText("customisation-item_already_purchased", {
@ -133,11 +125,9 @@ export class CustomizationController
return output; return output;
} }
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit {
{
const foundSuit = this.getAllTraderSuits(sessionId).find((x) => x._id === offerId); const foundSuit = this.getAllTraderSuits(sessionId).find((x) => x._id === offerId);
if (foundSuit === undefined) if (foundSuit === undefined) {
{
throw new Error(this.localisationService.getText("customisation-unable_to_find_suit_with_id", offerId)); throw new Error(this.localisationService.getText("customisation-unable_to_find_suit_with_id", offerId));
} }
@ -150,8 +140,7 @@ export class CustomizationController
* @param sessionID Session id of profile to check for clothing in * @param sessionID Session id of profile to check for clothing in
* @returns true if already purchased * @returns true if already purchased
*/ */
protected outfitAlreadyPurchased(suitId: string, sessionID: string): boolean protected outfitAlreadyPurchased(suitId: string, sessionID: string): boolean {
{
return this.saveServer.getProfile(sessionID).suits.includes(suitId); return this.saveServer.getProfile(sessionID).suits.includes(suitId);
} }
@ -167,10 +156,8 @@ export class CustomizationController
pmcData: IPmcData, pmcData: IPmcData,
clothingItems: ClothingItem[], clothingItems: ClothingItem[],
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{ for (const sellItem of clothingItems) {
for (const sellItem of clothingItems)
{
this.payForClothingItem(sessionId, pmcData, sellItem, output); this.payForClothingItem(sessionId, pmcData, sellItem, output);
} }
} }
@ -187,11 +174,9 @@ export class CustomizationController
pmcData: IPmcData, pmcData: IPmcData,
clothingItem: ClothingItem, clothingItem: ClothingItem,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const relatedItem = pmcData.Inventory.items.find((x) => x._id === clothingItem.id); const relatedItem = pmcData.Inventory.items.find((x) => x._id === clothingItem.id);
if (!relatedItem) if (!relatedItem) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"customisation-unable_to_find_clothing_item_in_inventory", "customisation-unable_to_find_clothing_item_in_inventory",
@ -202,19 +187,18 @@ export class CustomizationController
return; return;
} }
if (clothingItem.del === true) if (clothingItem.del === true) {
{
output.profileChanges[sessionId].items.del.push(relatedItem); output.profileChanges[sessionId].items.del.push(relatedItem);
pmcData.Inventory.items.splice(pmcData.Inventory.items.indexOf(relatedItem), 1); pmcData.Inventory.items.splice(pmcData.Inventory.items.indexOf(relatedItem), 1);
} }
if (!relatedItem.upd || !relatedItem.upd.StackObjectsCount) if (!relatedItem.upd || !relatedItem.upd.StackObjectsCount) {
{ throw new Error(
throw new Error(this.localisationService.getText("customisation-suit_lacks_upd_or_stack_property", relatedItem._tpl)); this.localisationService.getText("customisation-suit_lacks_upd_or_stack_property", relatedItem._tpl),
);
} }
if (relatedItem.upd.StackObjectsCount > clothingItem.count) if (relatedItem.upd.StackObjectsCount > clothingItem.count) {
{
relatedItem.upd.StackObjectsCount -= clothingItem.count; relatedItem.upd.StackObjectsCount -= clothingItem.count;
output.profileChanges[sessionId].items.change.push({ output.profileChanges[sessionId].items.change.push({
_id: relatedItem._id, _id: relatedItem._id,
@ -227,15 +211,12 @@ export class CustomizationController
} }
} }
protected getAllTraderSuits(sessionID: string): ISuit[] protected getAllTraderSuits(sessionID: string): ISuit[] {
{
const traders = this.databaseService.getTraders(); const traders = this.databaseService.getTraders();
let result: ISuit[] = []; let result: ISuit[] = [];
for (const traderID in traders) for (const traderID in traders) {
{ if (traders[traderID].base.customization_seller === true) {
if (traders[traderID].base.customization_seller === true)
{
result = [...result, ...this.getTraderSuits(traderID, sessionID)]; result = [...result, ...this.getTraderSuits(traderID, sessionID)];
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectAll, injectable } from "tsyringe";
import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot";
import { DialogueHelper } from "@spt/helpers/DialogueHelper"; import { DialogueHelper } from "@spt/helpers/DialogueHelper";
import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData"; import { IFriendRequestData } from "@spt/models/eft/dialog/IFriendRequestData";
@ -18,10 +17,10 @@ import { SaveServer } from "@spt/servers/SaveServer";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { MailSendService } from "@spt/services/MailSendService"; import { MailSendService } from "@spt/services/MailSendService";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectAll, injectable } from "tsyringe";
@injectable() @injectable()
export class DialogueController export class DialogueController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@ -31,37 +30,36 @@ export class DialogueController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@injectAll("DialogueChatBot") protected dialogueChatBots: IDialogueChatBot[], @injectAll("DialogueChatBot") protected dialogueChatBots: IDialogueChatBot[],
) ) {
{
const coreConfigs = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE); const coreConfigs = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE);
// if give command is disabled or commando commands are disabled // if give command is disabled or commando commands are disabled
if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled) if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled) {
{ const sptCommando = this.dialogueChatBots.find(
const sptCommando = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando")!; (c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando",
)!;
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1); this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1);
} }
if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled) if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled) {
{ const sptFriend = this.dialogueChatBots.find(
const sptFriend = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend")!; (c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend",
)!;
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1); this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1);
} }
} }
public registerChatBot(chatBot: IDialogueChatBot): void public registerChatBot(chatBot: IDialogueChatBot): void {
{ if (this.dialogueChatBots.some((cb) => cb.getChatBot()._id === chatBot.getChatBot()._id)) {
if (this.dialogueChatBots.some((cb) => cb.getChatBot()._id === chatBot.getChatBot()._id)) throw new Error(
{ this.localisationService.getText("dialog-chatbot_id_already_exists", chatBot.getChatBot()._id),
throw new Error(this.localisationService.getText("dialog-chatbot_id_already_exists", chatBot.getChatBot()._id)); );
} }
this.dialogueChatBots.push(chatBot); this.dialogueChatBots.push(chatBot);
} }
/** Handle onUpdate spt event */ /** Handle onUpdate spt event */
public update(): void public update(): void {
{
const profiles = this.saveServer.getProfiles(); const profiles = this.saveServer.getProfiles();
for (const sessionID in profiles) for (const sessionID in profiles) {
{
this.removeExpiredItemsFromMessages(sessionID); this.removeExpiredItemsFromMessages(sessionID);
} }
} }
@ -70,8 +68,7 @@ export class DialogueController
* Handle client/friend/list * Handle client/friend/list
* @returns IGetFriendListDataResponse * @returns IGetFriendListDataResponse
*/ */
public getFriendList(sessionID: string): IGetFriendListDataResponse public getFriendList(sessionID: string): IGetFriendListDataResponse {
{
// Force a fake friend called SPT into friend list // Force a fake friend called SPT into friend list
return { Friends: this.dialogueChatBots.map((v) => v.getChatBot()), Ignore: [], InIgnoreList: [] }; return { Friends: this.dialogueChatBots.map((v) => v.getChatBot()), Ignore: [], InIgnoreList: [] };
} }
@ -83,11 +80,9 @@ export class DialogueController
* @param sessionID Session Id * @param sessionID Session Id
* @returns array of dialogs * @returns array of dialogs
*/ */
public generateDialogueList(sessionID: string): DialogueInfo[] public generateDialogueList(sessionID: string): DialogueInfo[] {
{
const data: DialogueInfo[] = []; const data: DialogueInfo[] = [];
for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionID)) for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionID)) {
{
data.push(this.getDialogueInfo(dialogueId, sessionID)); data.push(this.getDialogueInfo(dialogueId, sessionID));
} }
@ -100,8 +95,7 @@ export class DialogueController
* @param sessionID Session Id * @param sessionID Session Id
* @returns DialogueInfo * @returns DialogueInfo
*/ */
public getDialogueInfo(dialogueID: string, sessionID: string): DialogueInfo public getDialogueInfo(dialogueID: string, sessionID: string): DialogueInfo {
{
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionID); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionID);
const dialogue = dialogs[dialogueID]; const dialogue = dialogs[dialogueID];
@ -125,18 +119,19 @@ export class DialogueController
* @param sessionID Player id * @param sessionID Player id
* @returns IUserDialogInfo array * @returns IUserDialogInfo array
*/ */
public getDialogueUsers(dialog: Dialogue, messageType: MessageType, sessionID: string): IUserDialogInfo[] | undefined public getDialogueUsers(
{ dialog: Dialogue,
messageType: MessageType,
sessionID: string,
): IUserDialogInfo[] | undefined {
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
// User to user messages are special in that they need the player to exist in them, add if they don't // User to user messages are special in that they need the player to exist in them, add if they don't
if ( if (
messageType === MessageType.USER_MESSAGE messageType === MessageType.USER_MESSAGE &&
&& !dialog.Users?.some((userDialog) => userDialog._id === profile.characters.pmc.sessionId) !dialog.Users?.some((userDialog) => userDialog._id === profile.characters.pmc.sessionId)
) ) {
{ if (!dialog.Users) {
if (!dialog.Users)
{
dialog.Users = []; dialog.Users = [];
} }
@ -168,8 +163,7 @@ export class DialogueController
public generateDialogueView( public generateDialogueView(
request: IGetMailDialogViewRequestData, request: IGetMailDialogViewRequestData,
sessionId: string, sessionId: string,
): IGetMailDialogViewResponseData ): IGetMailDialogViewResponseData {
{
const dialogueId = request.dialogId; const dialogueId = request.dialogId;
const fullProfile = this.saveServer.getProfile(sessionId); const fullProfile = this.saveServer.getProfile(sessionId);
const dialogue = this.getDialogByIdFromProfile(fullProfile, request); const dialogue = this.getDialogByIdFromProfile(fullProfile, request);
@ -193,10 +187,8 @@ export class DialogueController
* @param request get dialog request (params used when dialog doesnt exist in profile) * @param request get dialog request (params used when dialog doesnt exist in profile)
* @returns Dialogue * @returns Dialogue
*/ */
protected getDialogByIdFromProfile(profile: ISptProfile, request: IGetMailDialogViewRequestData): Dialogue protected getDialogByIdFromProfile(profile: ISptProfile, request: IGetMailDialogViewRequestData): Dialogue {
{ if (!profile.dialogues[request.dialogId]) {
if (!profile.dialogues[request.dialogId])
{
profile.dialogues[request.dialogId] = { profile.dialogues[request.dialogId] = {
_id: request.dialogId, _id: request.dialogId,
attachmentsNew: 0, attachmentsNew: 0,
@ -206,14 +198,11 @@ export class DialogueController
type: request.type, type: request.type,
}; };
if (request.type === MessageType.USER_MESSAGE) if (request.type === MessageType.USER_MESSAGE) {
{
profile.dialogues[request.dialogId].Users = []; profile.dialogues[request.dialogId].Users = [];
const chatBot = this.dialogueChatBots.find((cb) => cb.getChatBot()._id === request.dialogId); const chatBot = this.dialogueChatBots.find((cb) => cb.getChatBot()._id === request.dialogId);
if (chatBot) if (chatBot) {
{ if (!profile.dialogues[request.dialogId].Users) {
if (!profile.dialogues[request.dialogId].Users)
{
profile.dialogues[request.dialogId].Users = []; profile.dialogues[request.dialogId].Users = [];
} }
profile.dialogues[request.dialogId].Users!.push(chatBot.getChatBot()); profile.dialogues[request.dialogId].Users!.push(chatBot.getChatBot());
@ -230,15 +219,12 @@ export class DialogueController
* @param dialogUsers The participants of the mail * @param dialogUsers The participants of the mail
* @returns IUserDialogInfo array * @returns IUserDialogInfo array
*/ */
protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers?: IUserDialogInfo[]): IUserDialogInfo[] protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers?: IUserDialogInfo[]): IUserDialogInfo[] {
{
const result: IUserDialogInfo[] = []; const result: IUserDialogInfo[] = [];
if (dialogUsers) if (dialogUsers) {
{
result.push(...dialogUsers); result.push(...dialogUsers);
if (!result.some((userDialog) => userDialog._id === fullProfile.info.id)) if (!result.some((userDialog) => userDialog._id === fullProfile.info.id)) {
{
// Player doesnt exist, add them in before returning // Player doesnt exist, add them in before returning
const pmcProfile = fullProfile.characters.pmc; const pmcProfile = fullProfile.characters.pmc;
result.push({ result.push({
@ -264,14 +250,11 @@ export class DialogueController
* @param dialogueID Dialog id * @param dialogueID Dialog id
* @returns Count of messages with attachments * @returns Count of messages with attachments
*/ */
protected getUnreadMessagesWithAttachmentsCount(sessionID: string, dialogueID: string): number protected getUnreadMessagesWithAttachmentsCount(sessionID: string, dialogueID: string): number {
{
let newAttachmentCount = 0; let newAttachmentCount = 0;
const activeMessages = this.getActiveMessagesFromDialog(sessionID, dialogueID); const activeMessages = this.getActiveMessagesFromDialog(sessionID, dialogueID);
for (const message of activeMessages) for (const message of activeMessages) {
{ if (message.hasRewards && !message.rewardCollected) {
if (message.hasRewards && !message.rewardCollected)
{
newAttachmentCount++; newAttachmentCount++;
} }
} }
@ -284,8 +267,7 @@ export class DialogueController
* @param messages Messages to check * @param messages Messages to check
* @returns true if uncollected rewards found * @returns true if uncollected rewards found
*/ */
protected messagesHaveUncollectedRewards(messages: Message[]): boolean protected messagesHaveUncollectedRewards(messages: Message[]): boolean {
{
return messages.some((message) => (message.items?.data?.length ?? 0) > 0); return messages.some((message) => (message.items?.data?.length ?? 0) > 0);
} }
@ -295,13 +277,16 @@ export class DialogueController
* @param dialogueId id of the dialog to remove * @param dialogueId id of the dialog to remove
* @param sessionId Player id * @param sessionId Player id
*/ */
public removeDialogue(dialogueId: string, sessionId: string): void public removeDialogue(dialogueId: string, sessionId: string): void {
{
const profile = this.saveServer.getProfile(sessionId); const profile = this.saveServer.getProfile(sessionId);
const dialog = profile.dialogues[dialogueId]; const dialog = profile.dialogues[dialogueId];
if (!dialog) if (!dialog) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId })); this.localisationService.getText("dialogue-unable_to_find_in_profile", {
sessionId: sessionId,
dialogueId: dialogueId,
}),
);
return; return;
} }
@ -310,12 +295,15 @@ export class DialogueController
} }
/** Handle client/mail/dialog/pin && Handle client/mail/dialog/unpin */ /** Handle client/mail/dialog/pin && Handle client/mail/dialog/unpin */
public setDialoguePin(dialogueId: string, shouldPin: boolean, sessionId: string): void public setDialoguePin(dialogueId: string, shouldPin: boolean, sessionId: string): void {
{
const dialog = this.dialogueHelper.getDialogsForProfile(sessionId)[dialogueId]; const dialog = this.dialogueHelper.getDialogsForProfile(sessionId)[dialogueId];
if (!dialog) if (!dialog) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId })); this.localisationService.getText("dialogue-unable_to_find_in_profile", {
sessionId: sessionId,
dialogueId: dialogueId,
}),
);
return; return;
} }
@ -329,18 +317,19 @@ export class DialogueController
* @param dialogueIds Dialog ids to set as read * @param dialogueIds Dialog ids to set as read
* @param sessionId Player profile id * @param sessionId Player profile id
*/ */
public setRead(dialogueIds: string[], sessionId: string): void public setRead(dialogueIds: string[], sessionId: string): void {
{
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
if (!dialogs) if (!dialogs) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_dialogs_in_profile", { sessionId: sessionId })); this.localisationService.getText("dialogue-unable_to_find_dialogs_in_profile", {
sessionId: sessionId,
}),
);
return; return;
} }
for (const dialogId of dialogueIds) for (const dialogId of dialogueIds) {
{
dialogs[dialogId].new = 0; dialogs[dialogId].new = 0;
dialogs[dialogId].attachmentsNew = 0; dialogs[dialogId].attachmentsNew = 0;
} }
@ -353,13 +342,16 @@ export class DialogueController
* @param sessionId Session id * @param sessionId Session id
* @returns IGetAllAttachmentsResponse * @returns IGetAllAttachmentsResponse
*/ */
public getAllAttachments(dialogueId: string, sessionId: string): IGetAllAttachmentsResponse | undefined public getAllAttachments(dialogueId: string, sessionId: string): IGetAllAttachmentsResponse | undefined {
{
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
const dialog = dialogs[dialogueId]; const dialog = dialogs[dialogueId];
if (!dialog) if (!dialog) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId })); this.localisationService.getText("dialogue-unable_to_find_in_profile", {
sessionId: sessionId,
dialogueId: dialogueId,
}),
);
return undefined; return undefined;
} }
@ -378,8 +370,7 @@ export class DialogueController
} }
/** client/mail/msg/send */ /** client/mail/msg/send */
public sendMessage(sessionId: string, request: ISendMessageRequest): string public sendMessage(sessionId: string, request: ISendMessageRequest): string {
{
this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text); this.mailSendService.sendPlayerMessageToNpc(sessionId, request.dialogId, request.text);
return ( return (
@ -395,8 +386,7 @@ export class DialogueController
* @param dialogueId Dialog to get mail attachments from * @param dialogueId Dialog to get mail attachments from
* @returns Message array * @returns Message array
*/ */
protected getActiveMessagesFromDialog(sessionId: string, dialogueId: string): Message[] protected getActiveMessagesFromDialog(sessionId: string, dialogueId: string): Message[] {
{
const timeNow = this.timeUtil.getTimestamp(); const timeNow = this.timeUtil.getTimestamp();
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + (message.maxStorageTime ?? 0)); return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + (message.maxStorageTime ?? 0));
@ -407,8 +397,7 @@ export class DialogueController
* @param messages Messages to parse * @param messages Messages to parse
* @returns messages with items to collect * @returns messages with items to collect
*/ */
protected getMessagesWithAttachments(messages: Message[]): Message[] protected getMessagesWithAttachments(messages: Message[]): Message[] {
{
return messages.filter((message) => (message.items?.data?.length ?? 0) > 0); return messages.filter((message) => (message.items?.data?.length ?? 0) > 0);
} }
@ -416,10 +405,8 @@ export class DialogueController
* Delete expired items from all messages in player profile. triggers when updating traders. * Delete expired items from all messages in player profile. triggers when updating traders.
* @param sessionId Session id * @param sessionId Session id
*/ */
protected removeExpiredItemsFromMessages(sessionId: string): void protected removeExpiredItemsFromMessages(sessionId: string): void {
{ for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionId)) {
for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionId))
{
this.removeExpiredItemsFromMessage(sessionId, dialogueId); this.removeExpiredItemsFromMessage(sessionId, dialogueId);
} }
} }
@ -429,19 +416,15 @@ export class DialogueController
* @param sessionId Session id * @param sessionId Session id
* @param dialogueId Dialog id * @param dialogueId Dialog id
*/ */
protected removeExpiredItemsFromMessage(sessionId: string, dialogueId: string): void protected removeExpiredItemsFromMessage(sessionId: string, dialogueId: string): void {
{
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId); const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
const dialog = dialogs[dialogueId]; const dialog = dialogs[dialogueId];
if (!dialog.messages) if (!dialog.messages) {
{
return; return;
} }
for (const message of dialog.messages) for (const message of dialog.messages) {
{ if (this.messageHasExpired(message)) {
if (this.messageHasExpired(message))
{
message.items = {}; message.items = {};
} }
} }
@ -452,14 +435,12 @@ export class DialogueController
* @param message Message to check expiry of * @param message Message to check expiry of
* @returns true or false * @returns true or false
*/ */
protected messageHasExpired(message: Message): boolean protected messageHasExpired(message: Message): boolean {
{
return this.timeUtil.getTimestamp() > message.dt + (message.maxStorageTime ?? 0); return this.timeUtil.getTimestamp() > message.dt + (message.maxStorageTime ?? 0);
} }
/** Handle client/friend/request/send */ /** Handle client/friend/request/send */
public sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse public sendFriendRequest(sessionID: string, request: IFriendRequestData): IFriendRequestSendResponse {
{
return { status: 0, requestId: "12345", retryAfter: 600 }; return { status: 0, requestId: "12345", retryAfter: 600 };
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt/context/ApplicationContext"; import { ApplicationContext } from "@spt/context/ApplicationContext";
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
import { HideoutHelper } from "@spt/helpers/HideoutHelper"; import { HideoutHelper } from "@spt/helpers/HideoutHelper";
@ -44,14 +43,14 @@ import { ProfileActivityService } from "@spt/services/ProfileActivityService";
import { ProfileFixerService } from "@spt/services/ProfileFixerService"; import { ProfileFixerService } from "@spt/services/ProfileFixerService";
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService"; import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService"; import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class GameController export class GameController {
{
protected httpConfig: IHttpConfig; protected httpConfig: IHttpConfig;
protected coreConfig: ICoreConfig; protected coreConfig: ICoreConfig;
protected locationConfig: ILocationConfig; protected locationConfig: ILocationConfig;
@ -83,8 +82,7 @@ export class GameController
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION); this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
@ -95,8 +93,7 @@ export class GameController
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
} }
public load(): void public load(): void {
{
// Regenerate base cache now mods are loaded and game is starting // Regenerate base cache now mods are loaded and game is starting
// Mods that add items and use the baseClass service generate the cache including their items, the next mod that // Mods that add items and use the baseClass service generate the cache including their items, the next mod that
// add items gets left out,causing warnings // add items gets left out,causing warnings
@ -107,30 +104,25 @@ export class GameController
/** /**
* Handle client/game/start * Handle client/game/start
*/ */
public gameStart(_url: string, _info: IEmptyRequestData, sessionID: string, startTimeStampMS: number): void public gameStart(_url: string, _info: IEmptyRequestData, sessionID: string, startTimeStampMS: number): void {
{
// Store client start time in app context // Store client start time in app context
this.applicationContext.addValue(ContextVariableType.CLIENT_START_TIMESTAMP, startTimeStampMS); this.applicationContext.addValue(ContextVariableType.CLIENT_START_TIMESTAMP, startTimeStampMS);
this.profileActivityService.setActivityTimestamp(sessionID); this.profileActivityService.setActivityTimestamp(sessionID);
if (this.coreConfig.fixes.fixShotgunDispersion) if (this.coreConfig.fixes.fixShotgunDispersion) {
{
this.fixShotgunDispersions(); this.fixShotgunDispersions();
} }
if (this.locationConfig.addOpenZonesToAllMaps) if (this.locationConfig.addOpenZonesToAllMaps) {
{
this.openZoneService.applyZoneChangesToAllMaps(); this.openZoneService.applyZoneChangesToAllMaps();
} }
if (this.locationConfig.addCustomBotWavesToMaps) if (this.locationConfig.addCustomBotWavesToMaps) {
{
this.customLocationWaveService.applyWaveChangesToAllMaps(); this.customLocationWaveService.applyWaveChangesToAllMaps();
} }
if (this.locationConfig.enableBotTypeLimits) if (this.locationConfig.enableBotTypeLimits) {
{
this.adjustMapBotLimits(); this.adjustMapBotLimits();
} }
@ -143,22 +135,18 @@ export class GameController
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in // repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or // offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
// successful) repeatable quests. We also have to remove the Counters from the repeatableQuests // successful) repeatable quests. We also have to remove the Counters from the repeatableQuests
if (sessionID) if (sessionID) {
{
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
if (fullProfile.info.wipe) if (fullProfile.info.wipe) {
{
// Don't bother doing any fixes, we're resetting profile // Don't bother doing any fixes, we're resetting profile
return; return;
} }
if (Array.isArray(fullProfile.characters.pmc.WishList)) if (Array.isArray(fullProfile.characters.pmc.WishList)) {
{
fullProfile.characters.pmc.WishList = {}; fullProfile.characters.pmc.WishList = {};
} }
if (Array.isArray(fullProfile.characters.scav.WishList)) if (Array.isArray(fullProfile.characters.scav.WishList)) {
{
fullProfile.characters.scav.WishList = {}; fullProfile.characters.scav.WishList = {};
} }
@ -167,34 +155,28 @@ export class GameController
this.logger.debug(`Started game with sessionId: ${sessionID} ${pmcProfile.Info?.Nickname}`); this.logger.debug(`Started game with sessionId: ${sessionID} ${pmcProfile.Info?.Nickname}`);
// Migrate aki object data into spt for 3.9.0 release // Migrate aki object data into spt for 3.9.0 release
if ((fullProfile as any).aki) if ((fullProfile as any).aki) {
{
fullProfile.spt = this.cloner.clone((fullProfile as any).aki); fullProfile.spt = this.cloner.clone((fullProfile as any).aki);
delete (fullProfile as any).aki; delete (fullProfile as any).aki;
} }
if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues) if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues) {
{
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile); this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile);
} }
if (pmcProfile.Health) if (pmcProfile.Health) {
{
this.updateProfileHealthValues(pmcProfile); this.updateProfileHealthValues(pmcProfile);
} }
if (this.locationConfig.fixEmptyBotWavesSettings.enabled) if (this.locationConfig.fixEmptyBotWavesSettings.enabled) {
{
this.fixBrokenOfflineMapWaves(); this.fixBrokenOfflineMapWaves();
} }
if (this.locationConfig.rogueLighthouseSpawnTimeSettings.enabled) if (this.locationConfig.rogueLighthouseSpawnTimeSettings.enabled) {
{
this.fixRoguesSpawningInstantlyOnLighthouse(); this.fixRoguesSpawningInstantlyOnLighthouse();
} }
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.enabled) if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.enabled) {
{
this.splitBotWavesIntoSingleWaves(); this.splitBotWavesIntoSingleWaves();
} }
@ -202,8 +184,7 @@ export class GameController
this.profileFixerService.addMissingHideoutAreasToProfile(fullProfile); this.profileFixerService.addMissingHideoutAreasToProfile(fullProfile);
if (pmcProfile.Inventory) if (pmcProfile.Inventory) {
{
// MUST occur prior to `profileFixerService.checkForAndFixPmcProfileIssues()` // MUST occur prior to `profileFixerService.checkForAndFixPmcProfileIssues()`
this.profileFixerService.fixIncorrectAidValue(fullProfile); this.profileFixerService.fixIncorrectAidValue(fullProfile);
@ -218,8 +199,7 @@ export class GameController
this.profileFixerService.addMissingSptVersionTagToProfile(fullProfile); this.profileFixerService.addMissingSptVersionTagToProfile(fullProfile);
if (pmcProfile.Hideout) if (pmcProfile.Hideout) {
{
this.profileFixerService.addMissingHideoutBonusesToProfile(pmcProfile); this.profileFixerService.addMissingHideoutBonusesToProfile(pmcProfile);
this.profileFixerService.addMissingUpgradesPropertyToHideout(pmcProfile); this.profileFixerService.addMissingUpgradesPropertyToHideout(pmcProfile);
this.hideoutHelper.setHideoutImprovementsToCompleted(pmcProfile); this.hideoutHelper.setHideoutImprovementsToCompleted(pmcProfile);
@ -241,40 +221,33 @@ export class GameController
this.validateQuestAssortUnlocksExist(); this.validateQuestAssortUnlocksExist();
if (pmcProfile.Info) if (pmcProfile.Info) {
{
this.addPlayerToPMCNames(pmcProfile); this.addPlayerToPMCNames(pmcProfile);
this.checkForAndRemoveUndefinedDialogs(fullProfile); this.checkForAndRemoveUndefinedDialogs(fullProfile);
} }
if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) {
{
this.seasonalEventService.enableSeasonalEvents(sessionID); this.seasonalEventService.enableSeasonalEvents(sessionID);
} }
if (pmcProfile?.Skills?.Common) if (pmcProfile?.Skills?.Common) {
{
this.warnOnActiveBotReloadSkill(pmcProfile); this.warnOnActiveBotReloadSkill(pmcProfile);
} }
// Flea bsg blacklist is off // Flea bsg blacklist is off
if (!this.ragfairConfig.dynamic.blacklist.enableBsgList) if (!this.ragfairConfig.dynamic.blacklist.enableBsgList) {
{
this.setAllDbItemsAsSellableOnFlea(); this.setAllDbItemsAsSellableOnFlea();
} }
} }
} }
protected adjustHideoutCraftTimes(overrideSeconds: number): void protected adjustHideoutCraftTimes(overrideSeconds: number): void {
{ if (overrideSeconds === -1) {
if (overrideSeconds === -1)
{
return; return;
} }
for (const craft of this.databaseService.getHideout().production) for (const craft of this.databaseService.getHideout().production) {
{
// Only adjust crafts ABOVE the override // Only adjust crafts ABOVE the override
craft.productionTime = Math.min(craft.productionTime, overrideSeconds); craft.productionTime = Math.min(craft.productionTime, overrideSeconds);
} }
@ -283,32 +256,25 @@ export class GameController
/** /**
* Adjust all hideout craft times to be no higher than the override * Adjust all hideout craft times to be no higher than the override
*/ */
protected adjustHideoutBuildTimes(overrideSeconds: number): void protected adjustHideoutBuildTimes(overrideSeconds: number): void {
{ if (overrideSeconds === -1) {
if (overrideSeconds === -1)
{
return; return;
} }
for (const area of this.databaseService.getHideout().areas) for (const area of this.databaseService.getHideout().areas) {
{ for (const stage of Object.values(area.stages)) {
for (const stage of Object.values(area.stages))
{
// Only adjust crafts ABOVE the override // Only adjust crafts ABOVE the override
stage.constructionTime = Math.min(stage.constructionTime, overrideSeconds); stage.constructionTime = Math.min(stage.constructionTime, overrideSeconds);
} }
} }
} }
protected adjustLocationBotValues(): void protected adjustLocationBotValues(): void {
{
const mapsDb = this.databaseService.getLocations(); const mapsDb = this.databaseService.getLocations();
for (const locationKey in this.botConfig.maxBotCap) for (const locationKey in this.botConfig.maxBotCap) {
{
const map: ILocation = mapsDb[locationKey]; const map: ILocation = mapsDb[locationKey];
if (!map) if (!map) {
{
continue; continue;
} }
@ -322,15 +288,16 @@ export class GameController
/** /**
* Out of date/incorrectly made trader mods forget this data * Out of date/incorrectly made trader mods forget this data
*/ */
protected checkTraderRepairValuesExist(): void protected checkTraderRepairValuesExist(): void {
{
const traders = this.databaseService.getTraders(); const traders = this.databaseService.getTraders();
for (const trader of Object.values(traders)) for (const trader of Object.values(traders)) {
{ if (!trader?.base?.repair) {
if (!trader?.base?.repair) this.logger.warning(
{ this.localisationService.getText("trader-missing_repair_property_using_default", {
this.logger.warning(this.localisationService.getText("trader-missing_repair_property_using_default", traderId: trader.base._id,
{ traderId: trader.base._id, nickname: trader.base.nickname })); nickname: trader.base.nickname,
}),
);
// use ragfair trader as a default // use ragfair trader as a default
trader.base.repair = this.cloner.clone(traders.ragfair.base.repair); trader.base.repair = this.cloner.clone(traders.ragfair.base.repair);
@ -338,49 +305,46 @@ export class GameController
return; return;
} }
if (trader.base.repair?.quality === undefined) if (trader.base.repair?.quality === undefined) {
{ this.logger.warning(
this.logger.warning(this.localisationService.getText("trader-missing_repair_quality_property_using_default", this.localisationService.getText("trader-missing_repair_quality_property_using_default", {
{ traderId: trader.base._id, nickname: trader.base.nickname })); traderId: trader.base._id,
nickname: trader.base.nickname,
}),
);
// use ragfair trader as a default // use ragfair trader as a default
trader.base.repair.quality = this.cloner.clone( trader.base.repair.quality = this.cloner.clone(traders.ragfair.base.repair.quality);
traders.ragfair.base.repair.quality,
);
trader.base.repair.quality = traders.ragfair.base.repair.quality; trader.base.repair.quality = traders.ragfair.base.repair.quality;
} }
} }
} }
protected addCustomLooseLootPositions(): void protected addCustomLooseLootPositions(): void {
{
const looseLootPositionsToAdd = this.lootConfig.looseLoot; const looseLootPositionsToAdd = this.lootConfig.looseLoot;
for (const [mapId, positionsToAdd] of Object.entries(looseLootPositionsToAdd)) for (const [mapId, positionsToAdd] of Object.entries(looseLootPositionsToAdd)) {
{ if (!mapId) {
if (!mapId) this.logger.warning(
{ this.localisationService.getText("location-unable_to_add_custom_loot_position", mapId),
this.logger.warning(this.localisationService.getText("location-unable_to_add_custom_loot_position", mapId)); );
continue; continue;
} }
const mapLooseLoot = this.databaseService.getLocation(mapId).looseLoot; const mapLooseLoot = this.databaseService.getLocation(mapId).looseLoot;
if (!mapLooseLoot) if (!mapLooseLoot) {
{
this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId)); this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId));
continue; continue;
} }
for (const positionToAdd of positionsToAdd) for (const positionToAdd of positionsToAdd) {
{
// Exists already, add new items to existing positions pool // Exists already, add new items to existing positions pool
const existingLootPosition = mapLooseLoot.spawnpoints.find( const existingLootPosition = mapLooseLoot.spawnpoints.find(
(x) => x.template.Id === positionToAdd.template.Id, (x) => x.template.Id === positionToAdd.template.Id,
); );
if (existingLootPosition) if (existingLootPosition) {
{
existingLootPosition.template.Items.push(...positionToAdd.template.Items); existingLootPosition.template.Items.push(...positionToAdd.template.Items);
existingLootPosition.itemDistribution.push(...positionToAdd.itemDistribution); existingLootPosition.itemDistribution.push(...positionToAdd.itemDistribution);
@ -393,27 +357,27 @@ export class GameController
} }
} }
protected adjustLooseLootSpawnProbabilities(): void protected adjustLooseLootSpawnProbabilities(): void {
{
const adjustments = this.lootConfig.looseLootSpawnPointAdjustments; const adjustments = this.lootConfig.looseLootSpawnPointAdjustments;
for (const [mapId, mapAdjustments] of Object.entries(adjustments)) for (const [mapId, mapAdjustments] of Object.entries(adjustments)) {
{
const mapLooseLootData = this.databaseService.getLocation(mapId).looseLoot; const mapLooseLootData = this.databaseService.getLocation(mapId).looseLoot;
if (!mapLooseLootData) if (!mapLooseLootData) {
{
this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId)); this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId));
continue; continue;
} }
for (const [lootKey, newChanceValue] of Object.entries(mapAdjustments)) for (const [lootKey, newChanceValue] of Object.entries(mapAdjustments)) {
{ const lootPostionToAdjust = mapLooseLootData.spawnpoints.find(
const lootPostionToAdjust = mapLooseLootData.spawnpoints (spawnPoint) => spawnPoint.template.Id === lootKey,
.find((spawnPoint) => spawnPoint.template.Id === lootKey); );
if (!lootPostionToAdjust) if (!lootPostionToAdjust) {
{ this.logger.warning(
this.logger.warning(this.localisationService.getText("location-unable_to_adjust_loot_position_on_map", this.localisationService.getText("location-unable_to_adjust_loot_position_on_map", {
{ lootKey: lootKey, mapId: mapId })); lootKey: lootKey,
mapId: mapId,
}),
);
continue; continue;
} }
@ -424,36 +388,28 @@ export class GameController
} }
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */ /** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
protected adjustMapBotLimits(): void protected adjustMapBotLimits(): void {
{
const mapsDb = this.databaseService.getLocations(); const mapsDb = this.databaseService.getLocations();
if (!this.locationConfig.botTypeLimits) if (!this.locationConfig.botTypeLimits) {
{
return; return;
} }
for (const mapId in this.locationConfig.botTypeLimits) for (const mapId in this.locationConfig.botTypeLimits) {
{
const map: ILocation = mapsDb[mapId]; const map: ILocation = mapsDb[mapId];
if (!map) if (!map) {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId), this.localisationService.getText("bot-unable_to_edit_limits_of_unknown_map", mapId),
); );
} }
for (const botToLimit of this.locationConfig.botTypeLimits[mapId]) for (const botToLimit of this.locationConfig.botTypeLimits[mapId]) {
{
const index = map.base.MinMaxBots.findIndex((x) => x.WildSpawnType === botToLimit.type); const index = map.base.MinMaxBots.findIndex((x) => x.WildSpawnType === botToLimit.type);
if (index !== -1) if (index !== -1) {
{
// Existing bot type found in MinMaxBots array, edit // Existing bot type found in MinMaxBots array, edit
const limitObjectToUpdate = map.base.MinMaxBots[index]; const limitObjectToUpdate = map.base.MinMaxBots[index];
limitObjectToUpdate.min = botToLimit.min; limitObjectToUpdate.min = botToLimit.min;
limitObjectToUpdate.max = botToLimit.max; limitObjectToUpdate.max = botToLimit.max;
} } else {
else
{
// Bot type not found, add new object // Bot type not found, add new object
map.base.MinMaxBots.push({ map.base.MinMaxBots.push({
// Bot type not found, add new object // Bot type not found, add new object
@ -469,11 +425,10 @@ export class GameController
/** /**
* Handle client/game/config * Handle client/game/config
*/ */
public getGameConfig(sessionID: string): IGameConfigResponse public getGameConfig(sessionID: string): IGameConfigResponse {
{
const profile = this.profileHelper.getPmcProfile(sessionID); const profile = this.profileHelper.getPmcProfile(sessionID);
const gameTime const gameTime =
= profile.Stats?.Eft.OverallCounters.Items?.find( profile.Stats?.Eft.OverallCounters.Items?.find(
(counter) => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"), (counter) => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"),
)?.Value ?? 0; )?.Value ?? 0;
@ -504,40 +459,35 @@ export class GameController
/** /**
* Handle client/game/mode * Handle client/game/mode
*/ */
public getGameMode(sessionID: string, info: IGameModeRequestData): any public getGameMode(sessionID: string, info: IGameModeRequestData): any {
{
return { gameMode: ESessionMode.PVE, backendUrl: this.httpServerHelper.getBackendUrl() }; return { gameMode: ESessionMode.PVE, backendUrl: this.httpServerHelper.getBackendUrl() };
} }
/** /**
* Handle client/server/list * Handle client/server/list
*/ */
public getServer(sessionId: string): IServerDetails[] public getServer(sessionId: string): IServerDetails[] {
{
return [{ ip: this.httpConfig.backendIp, port: Number.parseInt(this.httpConfig.backendPort) }]; return [{ ip: this.httpConfig.backendIp, port: Number.parseInt(this.httpConfig.backendPort) }];
} }
/** /**
* Handle client/match/group/current * Handle client/match/group/current
*/ */
public getCurrentGroup(sessionId: string): ICurrentGroupResponse public getCurrentGroup(sessionId: string): ICurrentGroupResponse {
{
return { squad: [] }; return { squad: [] };
} }
/** /**
* Handle client/checkVersion * Handle client/checkVersion
*/ */
public getValidGameVersion(sessionId: string): ICheckVersionResponse public getValidGameVersion(sessionId: string): ICheckVersionResponse {
{
return { isvalid: true, latestVersion: this.coreConfig.compatibleTarkovVersion }; return { isvalid: true, latestVersion: this.coreConfig.compatibleTarkovVersion };
} }
/** /**
* Handle client/game/keepalive * Handle client/game/keepalive
*/ */
public getKeepAlive(sessionId: string): IGameKeepAliveResponse public getKeepAlive(sessionId: string): IGameKeepAliveResponse {
{
this.profileActivityService.setActivityTimestamp(sessionId); this.profileActivityService.setActivityTimestamp(sessionId);
return { msg: "OK", utc_time: new Date().getTime() / 1000 }; return { msg: "OK", utc_time: new Date().getTime() / 1000 };
} }
@ -545,8 +495,7 @@ export class GameController
/** /**
* Handle singleplayer/settings/getRaidTime * Handle singleplayer/settings/getRaidTime
*/ */
public getRaidTime(sessionId: string, request: IGetRaidTimeRequest): IGetRaidTimeResponse public getRaidTime(sessionId: string, request: IGetRaidTimeRequest): IGetRaidTimeResponse {
{
// Set interval times to in-raid value // Set interval times to in-raid value
this.ragfairConfig.runIntervalSeconds = this.ragfairConfig.runIntervalValues.inRaid; this.ragfairConfig.runIntervalSeconds = this.ragfairConfig.runIntervalValues.inRaid;
@ -558,19 +507,12 @@ export class GameController
/** /**
* BSG have two values for shotgun dispersion, we make sure both have the same value * BSG have two values for shotgun dispersion, we make sure both have the same value
*/ */
protected fixShotgunDispersions(): void protected fixShotgunDispersions(): void {
{
const itemDb = this.databaseService.getItems(); const itemDb = this.databaseService.getItems();
const shotguns = [ const shotguns = [Weapons.SHOTGUN_12G_SAIGA_12K, Weapons.SHOTGUN_20G_TOZ_106, Weapons.SHOTGUN_12G_M870];
Weapons.SHOTGUN_12G_SAIGA_12K, for (const shotgunId of shotguns) {
Weapons.SHOTGUN_20G_TOZ_106, if (itemDb[shotgunId]._props.ShotgunDispersion) {
Weapons.SHOTGUN_12G_M870,
];
for (const shotgunId of shotguns)
{
if (itemDb[shotgunId]._props.ShotgunDispersion)
{
itemDb[shotgunId]._props.shotgunDispersion = itemDb[shotgunId]._props.ShotgunDispersion; itemDb[shotgunId]._props.shotgunDispersion = itemDb[shotgunId]._props.ShotgunDispersion;
} }
} }
@ -580,22 +522,17 @@ export class GameController
* Players set botReload to a high value and don't expect the crazy fast reload speeds, give them a warn about it * Players set botReload to a high value and don't expect the crazy fast reload speeds, give them a warn about it
* @param pmcProfile Player profile * @param pmcProfile Player profile
*/ */
protected warnOnActiveBotReloadSkill(pmcProfile: IPmcData): void protected warnOnActiveBotReloadSkill(pmcProfile: IPmcData): void {
{
const botReloadSkill = this.profileHelper.getSkillFromProfile(pmcProfile, SkillTypes.BOT_RELOAD); const botReloadSkill = this.profileHelper.getSkillFromProfile(pmcProfile, SkillTypes.BOT_RELOAD);
if (botReloadSkill?.Progress > 0) if (botReloadSkill?.Progress > 0) {
{
this.logger.warning(this.localisationService.getText("server_start_player_active_botreload_skill")); this.logger.warning(this.localisationService.getText("server_start_player_active_botreload_skill"));
} }
} }
protected setAllDbItemsAsSellableOnFlea(): void protected setAllDbItemsAsSellableOnFlea(): void {
{
const dbItems = Object.values(this.databaseService.getItems()); const dbItems = Object.values(this.databaseService.getItems());
for (const item of dbItems) for (const item of dbItems) {
{ if (item._type === "Item" && !item._props?.CanSellOnRagfair) {
if (item._type === "Item" && !item._props?.CanSellOnRagfair)
{
item._props.CanSellOnRagfair = true; item._props.CanSellOnRagfair = true;
} }
} }
@ -605,15 +542,13 @@ export class GameController
* When player logs in, iterate over all active effects and reduce timer * When player logs in, iterate over all active effects and reduce timer
* @param pmcProfile Profile to adjust values for * @param pmcProfile Profile to adjust values for
*/ */
protected updateProfileHealthValues(pmcProfile: IPmcData): void protected updateProfileHealthValues(pmcProfile: IPmcData): void {
{
const healthLastUpdated = pmcProfile.Health.UpdateTime; const healthLastUpdated = pmcProfile.Health.UpdateTime;
const currentTimeStamp = this.timeUtil.getTimestamp(); const currentTimeStamp = this.timeUtil.getTimestamp();
const diffSeconds = currentTimeStamp - healthLastUpdated; const diffSeconds = currentTimeStamp - healthLastUpdated;
// Last update is in past // Last update is in past
if (healthLastUpdated < currentTimeStamp) if (healthLastUpdated < currentTimeStamp) {
{
// Base values // Base values
let energyRegenPerHour = 60; let energyRegenPerHour = 60;
let hydrationRegenPerHour = 60; let hydrationRegenPerHour = 60;
@ -632,53 +567,42 @@ export class GameController
); );
// Player has energy deficit // Player has energy deficit
if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum) if (pmcProfile.Health.Energy.Current !== pmcProfile.Health.Energy.Maximum) {
{
// Set new value, whatever is smallest // Set new value, whatever is smallest
pmcProfile.Health.Energy.Current += Math.round(energyRegenPerHour * (diffSeconds / 3600)); pmcProfile.Health.Energy.Current += Math.round(energyRegenPerHour * (diffSeconds / 3600));
if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum) if (pmcProfile.Health.Energy.Current > pmcProfile.Health.Energy.Maximum) {
{
pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum; pmcProfile.Health.Energy.Current = pmcProfile.Health.Energy.Maximum;
} }
} }
// Player has hydration deficit // Player has hydration deficit
if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum) if (pmcProfile.Health.Hydration.Current !== pmcProfile.Health.Hydration.Maximum) {
{
pmcProfile.Health.Hydration.Current += Math.round(hydrationRegenPerHour * (diffSeconds / 3600)); pmcProfile.Health.Hydration.Current += Math.round(hydrationRegenPerHour * (diffSeconds / 3600));
if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum) if (pmcProfile.Health.Hydration.Current > pmcProfile.Health.Hydration.Maximum) {
{
pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum; pmcProfile.Health.Hydration.Current = pmcProfile.Health.Hydration.Maximum;
} }
} }
// Check all body parts // Check all body parts
for (const bodyPartKey in pmcProfile.Health.BodyParts) for (const bodyPartKey in pmcProfile.Health.BodyParts) {
{
const bodyPart = pmcProfile.Health.BodyParts[bodyPartKey] as BodyPartHealth; const bodyPart = pmcProfile.Health.BodyParts[bodyPartKey] as BodyPartHealth;
// Check part hp // Check part hp
if (bodyPart.Health.Current < bodyPart.Health.Maximum) if (bodyPart.Health.Current < bodyPart.Health.Maximum) {
{
bodyPart.Health.Current += Math.round(hpRegenPerHour * (diffSeconds / 3600)); bodyPart.Health.Current += Math.round(hpRegenPerHour * (diffSeconds / 3600));
} }
if (bodyPart.Health.Current > bodyPart.Health.Maximum) if (bodyPart.Health.Current > bodyPart.Health.Maximum) {
{
bodyPart.Health.Current = bodyPart.Health.Maximum; bodyPart.Health.Current = bodyPart.Health.Maximum;
} }
// Look for effects // Look for effects
if (Object.keys(bodyPart.Effects ?? {}).length > 0) if (Object.keys(bodyPart.Effects ?? {}).length > 0) {
{
// Decrement effect time value by difference between current time and time health was last updated // Decrement effect time value by difference between current time and time health was last updated
for (const effectKey in bodyPart.Effects) for (const effectKey in bodyPart.Effects) {
{
// remove effects below 1, .e.g. bleeds at -1 // remove effects below 1, .e.g. bleeds at -1
if (bodyPart.Effects[effectKey].Time < 1) if (bodyPart.Effects[effectKey].Time < 1) {
{
// More than 30 mins has passed // More than 30 mins has passed
if (diffSeconds > 1800) if (diffSeconds > 1800) {
{
delete bodyPart.Effects[effectKey]; delete bodyPart.Effects[effectKey];
} }
@ -686,8 +610,7 @@ export class GameController
} }
bodyPart.Effects[effectKey].Time -= diffSeconds; bodyPart.Effects[effectKey].Time -= diffSeconds;
if (bodyPart.Effects[effectKey].Time < 1) if (bodyPart.Effects[effectKey].Time < 1) {
{
// effect time was sub 1, set floor it can be // effect time was sub 1, set floor it can be
bodyPart.Effects[effectKey].Time = 1; bodyPart.Effects[effectKey].Time = 1;
} }
@ -701,31 +624,25 @@ export class GameController
/** /**
* Waves with an identical min/max values spawn nothing, the number of bots that spawn is the difference between min and max * Waves with an identical min/max values spawn nothing, the number of bots that spawn is the difference between min and max
*/ */
protected fixBrokenOfflineMapWaves(): void protected fixBrokenOfflineMapWaves(): void {
{
const locations = this.databaseService.getLocations(); const locations = this.databaseService.getLocations();
for (const locationKey in locations) for (const locationKey in locations) {
{
// Skip ignored maps // Skip ignored maps
if (this.locationConfig.fixEmptyBotWavesSettings.ignoreMaps.includes(locationKey)) if (this.locationConfig.fixEmptyBotWavesSettings.ignoreMaps.includes(locationKey)) {
{
continue; continue;
} }
// Loop over all of the locations waves and look for waves with identical min and max slots // Loop over all of the locations waves and look for waves with identical min and max slots
const location: ILocation = locations[locationKey]; const location: ILocation = locations[locationKey];
if (!location.base) if (!location.base) {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey), this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey),
); );
continue; continue;
} }
for (const wave of location.base.waves ?? []) for (const wave of location.base.waves ?? []) {
{ if (wave.slots_max - wave.slots_min === 0) {
if (wave.slots_max - wave.slots_min === 0)
{
this.logger.debug( this.logger.debug(
`Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`, `Fixed ${wave.WildSpawnType} Spawn: ${locationKey} wave: ${wave.number} of type: ${wave.WildSpawnType} in zone: ${wave.SpawnPoints} with Max Slots of ${wave.slots_max}`,
); );
@ -738,21 +655,18 @@ export class GameController
/** /**
* Make Rogues spawn later to allow for scavs to spawn first instead of rogues filling up all spawn positions * Make Rogues spawn later to allow for scavs to spawn first instead of rogues filling up all spawn positions
*/ */
protected fixRoguesSpawningInstantlyOnLighthouse(): void protected fixRoguesSpawningInstantlyOnLighthouse(): void {
{
const rogueSpawnDelaySeconds = this.locationConfig.rogueLighthouseSpawnTimeSettings.waitTimeSeconds; const rogueSpawnDelaySeconds = this.locationConfig.rogueLighthouseSpawnTimeSettings.waitTimeSeconds;
const lighthouse = this.databaseService.getLocations().lighthouse?.base; const lighthouse = this.databaseService.getLocations().lighthouse?.base;
if (!lighthouse) if (!lighthouse) {
{
return; return;
} }
// Find Rogues that spawn instantly // Find Rogues that spawn instantly
const instantRogueBossSpawns = lighthouse.BossLocationSpawn const instantRogueBossSpawns = lighthouse.BossLocationSpawn.filter(
.filter((spawn) => spawn.BossName === "exUsec" (spawn) => spawn.BossName === "exUsec" && spawn.Time === -1,
&& spawn.Time === -1); );
for (const wave of instantRogueBossSpawns) for (const wave of instantRogueBossSpawns) {
{
wave.Time = rogueSpawnDelaySeconds; wave.Time = rogueSpawnDelaySeconds;
} }
} }
@ -761,21 +675,18 @@ export class GameController
* Send starting gifts to profile after x days * Send starting gifts to profile after x days
* @param pmcProfile Profile to add gifts to * @param pmcProfile Profile to add gifts to
*/ */
protected sendPraporGiftsToNewProfiles(pmcProfile: IPmcData): void protected sendPraporGiftsToNewProfiles(pmcProfile: IPmcData): void {
{
const timeStampProfileCreated = pmcProfile.Info.RegistrationDate; const timeStampProfileCreated = pmcProfile.Info.RegistrationDate;
const oneDaySeconds = this.timeUtil.getHoursAsSeconds(24); const oneDaySeconds = this.timeUtil.getHoursAsSeconds(24);
const currentTimeStamp = this.timeUtil.getTimestamp(); const currentTimeStamp = this.timeUtil.getTimestamp();
// One day post-profile creation // One day post-profile creation
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds) if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds) {
{
this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 1); this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 1);
} }
// Two day post-profile creation // Two day post-profile creation
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2) if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2) {
{
this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 2); this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 2);
} }
} }
@ -784,26 +695,21 @@ export class GameController
* Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these * Find and split waves with large numbers of bots into smaller waves - BSG appears to reduce the size of these
* waves to one bot when they're waiting to spawn for too long * waves to one bot when they're waiting to spawn for too long
*/ */
protected splitBotWavesIntoSingleWaves(): void protected splitBotWavesIntoSingleWaves(): void {
{
const locations = this.databaseService.getLocations(); const locations = this.databaseService.getLocations();
for (const locationKey in locations) for (const locationKey in locations) {
{ if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.ignoreMaps.includes(locationKey)) {
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.ignoreMaps.includes(locationKey))
{
continue; continue;
} }
// Iterate over all maps // Iterate over all maps
const location: ILocation = locations[locationKey]; const location: ILocation = locations[locationKey];
for (const wave of location.base.waves) for (const wave of location.base.waves) {
{
// Wave has size that makes it candidate for splitting // Wave has size that makes it candidate for splitting
if ( if (
wave.slots_max - wave.slots_min wave.slots_max - wave.slots_min >=
>= this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold
) ) {
{
// Get count of bots to be spawned in wave // Get count of bots to be spawned in wave
const waveSize = wave.slots_max - wave.slots_min; const waveSize = wave.slots_max - wave.slots_min;
@ -819,14 +725,12 @@ export class GameController
// Add new waves to fill gap from bots we removed in above wave // Add new waves to fill gap from bots we removed in above wave
let wavesAddedCount = 0; let wavesAddedCount = 0;
for (let index = indexOfWaveToSplit + 1; index < indexOfWaveToSplit + waveSize; index++) for (let index = indexOfWaveToSplit + 1; index < indexOfWaveToSplit + waveSize; index++) {
{
// Clone wave ready to insert into array // Clone wave ready to insert into array
const waveToAddClone = this.cloner.clone(wave); const waveToAddClone = this.cloner.clone(wave);
// Some waves have value of 0 for some reason, preserve // Some waves have value of 0 for some reason, preserve
if (waveToAddClone.number !== 0) if (waveToAddClone.number !== 0) {
{
// Update wave number to new location in array // Update wave number to new location in array
waveToAddClone.number = index; waveToAddClone.number = index;
} }
@ -841,11 +745,9 @@ export class GameController
let index = indexOfWaveToSplit + wavesAddedCount + 1; let index = indexOfWaveToSplit + wavesAddedCount + 1;
index < location.base.waves.length; index < location.base.waves.length;
index++ index++
) ) {
{
// Some waves have value of 0, leave them as-is // Some waves have value of 0, leave them as-is
if (location.base.waves[index].number !== 0) if (location.base.waves[index].number !== 0) {
{
location.base.waves[index].number += wavesAddedCount; location.base.waves[index].number += wavesAddedCount;
} }
} }
@ -858,28 +760,24 @@ export class GameController
* Get a list of installed mods and save their details to the profile being used * Get a list of installed mods and save their details to the profile being used
* @param fullProfile Profile to add mod details to * @param fullProfile Profile to add mod details to
*/ */
protected saveActiveModsToProfile(fullProfile: ISptProfile): void protected saveActiveModsToProfile(fullProfile: ISptProfile): void {
{
// Add empty mod array if undefined // Add empty mod array if undefined
if (!fullProfile.spt.mods) if (!fullProfile.spt.mods) {
{
fullProfile.spt.mods = []; fullProfile.spt.mods = [];
} }
// Get active mods // Get active mods
const activeMods = this.preSptModLoader.getImportedModDetails(); const activeMods = this.preSptModLoader.getImportedModDetails();
for (const modKey in activeMods) for (const modKey in activeMods) {
{
const modDetails = activeMods[modKey]; const modDetails = activeMods[modKey];
if ( if (
fullProfile.spt.mods.some( fullProfile.spt.mods.some(
(mod) => (mod) =>
mod.author === modDetails.author mod.author === modDetails.author &&
&& mod.name === modDetails.name mod.name === modDetails.name &&
&& mod.version === modDetails.version, mod.version === modDetails.version,
) )
) ) {
{
// Exists already, skip // Exists already, skip
continue; continue;
} }
@ -897,17 +795,14 @@ export class GameController
/** /**
* Check for any missing assorts inside each traders assort.json data, checking against traders questassort.json * Check for any missing assorts inside each traders assort.json data, checking against traders questassort.json
*/ */
protected validateQuestAssortUnlocksExist(): void protected validateQuestAssortUnlocksExist(): void {
{
const db = this.databaseService.getTables(); const db = this.databaseService.getTables();
const traders = db.traders!; const traders = db.traders!;
const quests = db.templates!.quests; const quests = db.templates!.quests;
for (const traderId of Object.values(Traders)) for (const traderId of Object.values(Traders)) {
{
const traderData = traders[traderId]; const traderData = traders[traderId];
const traderAssorts = traderData?.assort; const traderAssorts = traderData?.assort;
if (!traderAssorts) if (!traderAssorts) {
{
continue; continue;
} }
@ -919,11 +814,9 @@ export class GameController
}; };
// Loop over all assorts for trader // Loop over all assorts for trader
for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts)) for (const [assortKey, questKey] of Object.entries(mergedQuestAssorts)) {
{
// Does assort key exist in trader assort file // Does assort key exist in trader assort file
if (!traderAssorts.loyal_level_items[assortKey]) if (!traderAssorts.loyal_level_items[assortKey]) {
{
// Reverse lookup of enum key by value // Reverse lookup of enum key by value
const messageValues = { const messageValues = {
traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)], traderName: Object.keys(Traders)[Object.values(Traders).indexOf(traderId)],
@ -941,20 +834,16 @@ export class GameController
* Add the logged in players name to PMC name pool * Add the logged in players name to PMC name pool
* @param pmcProfile Profile of player to get name from * @param pmcProfile Profile of player to get name from
*/ */
protected addPlayerToPMCNames(pmcProfile: IPmcData): void protected addPlayerToPMCNames(pmcProfile: IPmcData): void {
{
const playerName = pmcProfile.Info.Nickname; const playerName = pmcProfile.Info.Nickname;
if (playerName) if (playerName) {
{
const bots = this.databaseService.getBots().types; const bots = this.databaseService.getBots().types;
if (bots.bear) if (bots.bear) {
{
bots.bear.firstName.push(playerName); bots.bear.firstName.push(playerName);
} }
if (bots.usec) if (bots.usec) {
{
bots.usec.firstName.push(playerName); bots.usec.firstName.push(playerName);
} }
} }
@ -964,11 +853,9 @@ export class GameController
* Check for a dialog with the key 'undefined', and remove it * Check for a dialog with the key 'undefined', and remove it
* @param fullProfile Profile to check for dialog in * @param fullProfile Profile to check for dialog in
*/ */
protected checkForAndRemoveUndefinedDialogs(fullProfile: ISptProfile): void protected checkForAndRemoveUndefinedDialogs(fullProfile: ISptProfile): void {
{
const undefinedDialog = fullProfile.dialogues.undefined; const undefinedDialog = fullProfile.dialogues.undefined;
if (undefinedDialog) if (undefinedDialog) {
{
delete fullProfile.dialogues.undefined; delete fullProfile.dialogues.undefined;
} }
} }
@ -976,12 +863,10 @@ export class GameController
/** /**
* Blank out the "test" mail message from prapor * Blank out the "test" mail message from prapor
*/ */
protected removePraporTestMessage(): void protected removePraporTestMessage(): void {
{
// Iterate over all languages (e.g. "en", "fr") // Iterate over all languages (e.g. "en", "fr")
const locales = this.databaseService.getLocales(); const locales = this.databaseService.getLocales();
for (const localeKey in locales.global) for (const localeKey in locales.global) {
{
locales.global[localeKey]["61687e2c3e526901fa76baf9"] = ""; locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
} }
} }
@ -989,24 +874,21 @@ export class GameController
/** /**
* Make non-trigger-spawned raiders spawn earlier + always * Make non-trigger-spawned raiders spawn earlier + always
*/ */
protected adjustLabsRaiderSpawnRate(): void protected adjustLabsRaiderSpawnRate(): void {
{
const labsBase = this.databaseService.getLocations().laboratory!.base; const labsBase = this.databaseService.getLocations().laboratory!.base;
// Find spawns with empty string for triggerId/TriggerName // Find spawns with empty string for triggerId/TriggerName
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(
.filter((bossSpawn) => !bossSpawn.TriggerId (bossSpawn) => !bossSpawn.TriggerId && !bossSpawn.TriggerName,
&& !bossSpawn.TriggerName); );
for (const boss of nonTriggerLabsBossSpawns) for (const boss of nonTriggerLabsBossSpawns) {
{
boss.BossChance = 100; boss.BossChance = 100;
boss.Time /= 10; boss.Time /= 10;
} }
} }
protected logProfileDetails(fullProfile: ISptProfile): void protected logProfileDetails(fullProfile: ISptProfile): void {
{
this.logger.debug(`Profile made with: ${fullProfile.spt.version}`); this.logger.debug(`Profile made with: ${fullProfile.spt.version}`);
this.logger.debug( this.logger.debug(
`Server version: ${globalThis.G_SPTVERSION || this.coreConfig.sptVersion} ${globalThis.G_COMMIT}`, `Server version: ${globalThis.G_SPTVERSION || this.coreConfig.sptVersion} ${globalThis.G_COMMIT}`,

View File

@ -1,18 +1,15 @@
import { inject, injectable } from "tsyringe";
import { HandbookHelper } from "@spt/helpers/HandbookHelper"; import { HandbookHelper } from "@spt/helpers/HandbookHelper";
import { DatabaseServer } from "@spt/servers/DatabaseServer"; import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HandbookController export class HandbookController {
{
constructor( constructor(
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("HandbookHelper") protected handbookHelper: HandbookHelper, @inject("HandbookHelper") protected handbookHelper: HandbookHelper,
) ) {}
{}
public load(): void public load(): void {
{
return; return;
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { HealthHelper } from "@spt/helpers/HealthHelper"; import { HealthHelper } from "@spt/helpers/HealthHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper"; import { InventoryHelper } from "@spt/helpers/InventoryHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
@ -15,12 +14,12 @@ import { ILogger } from "@spt/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { PaymentService } from "@spt/services/PaymentService"; import { PaymentService } from "@spt/services/PaymentService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HealthController export class HealthController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@ -31,8 +30,7 @@ export class HealthController
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("HealthHelper") protected healthHelper: HealthHelper, @inject("HealthHelper") protected healthHelper: HealthHelper,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {}
{}
/** /**
* stores in-raid player health * stores in-raid player health
@ -48,8 +46,7 @@ export class HealthController
sessionID: string, sessionID: string,
addEffects = true, addEffects = true,
deleteExistingEffects = true, deleteExistingEffects = true,
): void ): void {
{
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects); this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
} }
@ -64,14 +61,12 @@ export class HealthController
pmcData: IPmcData, pmcData: IPmcData,
request: IOffraidHealRequestData, request: IOffraidHealRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// Update medkit used (hpresource) // Update medkit used (hpresource)
const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item); const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item);
if (!healingItemToUse) if (!healingItemToUse) {
{
const errorMessage = this.localisationService.getText( const errorMessage = this.localisationService.getText(
"health-healing_item_not_found", "health-healing_item_not_found",
healingItemToUse._id, healingItemToUse._id,
@ -84,20 +79,16 @@ export class HealthController
// Ensure item has a upd object // Ensure item has a upd object
this.itemHelper.addUpdObjectToItem(healingItemToUse); this.itemHelper.addUpdObjectToItem(healingItemToUse);
if (healingItemToUse.upd.MedKit) if (healingItemToUse.upd.MedKit) {
{
healingItemToUse.upd.MedKit.HpResource -= request.count; healingItemToUse.upd.MedKit.HpResource -= request.count;
} } else {
else
{
// Get max healing from db // Get max healing from db
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource; const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
} }
// Resource in medkit is spent, delete it // Resource in medkit is spent, delete it
if (healingItemToUse.upd.MedKit.HpResource <= 0) if (healingItemToUse.upd.MedKit.HpResource <= 0) {
{
this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output); this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output);
} }
@ -112,14 +103,12 @@ export class HealthController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public offraidEat(pmcData: IPmcData, request: IOffraidEatRequestData, sessionID: string): IItemEventRouterResponse public offraidEat(pmcData: IPmcData, request: IOffraidEatRequestData, sessionID: string): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
let resourceLeft = 0; let resourceLeft = 0;
const itemToConsume = pmcData.Inventory.items.find((item) => item._id === request.item); const itemToConsume = pmcData.Inventory.items.find((item) => item._id === request.item);
if (!itemToConsume) if (!itemToConsume) {
{
// Item not found, very bad // Item not found, very bad
return this.httpResponse.appendErrorToOutput( return this.httpResponse.appendErrorToOutput(
output, output,
@ -128,17 +117,13 @@ export class HealthController
} }
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource; const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
if (consumedItemMaxResource > 1) if (consumedItemMaxResource > 1) {
{
// Ensure item has a upd object // Ensure item has a upd object
this.itemHelper.addUpdObjectToItem(itemToConsume); this.itemHelper.addUpdObjectToItem(itemToConsume);
if (itemToConsume.upd.FoodDrink === undefined) if (itemToConsume.upd.FoodDrink === undefined) {
{
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count }; itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count };
} } else {
else
{
itemToConsume.upd.FoodDrink.HpPercent -= request.count; itemToConsume.upd.FoodDrink.HpPercent -= request.count;
} }
@ -146,8 +131,7 @@ export class HealthController
} }
// Remove item from inventory if resource has dropped below threshold // Remove item from inventory if resource has dropped below threshold
if (consumedItemMaxResource === 1 || resourceLeft < 1) if (consumedItemMaxResource === 1 || resourceLeft < 1) {
{
this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output); this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output);
} }
@ -166,8 +150,7 @@ export class HealthController
pmcData: IPmcData, pmcData: IPmcData,
healthTreatmentRequest: IHealthTreatmentRequestData, healthTreatmentRequest: IHealthTreatmentRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const payMoneyRequest: IProcessBuyTradeRequestData = { const payMoneyRequest: IProcessBuyTradeRequestData = {
Action: healthTreatmentRequest.Action, Action: healthTreatmentRequest.Action,
@ -180,36 +163,30 @@ export class HealthController
}; };
this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output); this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return output; return output;
} }
for (const bodyPartKey in healthTreatmentRequest.difference.BodyParts) for (const bodyPartKey in healthTreatmentRequest.difference.BodyParts) {
{
// Get body part from request + from pmc profile // Get body part from request + from pmc profile
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey]; const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
const profilePart = pmcData.Health.BodyParts[bodyPartKey]; const profilePart = pmcData.Health.BodyParts[bodyPartKey];
// Bodypart healing is chosen when part request hp is above 0 // Bodypart healing is chosen when part request hp is above 0
if (partRequest.Health > 0) if (partRequest.Health > 0) {
{
// Heal bodypart // Heal bodypart
profilePart.Health.Current = profilePart.Health.Maximum; profilePart.Health.Current = profilePart.Health.Maximum;
} }
// Check for effects to remove // Check for effects to remove
if (partRequest.Effects?.length > 0) if (partRequest.Effects?.length > 0) {
{
// Found some, loop over them and remove from pmc profile // Found some, loop over them and remove from pmc profile
for (const effect of partRequest.Effects) for (const effect of partRequest.Effects) {
{
delete pmcData.Health.BodyParts[bodyPartKey].Effects[effect]; delete pmcData.Health.BodyParts[bodyPartKey].Effects[effect];
} }
// Remove empty effect object // Remove empty effect object
if (Object.keys(pmcData.Health.BodyParts[bodyPartKey].Effects).length === 0) if (Object.keys(pmcData.Health.BodyParts[bodyPartKey].Effects).length === 0) {
{
delete pmcData.Health.BodyParts[bodyPartKey].Effects; delete pmcData.Health.BodyParts[bodyPartKey].Effects;
} }
} }
@ -227,8 +204,7 @@ export class HealthController
* @param info Request data * @param info Request data
* @param sessionID * @param sessionID
*/ */
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void {
{
// https://dev.sp-tarkov.com/SPT/Server/issues/2674 // https://dev.sp-tarkov.com/SPT/Server/issues/2674
// TODO: // TODO:
// Health effects (fractures etc) are handled in /player/health/sync. // Health effects (fractures etc) are handled in /player/health/sync.

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator"; import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator";
import { HideoutHelper } from "@spt/helpers/HideoutHelper"; import { HideoutHelper } from "@spt/helpers/HideoutHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper"; import { InventoryHelper } from "@spt/helpers/InventoryHelper";
@ -7,12 +6,7 @@ import { PaymentHelper } from "@spt/helpers/PaymentHelper";
import { PresetHelper } from "@spt/helpers/PresetHelper"; import { PresetHelper } from "@spt/helpers/PresetHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { import { HideoutArea, ITaskConditionCounter, Product, ScavCase } from "@spt/models/eft/common/tables/IBotBase";
HideoutArea,
ITaskConditionCounter,
Product,
ScavCase,
} from "@spt/models/eft/common/tables/IBotBase";
import { Item } from "@spt/models/eft/common/tables/IItem"; import { Item } from "@spt/models/eft/common/tables/IItem";
import { HideoutUpgradeCompleteRequestData } from "@spt/models/eft/hideout/HideoutUpgradeCompleteRequestData"; import { HideoutUpgradeCompleteRequestData } from "@spt/models/eft/hideout/HideoutUpgradeCompleteRequestData";
import { IHandleQTEEventRequestData } from "@spt/models/eft/hideout/IHandleQTEEventRequestData"; import { IHandleQTEEventRequestData } from "@spt/models/eft/hideout/IHandleQTEEventRequestData";
@ -47,15 +41,15 @@ import { FenceService } from "@spt/services/FenceService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { PlayerService } from "@spt/services/PlayerService"; import { PlayerService } from "@spt/services/PlayerService";
import { ProfileActivityService } from "@spt/services/ProfileActivityService"; import { ProfileActivityService } from "@spt/services/ProfileActivityService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class HideoutController export class HideoutController {
{
/** Key used in TaskConditionCounters array */ /** Key used in TaskConditionCounters array */
protected static nameTaskConditionCountersCrafting = "CounterHoursCrafting"; protected static nameTaskConditionCountersCrafting = "CounterHoursCrafting";
protected hideoutConfig: IHideoutConfig; protected hideoutConfig: IHideoutConfig;
@ -82,8 +76,7 @@ export class HideoutController
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
} }
@ -100,19 +93,15 @@ export class HideoutController
request: IHideoutUpgradeRequestData, request: IHideoutUpgradeRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{ const items = request.items.map((reqItem) => {
const items = request.items.map((reqItem) =>
{
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return { inventoryItem: item, requestedItem: reqItem }; return { inventoryItem: item, requestedItem: reqItem };
}); });
// If it's not money, its construction / barter items // If it's not money, its construction / barter items
for (const item of items) for (const item of items) {
{ if (!item.inventoryItem) {
if (!item.inventoryItem)
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id), this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
); );
@ -122,24 +111,20 @@ export class HideoutController
} }
if ( if (
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
&& item.inventoryItem.upd item.inventoryItem.upd &&
&& item.inventoryItem.upd.StackObjectsCount item.inventoryItem.upd.StackObjectsCount &&
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
) ) {
{
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count; item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
} } else {
else
{
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output); this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionID, output);
} }
} }
// Construction time management // Construction time management
const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea) {
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
this.httpResponse.appendErrorToOutput(output); this.httpResponse.appendErrorToOutput(output);
@ -149,8 +134,7 @@ export class HideoutController
const hideoutDataDb = this.databaseService const hideoutDataDb = this.databaseService
.getTables() .getTables()
.hideout.areas.find((area) => area.type === request.areaType); .hideout.areas.find((area) => area.type === request.areaType);
if (!hideoutDataDb) if (!hideoutDataDb) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType), this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
); );
@ -160,10 +144,8 @@ export class HideoutController
} }
let ctime = hideoutDataDb.stages[profileHideoutArea.level + 1].constructionTime; let ctime = hideoutDataDb.stages[profileHideoutArea.level + 1].constructionTime;
if (ctime > 0) if (ctime > 0) {
{ if (this.profileHelper.isDeveloperAccount(sessionID)) {
if (this.profileHelper.isDeveloperAccount(sessionID))
{
ctime = 40; ctime = 40;
} }
const timestamp = this.timeUtil.getTimestamp(); const timestamp = this.timeUtil.getTimestamp();
@ -186,14 +168,12 @@ export class HideoutController
request: HideoutUpgradeCompleteRequestData, request: HideoutUpgradeCompleteRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const hideout = this.databaseService.getHideout(); const hideout = this.databaseService.getHideout();
const globals = this.databaseService.getGlobals(); const globals = this.databaseService.getGlobals();
const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea) {
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
this.httpResponse.appendErrorToOutput(output); this.httpResponse.appendErrorToOutput(output);
@ -206,8 +186,7 @@ export class HideoutController
profileHideoutArea.constructing = false; profileHideoutArea.constructing = false;
const hideoutData = hideout.areas.find((area) => area.type === profileHideoutArea.type); const hideoutData = hideout.areas.find((area) => area.type === profileHideoutArea.type);
if (!hideoutData) if (!hideoutData) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType), this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
); );
@ -219,17 +198,14 @@ export class HideoutController
// Apply bonuses // Apply bonuses
const hideoutStage = hideoutData.stages[profileHideoutArea.level]; const hideoutStage = hideoutData.stages[profileHideoutArea.level];
const bonuses = hideoutStage.bonuses; const bonuses = hideoutStage.bonuses;
if (bonuses?.length > 0) if (bonuses?.length > 0) {
{ for (const bonus of bonuses) {
for (const bonus of bonuses)
{
this.hideoutHelper.applyPlayerUpgradesBonuses(pmcData, bonus); this.hideoutHelper.applyPlayerUpgradesBonuses(pmcData, bonus);
} }
} }
// Upgrade includes a container improvement/addition // Upgrade includes a container improvement/addition
if (hideoutStage?.container) if (hideoutStage?.container) {
{
this.addContainerImprovementToProfile( this.addContainerImprovementToProfile(
output, output,
sessionID, sessionID,
@ -242,10 +218,9 @@ export class HideoutController
// Upgrading water collector / med station // Upgrading water collector / med station
if ( if (
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR ||
|| profileHideoutArea.type === HideoutAreas.MEDSTATION profileHideoutArea.type === HideoutAreas.MEDSTATION
) ) {
{
this.checkAndUpgradeWall(pmcData); this.checkAndUpgradeWall(pmcData);
} }
@ -261,15 +236,12 @@ export class HideoutController
* Upgrade wall status to visible in profile if medstation/water collector are both level 1 * Upgrade wall status to visible in profile if medstation/water collector are both level 1
* @param pmcData Player profile * @param pmcData Player profile
*/ */
protected checkAndUpgradeWall(pmcData: IPmcData): void protected checkAndUpgradeWall(pmcData: IPmcData): void {
{
const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION); const medStation = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.MEDSTATION);
const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR); const waterCollector = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.WATER_COLLECTOR);
if (medStation?.level >= 1 && waterCollector?.level >= 1) if (medStation?.level >= 1 && waterCollector?.level >= 1) {
{
const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL); const wall = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.EMERGENCY_WALL);
if (wall?.level === 0) if (wall?.level === 0) {
{
wall.level = 3; wall.level = 3;
} }
} }
@ -290,11 +262,9 @@ export class HideoutController
profileParentHideoutArea: HideoutArea, profileParentHideoutArea: HideoutArea,
dbHideoutArea: IHideoutArea, dbHideoutArea: IHideoutArea,
hideoutStage: Stage, hideoutStage: Stage,
): void ): void {
{
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id // Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type]) if (!pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type]) {
{
pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type] = dbHideoutArea._id; pmcData.Inventory.hideoutAreaStashes[dbHideoutArea.type] = dbHideoutArea._id;
} }
@ -302,29 +272,24 @@ export class HideoutController
this.addUpdateInventoryItemToProfile(pmcData, dbHideoutArea, hideoutStage); this.addUpdateInventoryItemToProfile(pmcData, dbHideoutArea, hideoutStage);
// Dont inform client when upgraded area is hall of fame, BSG doesnt inform client upgrade has occurred, will break client if data is sent // Dont inform client when upgraded area is hall of fame, BSG doesnt inform client upgrade has occurred, will break client if data is sent
if (dbHideoutArea.type !== HideoutAreas.PLACE_OF_FAME) if (dbHideoutArea.type !== HideoutAreas.PLACE_OF_FAME) {
{
// Inform client of changes // Inform client of changes
this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage); this.addContainerUpgradeToClientOutput(output, sessionID, dbHideoutArea.type, dbHideoutArea, hideoutStage);
} }
// Some areas like gun stand have a child area linked to it, it needs to do the same as above // Some areas like gun stand have a child area linked to it, it needs to do the same as above
const childDbArea = this.databaseService.getHideout().areas const childDbArea = this.databaseService
.find((area) => area.parentArea === dbHideoutArea._id); .getHideout()
if (childDbArea) .areas.find((area) => area.parentArea === dbHideoutArea._id);
{ if (childDbArea) {
// Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id // Add key/value to `hideoutAreaStashes` dictionary - used to link hideout area to inventory stash by its id
if (!pmcData.Inventory.hideoutAreaStashes[childDbArea.type]) if (!pmcData.Inventory.hideoutAreaStashes[childDbArea.type]) {
{
pmcData.Inventory.hideoutAreaStashes[childDbArea.type] = childDbArea._id; pmcData.Inventory.hideoutAreaStashes[childDbArea.type] = childDbArea._id;
} }
// Set child area level to same as parent area // Set child area level to same as parent area
pmcData.Hideout.Areas pmcData.Hideout.Areas.find((hideoutArea) => hideoutArea.type === childDbArea.type).level =
.find((hideoutArea) => hideoutArea.type === childDbArea.type).level pmcData.Hideout.Areas.find((x) => x.type === profileParentHideoutArea.type).level;
= pmcData.Hideout.Areas
.find((x) => x.type === profileParentHideoutArea.type,
).level;
// Add/upgrade stash item in player inventory // Add/upgrade stash item in player inventory
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level]; const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
@ -345,11 +310,9 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
dbHideoutData: IHideoutArea, dbHideoutData: IHideoutArea,
hideoutStage: Stage, hideoutStage: Stage,
): void ): void {
{
const existingInventoryItem = pmcData.Inventory.items.find((item) => item._id === dbHideoutData._id); const existingInventoryItem = pmcData.Inventory.items.find((item) => item._id === dbHideoutData._id);
if (existingInventoryItem) if (existingInventoryItem) {
{
// Update existing items container tpl to point to new id (tpl) // Update existing items container tpl to point to new id (tpl)
existingInventoryItem._tpl = hideoutStage.container; existingInventoryItem._tpl = hideoutStage.container;
@ -373,10 +336,8 @@ export class HideoutController
areaType: HideoutAreas, areaType: HideoutAreas,
hideoutDbData: IHideoutArea, hideoutDbData: IHideoutArea,
hideoutStage: Stage, hideoutStage: Stage,
): void ): void {
{ if (!output.profileChanges[sessionID].changedHideoutStashes) {
if (!output.profileChanges[sessionID].changedHideoutStashes)
{
output.profileChanges[sessionID].changedHideoutStashes = {}; output.profileChanges[sessionID].changedHideoutStashes = {};
} }
@ -398,19 +359,16 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
addItemToHideoutRequest: IHideoutPutItemInRequestData, addItemToHideoutRequest: IHideoutPutItemInRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) => const itemsToAdd = Object.entries(addItemToHideoutRequest.items).map((kvp) => {
{
const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1].id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === kvp[1].id);
return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] }; return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] };
}); });
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType);
if (!hideoutArea) if (!hideoutArea) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"hideout-unable_to_find_area_in_database", "hideout-unable_to_find_area_in_database",
@ -420,10 +378,8 @@ export class HideoutController
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
for (const item of itemsToAdd) for (const item of itemsToAdd) {
{ if (!item.inventoryItem) {
if (!item.inventoryItem)
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", { this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {
itemId: item.requestedItem.id, itemId: item.requestedItem.id,
@ -465,19 +421,16 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutTakeItemOutRequestData, request: IHideoutTakeItemOutRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea) if (!hideoutArea) {
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
if (!hideoutArea.slots || hideoutArea.slots.length === 0) if (!hideoutArea.slots || hideoutArea.slots.length === 0) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type), this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type),
); );
@ -492,8 +445,7 @@ export class HideoutController
HideoutAreas.GENERATOR, HideoutAreas.GENERATOR,
HideoutAreas.BITCOIN_FARM, HideoutAreas.BITCOIN_FARM,
].includes(hideoutArea.type) ].includes(hideoutArea.type)
) ) {
{
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea); const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
// Force a refresh of productions/hideout areas with resources // Force a refresh of productions/hideout areas with resources
@ -521,13 +473,11 @@ export class HideoutController
removeResourceRequest: IHideoutTakeItemOutRequestData, removeResourceRequest: IHideoutTakeItemOutRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
hideoutArea: HideoutArea, hideoutArea: HideoutArea,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const slotIndexToRemove = removeResourceRequest.slots[0]; const slotIndexToRemove = removeResourceRequest.slots[0];
// Assume only one item in slot // Assume only one item in slot
const itemToReturn = hideoutArea.slots const itemToReturn = hideoutArea.slots.find((slot) => slot.locationIndex === slotIndexToRemove).item[0];
.find((slot) => slot.locationIndex === slotIndexToRemove).item[0];
const request: IAddItemDirectRequest = { const request: IAddItemDirectRequest = {
itemWithModsToAdd: [itemToReturn], itemWithModsToAdd: [itemToReturn],
@ -537,8 +487,7 @@ export class HideoutController
}; };
this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output); this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output);
if (output.warnings && output.warnings.length > 0) if (output.warnings && output.warnings.length > 0) {
{
// Adding to stash failed, drop out - dont remove item from hideout area slot // Adding to stash failed, drop out - dont remove item from hideout area slot
return output; return output;
} }
@ -562,16 +511,14 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutToggleAreaRequestData, request: IHideoutToggleAreaRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress) // Force a production update (occur before area is toggled as it could be generator and doing it after generator enabled would cause incorrect calculaton of production progress)
this.hideoutHelper.updatePlayerHideout(sessionID); this.hideoutHelper.updatePlayerHideout(sessionID);
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType); const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === request.areaType);
if (!hideoutArea) if (!hideoutArea) {
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
@ -593,14 +540,14 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutSingleProductionStartRequestData, body: IHideoutSingleProductionStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// Start production // Start production
this.hideoutHelper.registerProduction(pmcData, body, sessionID); this.hideoutHelper.registerProduction(pmcData, body, sessionID);
// Find the recipe of the production // Find the recipe of the production
const recipe = this.databaseService.getHideout().production const recipe = this.databaseService
.find((production) => production._id === body.recipeId); .getHideout()
.production.find((production) => production._id === body.recipeId);
// Find the actual amount of items we need to remove because body can send weird data // Find the actual amount of items we need to remove because body can send weird data
const recipeRequirementsClone = this.cloner.clone( const recipeRequirementsClone = this.cloner.clone(
@ -609,8 +556,7 @@ export class HideoutController
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const itemsToDelete = body.items.concat(body.tools); const itemsToDelete = body.items.concat(body.tools);
for (const itemToDelete of itemsToDelete) for (const itemToDelete of itemsToDelete) {
{
const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id); const itemToCheck = pmcData.Inventory.items.find((i) => i._id === itemToDelete.id);
const requirement = recipeRequirementsClone.find( const requirement = recipeRequirementsClone.find(
(requirement) => requirement.templateId === itemToCheck._tpl, (requirement) => requirement.templateId === itemToCheck._tpl,
@ -618,16 +564,14 @@ export class HideoutController
// Handle tools not having a `count`, but always only requiring 1 // Handle tools not having a `count`, but always only requiring 1
const requiredCount = requirement.count ?? 1; const requiredCount = requirement.count ?? 1;
if (requiredCount <= 0) if (requiredCount <= 0) {
{
continue; continue;
} }
this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requiredCount, sessionID, output); this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requiredCount, sessionID, output);
// Tools don't have a count // Tools don't have a count
if (requirement.type !== "Tool") if (requirement.type !== "Tool") {
{
requirement.count -= itemToDelete.count; requirement.count -= itemToDelete.count;
} }
} }
@ -647,15 +591,12 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
body: IHideoutScavCaseStartRequestData, body: IHideoutScavCaseStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
for (const requestedItem of body.items) for (const requestedItem of body.items) {
{
const inventoryItem = pmcData.Inventory.items.find((item) => item._id === requestedItem.id); const inventoryItem = pmcData.Inventory.items.find((item) => item._id === requestedItem.id);
if (!inventoryItem) if (!inventoryItem) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory", "hideout-unable_to_find_scavcase_requested_item_in_profile_inventory",
@ -665,20 +606,15 @@ export class HideoutController
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
if (inventoryItem.upd?.StackObjectsCount && inventoryItem.upd.StackObjectsCount > requestedItem.count) if (inventoryItem.upd?.StackObjectsCount && inventoryItem.upd.StackObjectsCount > requestedItem.count) {
{
inventoryItem.upd.StackObjectsCount -= requestedItem.count; inventoryItem.upd.StackObjectsCount -= requestedItem.count;
} } else {
else
{
this.inventoryHelper.removeItem(pmcData, requestedItem.id, sessionID, output); this.inventoryHelper.removeItem(pmcData, requestedItem.id, sessionID, output);
} }
} }
const recipe = this.databaseService.getHideout().scavcase const recipe = this.databaseService.getHideout().scavcase.find((r) => r._id === body.recipeId);
.find((r) => r._id === body.recipeId); if (!recipe) {
if (!recipe)
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId), this.localisationService.getText("hideout-unable_to_find_scav_case_recipie_in_database", body.recipeId),
); );
@ -689,9 +625,9 @@ export class HideoutController
// @Important: Here we need to be very exact: // @Important: Here we need to be very exact:
// - normal recipe: Production time value is stored in attribute "productionTime" with small "p" // - normal recipe: Production time value is stored in attribute "productionTime" with small "p"
// - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P" // - scav case recipe: Production time value is stored in attribute "ProductionTime" with capital "P"
const adjustedCraftTime const adjustedCraftTime =
= recipe.ProductionTime recipe.ProductionTime -
- this.hideoutHelper.getSkillProductionTimeReduction( this.hideoutHelper.getSkillProductionTimeReduction(
pmcData, pmcData,
recipe.ProductionTime, recipe.ProductionTime,
SkillTypes.CRAFTING, SkillTypes.CRAFTING,
@ -717,11 +653,9 @@ export class HideoutController
* @param productionTime Time to complete scav case in seconds * @param productionTime Time to complete scav case in seconds
* @returns Adjusted scav case time in seconds * @returns Adjusted scav case time in seconds
*/ */
protected getScavCaseTime(pmcData: IPmcData, productionTime: number): number protected getScavCaseTime(pmcData: IPmcData, productionTime: number): number {
{
const fenceLevel = this.fenceService.getFenceInfo(pmcData); const fenceLevel = this.fenceService.getFenceInfo(pmcData);
if (!fenceLevel) if (!fenceLevel) {
{
return productionTime; return productionTime;
} }
@ -734,8 +668,7 @@ export class HideoutController
* @param rewards reward items to add to profile * @param rewards reward items to add to profile
* @param recipeId recipe id to save into Production dict * @param recipeId recipe id to save into Production dict
*/ */
protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void protected addScavCaseRewardsToProfile(pmcData: IPmcData, rewards: Product[], recipeId: string): void {
{
pmcData.Hideout.Production[`ScavCase${recipeId}`] = { Products: rewards }; pmcData.Hideout.Production[`ScavCase${recipeId}`] = { Products: rewards };
} }
@ -750,8 +683,7 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutContinuousProductionStartRequestData, request: IHideoutContinuousProductionStartRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.hideoutHelper.registerProduction(pmcData, request, sessionID); this.hideoutHelper.registerProduction(pmcData, request, sessionID);
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
@ -769,13 +701,11 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutTakeProductionRequestData, request: IHideoutTakeProductionRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const hideoutDb = this.databaseService.getHideout(); const hideoutDb = this.databaseService.getHideout();
if (request.recipeId === HideoutHelper.bitcoinFarm) if (request.recipeId === HideoutHelper.bitcoinFarm) {
{
// Ensure server and client are in-sync when player presses 'get items' on farm // Ensure server and client are in-sync when player presses 'get items' on farm
this.hideoutHelper.updatePlayerHideout(sessionID); this.hideoutHelper.updatePlayerHideout(sessionID);
this.hideoutHelper.getBTC(pmcData, request, sessionID, output); this.hideoutHelper.getBTC(pmcData, request, sessionID, output);
@ -784,16 +714,14 @@ export class HideoutController
} }
const recipe = hideoutDb.production.find((r) => r._id === request.recipeId); const recipe = hideoutDb.production.find((r) => r._id === request.recipeId);
if (recipe) if (recipe) {
{
this.handleRecipe(sessionID, recipe, pmcData, request, output); this.handleRecipe(sessionID, recipe, pmcData, request, output);
return output; return output;
} }
const scavCase = hideoutDb.scavcase.find((r) => r._id === request.recipeId); const scavCase = hideoutDb.scavcase.find((r) => r._id === request.recipeId);
if (scavCase) if (scavCase) {
{
this.handleScavCase(sessionID, pmcData, request, output); this.handleScavCase(sessionID, pmcData, request, output);
return output; return output;
@ -823,24 +751,19 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutTakeProductionRequestData, request: IHideoutTakeProductionRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
// Validate that we have a matching production // Validate that we have a matching production
const productionDict = Object.entries(pmcData.Hideout.Production); const productionDict = Object.entries(pmcData.Hideout.Production);
let prodId: string; let prodId: string;
for (const [productionId, production] of productionDict) for (const [productionId, production] of productionDict) {
{
// Skip undefined production objects // Skip undefined production objects
if (!production) if (!production) {
{
continue; continue;
} }
if (this.hideoutHelper.isProductionType(production)) if (this.hideoutHelper.isProductionType(production)) {
{
// Production or ScavCase // Production or ScavCase
if (production.RecipeId === request.recipeId) if (production.RecipeId === request.recipeId) {
{
prodId = productionId; // Set to objects key prodId = productionId; // Set to objects key
break; break;
} }
@ -848,8 +771,7 @@ export class HideoutController
} }
// If we're unable to find the production, send an error to the client // If we're unable to find the production, send an error to the client
if (prodId === undefined) if (prodId === undefined) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id", "hideout-unable_to_find_production_in_profile_by_recipie_id",
@ -879,8 +801,7 @@ export class HideoutController
// Reward is weapon/armor preset, handle differently compared to 'normal' items // Reward is weapon/armor preset, handle differently compared to 'normal' items
const rewardIsPreset = this.presetHelper.hasPreset(recipe.endProduct); const rewardIsPreset = this.presetHelper.hasPreset(recipe.endProduct);
if (rewardIsPreset) if (rewardIsPreset) {
{
const preset = this.presetHelper.getDefaultPreset(recipe.endProduct); const preset = this.presetHelper.getDefaultPreset(recipe.endProduct);
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory // Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
@ -893,8 +814,7 @@ export class HideoutController
} }
const rewardIsStackable = this.itemHelper.isItemTplStackable(recipe.endProduct); const rewardIsStackable = this.itemHelper.isItemTplStackable(recipe.endProduct);
if (rewardIsStackable) if (rewardIsStackable) {
{
// Create root item // Create root item
const rewardToAdd: Item = { const rewardToAdd: Item = {
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
@ -905,32 +825,26 @@ export class HideoutController
// Split item into separate items with acceptable stack sizes // Split item into separate items with acceptable stack sizes
const splitReward = this.itemHelper.splitStackIntoSeparateItems(rewardToAdd); const splitReward = this.itemHelper.splitStackIntoSeparateItems(rewardToAdd);
itemAndChildrenToSendToPlayer.push(...splitReward); itemAndChildrenToSendToPlayer.push(...splitReward);
} } else {
else
{
// Not stackable, may have to send send multiple of reward // Not stackable, may have to send send multiple of reward
// Add the first reward item to array when not a preset (first preset added above earlier) // Add the first reward item to array when not a preset (first preset added above earlier)
if (!rewardIsPreset) if (!rewardIsPreset) {
{
itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]); itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]);
} }
// Add multiple of item if recipe requests it // Add multiple of item if recipe requests it
// Start index at one so we ignore first item in array // Start index at one so we ignore first item in array
const countOfItemsToReward = recipe.count; const countOfItemsToReward = recipe.count;
for (let index = 1; index < countOfItemsToReward; index++) for (let index = 1; index < countOfItemsToReward; index++) {
{
const itemAndMods: Item[] = this.itemHelper.replaceIDs(itemAndChildrenToSendToPlayer[0]); const itemAndMods: Item[] = this.itemHelper.replaceIDs(itemAndChildrenToSendToPlayer[0]);
itemAndChildrenToSendToPlayer.push(...[itemAndMods]); itemAndChildrenToSendToPlayer.push(...[itemAndMods]);
} }
} }
// Recipe has an `isEncoded` requirement for reward(s), Add `RecodableComponent` property // Recipe has an `isEncoded` requirement for reward(s), Add `RecodableComponent` property
if (recipe.isEncoded) if (recipe.isEncoded) {
{ for (const reward of itemAndChildrenToSendToPlayer) {
for (const reward of itemAndChildrenToSendToPlayer)
{
this.itemHelper.addUpdObjectToItem(reward[0]); this.itemHelper.addUpdObjectToItem(reward[0]);
reward[0].upd.RecodableComponent = { IsEncoded: true }; reward[0].upd.RecodableComponent = { IsEncoded: true };
@ -940,18 +854,15 @@ export class HideoutController
// Build an array of the tools that need to be returned to the player // Build an array of the tools that need to be returned to the player
const toolsToSendToPlayer: Item[][] = []; const toolsToSendToPlayer: Item[][] = [];
const production = pmcData.Hideout.Production[prodId]; const production = pmcData.Hideout.Production[prodId];
if (production.sptRequiredTools?.length > 0) if (production.sptRequiredTools?.length > 0) {
{ for (const tool of production.sptRequiredTools) {
for (const tool of production.sptRequiredTools)
{
toolsToSendToPlayer.push([tool]); toolsToSendToPlayer.push([tool]);
} }
} }
// Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times // Check if the recipe is the same as the last one - get bonus when crafting same thing multiple times
const area = pmcData.Hideout.Areas.find((area) => area.type === recipe.areaType); const area = pmcData.Hideout.Areas.find((area) => area.type === recipe.areaType);
if (area && request.recipeId !== area.lastRecipe) if (area && request.recipeId !== area.lastRecipe) {
{
// 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module // 1 point per craft upon the end of production for alternating between 2 different crafting recipes in the same module
craftingExpAmount += this.hideoutConfig.expCraftAmount; // Default is 10 craftingExpAmount += this.hideoutConfig.expCraftAmount; // Default is 10
} }
@ -959,8 +870,7 @@ export class HideoutController
// Update variable with time spent crafting item(s) // Update variable with time spent crafting item(s)
// 1 point per 8 hours of crafting // 1 point per 8 hours of crafting
hoursCrafting += recipe.productionTime; hoursCrafting += recipe.productionTime;
if (hoursCrafting / this.hideoutConfig.hoursForSkillCrafting >= 1) if (hoursCrafting / this.hideoutConfig.hoursForSkillCrafting >= 1) {
{
// Spent enough time crafting to get a bonus xp multipler // Spent enough time crafting to get a bonus xp multipler
const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting); const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
craftingExpAmount += 1 * multiplierCrafting; craftingExpAmount += 1 * multiplierCrafting;
@ -969,8 +879,7 @@ export class HideoutController
// Make sure we can fit both the craft result and tools in the stash // Make sure we can fit both the craft result and tools in the stash
const totalResultItems = toolsToSendToPlayer.concat(itemAndChildrenToSendToPlayer); const totalResultItems = toolsToSendToPlayer.concat(itemAndChildrenToSendToPlayer);
if (!this.inventoryHelper.canPlaceItemsInInventory(sessionID, totalResultItems)) if (!this.inventoryHelper.canPlaceItemsInInventory(sessionID, totalResultItems)) {
{
this.httpResponse.appendErrorToOutput( this.httpResponse.appendErrorToOutput(
output, output,
this.localisationService.getText("inventory-no_stash_space"), this.localisationService.getText("inventory-no_stash_space"),
@ -980,8 +889,7 @@ export class HideoutController
} }
// Add the tools to the stash, we have to do this individually due to FiR state potentially being different // Add the tools to the stash, we have to do this individually due to FiR state potentially being different
for (const toolItem of toolsToSendToPlayer) for (const toolItem of toolsToSendToPlayer) {
{
// Note: FIR state will be based on the first item's SpawnedInSession property per item group // Note: FIR state will be based on the first item's SpawnedInSession property per item group
const addToolsRequest: IAddItemsDirectRequest = { const addToolsRequest: IAddItemsDirectRequest = {
itemsWithModsToAdd: [toolItem], itemsWithModsToAdd: [toolItem],
@ -991,8 +899,7 @@ export class HideoutController
}; };
this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return; return;
} }
} }
@ -1005,8 +912,7 @@ export class HideoutController
callback: undefined, callback: undefined,
}; };
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return; return;
} }
@ -1023,13 +929,11 @@ export class HideoutController
); );
// Add Crafting skill to player profile // Add Crafting skill to player profile
if (craftingExpAmount > 0) if (craftingExpAmount > 0) {
{
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount); this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
const intellectAmountToGive = 0.5 * Math.round(craftingExpAmount / 15); const intellectAmountToGive = 0.5 * Math.round(craftingExpAmount / 15);
if (intellectAmountToGive > 0) if (intellectAmountToGive > 0) {
{
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive); this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive);
} }
} }
@ -1043,8 +947,7 @@ export class HideoutController
pmcData.Hideout.Production[prodId].sptIsContinuous = recipe.continuous; pmcData.Hideout.Production[prodId].sptIsContinuous = recipe.continuous;
// Flag normal (non continious) crafts as complete // Flag normal (non continious) crafts as complete
if (!recipe.continuous) if (!recipe.continuous) {
{
pmcData.Hideout.Production[prodId].inProgress = false; pmcData.Hideout.Production[prodId].inProgress = false;
} }
} }
@ -1058,11 +961,9 @@ export class HideoutController
protected getHoursCraftingTaskConditionCounter( protected getHoursCraftingTaskConditionCounter(
pmcData: IPmcData, pmcData: IPmcData,
recipe: IHideoutProduction, recipe: IHideoutProduction,
): ITaskConditionCounter ): ITaskConditionCounter {
{
let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting]; let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting];
if (!counterHoursCrafting) if (!counterHoursCrafting) {
{
// Doesn't exist, create // Doesn't exist, create
pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting] = { pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting] = {
id: recipe._id, id: recipe._id,
@ -1087,25 +988,20 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutTakeProductionRequestData, request: IHideoutTakeProductionRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const ongoingProductions = Object.entries(pmcData.Hideout.Production); const ongoingProductions = Object.entries(pmcData.Hideout.Production);
let prodId: string; let prodId: string;
for (const production of ongoingProductions) for (const production of ongoingProductions) {
{ if (this.hideoutHelper.isProductionType(production[1])) {
if (this.hideoutHelper.isProductionType(production[1]))
{
// Production or ScavCase // Production or ScavCase
if ((production[1] as ScavCase).RecipeId === request.recipeId) if ((production[1] as ScavCase).RecipeId === request.recipeId) {
{
prodId = production[0]; // Set to objects key prodId = production[0]; // Set to objects key
break; break;
} }
} }
} }
if (prodId === undefined) if (prodId === undefined) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"hideout-unable_to_find_production_in_profile_by_recipie_id", "hideout-unable_to_find_production_in_profile_by_recipie_id",
@ -1129,8 +1025,7 @@ export class HideoutController
}; };
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return; return;
} }
@ -1150,8 +1045,7 @@ export class HideoutController
* @param sessionId Session id * @param sessionId Session id
* @returns IQteData array * @returns IQteData array
*/ */
public getQteList(sessionId: string): IQteData[] public getQteList(sessionId: string): IQteData[] {
{
return this.databaseService.getHideout().qte; return this.databaseService.getHideout().qte;
} }
@ -1167,8 +1061,7 @@ export class HideoutController
pmcData: IPmcData, pmcData: IPmcData,
request: IHandleQTEEventRequestData, request: IHandleQTEEventRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
// { // {
// "Action": "HideoutQuickTimeEvent", // "Action": "HideoutQuickTimeEvent",
// "results": [true, false, true, true, true, true, true, true, true, false, false, false, false, false, false], // "results": [true, false, true, true, true, true, true, true, true, false, false, false, false, false, false],
@ -1180,14 +1073,12 @@ export class HideoutController
// /client/hideout/workout (applyWorkoutChanges). // /client/hideout/workout (applyWorkoutChanges).
pmcData.Health.Energy.Current -= 50; pmcData.Health.Energy.Current -= 50;
if (pmcData.Health.Energy.Current < 1) if (pmcData.Health.Energy.Current < 1) {
{
pmcData.Health.Energy.Current = 1; pmcData.Health.Energy.Current = 1;
} }
pmcData.Health.Hydration.Current -= 50; pmcData.Health.Hydration.Current -= 50;
if (pmcData.Health.Hydration.Current < 1) if (pmcData.Health.Hydration.Current < 1) {
{
pmcData.Health.Hydration.Current = 1; pmcData.Health.Hydration.Current = 1;
} }
} }
@ -1199,15 +1090,13 @@ export class HideoutController
* @param request shooting range score request * @param request shooting range score request
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void {
{
const shootingRangeKey = "ShootingRangePoints"; const shootingRangeKey = "ShootingRangePoints";
const overallCounterItems = pmcData.Stats.Eft.OverallCounters.Items; const overallCounterItems = pmcData.Stats.Eft.OverallCounters.Items;
// Find counter by key // Find counter by key
let shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey)); let shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey));
if (!shootingRangeHighScore) if (!shootingRangeHighScore) {
{
// Counter not found, add blank one // Counter not found, add blank one
overallCounterItems.push({ Key: [shootingRangeKey], Value: 0 }); overallCounterItems.push({ Key: [shootingRangeKey], Value: 0 });
shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey)); shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey));
@ -1226,22 +1115,18 @@ export class HideoutController
sessionId: string, sessionId: string,
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutImproveAreaRequestData, request: IHideoutImproveAreaRequestData,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
// Create mapping of required item with corrisponding item from player inventory // Create mapping of required item with corrisponding item from player inventory
const items = request.items.map((reqItem) => const items = request.items.map((reqItem) => {
{
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id); const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
return { inventoryItem: item, requestedItem: reqItem }; return { inventoryItem: item, requestedItem: reqItem };
}); });
// If it's not money, its construction / barter items // If it's not money, its construction / barter items
for (const item of items) for (const item of items) {
{ if (!item.inventoryItem) {
if (!item.inventoryItem)
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id), this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
); );
@ -1249,31 +1134,25 @@ export class HideoutController
} }
if ( if (
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
&& item.inventoryItem.upd item.inventoryItem.upd &&
&& item.inventoryItem.upd.StackObjectsCount item.inventoryItem.upd.StackObjectsCount &&
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
) ) {
{
item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count; item.inventoryItem.upd.StackObjectsCount -= item.requestedItem.count;
} } else {
else
{
this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionId, output); this.inventoryHelper.removeItem(pmcData, item.inventoryItem._id, sessionId, output);
} }
} }
const profileHideoutArea = pmcData.Hideout.Areas.find((x) => x.type === request.areaType); const profileHideoutArea = pmcData.Hideout.Areas.find((x) => x.type === request.areaType);
if (!profileHideoutArea) if (!profileHideoutArea) {
{
this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType)); this.logger.error(this.localisationService.getText("hideout-unable_to_find_area", request.areaType));
return this.httpResponse.appendErrorToOutput(output); return this.httpResponse.appendErrorToOutput(output);
} }
const hideoutDbData = this.databaseService.getHideout().areas const hideoutDbData = this.databaseService.getHideout().areas.find((area) => area.type === request.areaType);
.find((area) => area.type === request.areaType); if (!hideoutDbData) {
if (!hideoutDbData)
{
this.logger.error( this.logger.error(
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType), this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
); );
@ -1284,13 +1163,11 @@ export class HideoutController
const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements; const improvements = hideoutDbData.stages[profileHideoutArea.level].improvements;
const timestamp = this.timeUtil.getTimestamp(); const timestamp = this.timeUtil.getTimestamp();
if (!output.profileChanges[sessionId].improvements) if (!output.profileChanges[sessionId].improvements) {
{
output.profileChanges[sessionId].improvements = {}; output.profileChanges[sessionId].improvements = {};
} }
for (const improvement of improvements) for (const improvement of improvements) {
{
const improvementDetails = { const improvementDetails = {
completed: false, completed: false,
improveCompleteTimestamp: timestamp + improvement.improvementTime, improveCompleteTimestamp: timestamp + improvement.improvementTime,
@ -1313,13 +1190,11 @@ export class HideoutController
sessionId: string, sessionId: string,
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutCancelProductionRequestData, request: IHideoutCancelProductionRequestData,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
const craftToCancel = pmcData.Hideout.Production[request.recipeId]; const craftToCancel = pmcData.Hideout.Production[request.recipeId];
if (!craftToCancel) if (!craftToCancel) {
{
const errorMessage = `Unable to find craft ${request.recipeId} to cancel`; const errorMessage = `Unable to find craft ${request.recipeId} to cancel`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -1337,18 +1212,15 @@ export class HideoutController
/** /**
* Function called every x seconds as part of onUpdate event * Function called every x seconds as part of onUpdate event
*/ */
public update(): void public update(): void {
{ for (const sessionID in this.saveServer.getProfiles()) {
for (const sessionID in this.saveServer.getProfiles())
{
if ( if (
"Hideout" in this.saveServer.getProfile(sessionID).characters.pmc "Hideout" in this.saveServer.getProfile(sessionID).characters.pmc &&
&& this.profileActivityService.activeWithinLastMinutes( this.profileActivityService.activeWithinLastMinutes(
sessionID, sessionID,
this.hideoutConfig.updateProfileHideoutWhenActiveWithinMinutes, this.hideoutConfig.updateProfileHideoutWhenActiveWithinMinutes,
) )
) ) {
{
this.hideoutHelper.updatePlayerHideout(sessionID); this.hideoutHelper.updatePlayerHideout(sessionID);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt/context/ApplicationContext"; import { ApplicationContext } from "@spt/context/ApplicationContext";
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
@ -11,13 +10,13 @@ import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { inject, injectable } from "tsyringe";
/** /**
* Logic for handling In Raid callbacks * Logic for handling In Raid callbacks
*/ */
@injectable() @injectable()
export class InraidController export class InraidController {
{
protected inRaidConfig: IInRaidConfig; protected inRaidConfig: IInRaidConfig;
protected botConfig: IBotConfig; protected botConfig: IBotConfig;
@ -28,8 +27,7 @@ export class InraidController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.inRaidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID); this.inRaidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID);
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
} }
@ -39,18 +37,15 @@ export class InraidController
* @param sessionID Session id * @param sessionID Session id
* @param info Register player request * @param info Register player request
*/ */
public addPlayer(sessionID: string, info: IRegisterPlayerRequestData): void public addPlayer(sessionID: string, info: IRegisterPlayerRequestData): void {
{
this.applicationContext.addValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info); this.applicationContext.addValue(ContextVariableType.REGISTER_PLAYER_REQUEST, info);
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
if (!profile) if (!profile) {
{
this.logger.error(this.localisationService.getText("inraid-no_profile_found", sessionID)); this.logger.error(this.localisationService.getText("inraid-no_profile_found", sessionID));
return; return;
} }
if (!profile.inraid) if (!profile.inraid) {
{
profile.inraid = { character: sessionID, location: info.locationId }; profile.inraid = { character: sessionID, location: info.locationId };
return; return;
@ -66,8 +61,7 @@ export class InraidController
* @param offraidData post-raid request data * @param offraidData post-raid request data
* @param sessionID Session id * @param sessionID Session id
*/ */
public savePostRaidProfileForScav(offraidData: IScavSaveRequestData, sessionID: string): void public savePostRaidProfileForScav(offraidData: IScavSaveRequestData, sessionID: string): void {
{
const serverScavProfile = this.profileHelper.getScavProfile(sessionID); const serverScavProfile = this.profileHelper.getScavProfile(sessionID);
serverScavProfile.Inventory.items = offraidData.profile.Inventory.items; serverScavProfile.Inventory.items = offraidData.profile.Inventory.items;
@ -77,18 +71,15 @@ export class InraidController
* Get the inraid config from configs/inraid.json * Get the inraid config from configs/inraid.json
* @returns InRaid Config * @returns InRaid Config
*/ */
public getInraidConfig(): IInRaidConfig public getInraidConfig(): IInRaidConfig {
{
return this.inRaidConfig; return this.inRaidConfig;
} }
public getTraitorScavHostileChance(url: string, sessionID: string): number public getTraitorScavHostileChance(url: string, sessionID: string): number {
{
return this.inRaidConfig.playerScavHostileChancePercent; return this.inRaidConfig.playerScavHostileChancePercent;
} }
public getBossConvertSettings(url: string, sessionId: string): string[] public getBossConvertSettings(url: string, sessionId: string): string[] {
{
return Object.keys(this.botConfig.assaultToBossConversion.bossesToConvertToWeights); return Object.keys(this.botConfig.assaultToBossConversion.bossesToConvertToWeights);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { DialogueHelper } from "@spt/helpers/DialogueHelper"; import { DialogueHelper } from "@spt/helpers/DialogueHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
@ -26,15 +25,15 @@ import { LocalisationService } from "@spt/services/LocalisationService";
import { MailSendService } from "@spt/services/MailSendService"; import { MailSendService } from "@spt/services/MailSendService";
import { PaymentService } from "@spt/services/PaymentService"; import { PaymentService } from "@spt/services/PaymentService";
import { RagfairPriceService } from "@spt/services/RagfairPriceService"; import { RagfairPriceService } from "@spt/services/RagfairPriceService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { MathUtil } from "@spt/utils/MathUtil"; import { MathUtil } from "@spt/utils/MathUtil";
import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "@spt/utils/RandomUtil"; import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class InsuranceController export class InsuranceController {
{
protected insuranceConfig: IInsuranceConfig; protected insuranceConfig: IInsuranceConfig;
constructor( constructor(
@ -58,8 +57,7 @@ export class InsuranceController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
} }
@ -68,11 +66,9 @@ export class InsuranceController
* *
* @returns void * @returns void
*/ */
public processReturn(): void public processReturn(): void {
{
// Process each installed profile. // Process each installed profile.
for (const sessionID in this.saveServer.getProfiles()) for (const sessionID in this.saveServer.getProfiles()) {
{
this.processReturnByProfile(sessionID); this.processReturnByProfile(sessionID);
} }
} }
@ -82,14 +78,12 @@ export class InsuranceController
* *
* @returns void * @returns void
*/ */
public processReturnByProfile(sessionID: string): void public processReturnByProfile(sessionID: string): void {
{
// Filter out items that don't need to be processed yet. // Filter out items that don't need to be processed yet.
const insuranceDetails = this.filterInsuredItems(sessionID); const insuranceDetails = this.filterInsuredItems(sessionID);
// Skip profile if no insured items to process // Skip profile if no insured items to process
if (insuranceDetails.length === 0) if (insuranceDetails.length === 0) {
{
return; return;
} }
@ -103,14 +97,12 @@ export class InsuranceController
* @param time The time to check ready status against. Current time by default. * @param time The time to check ready status against. Current time by default.
* @returns All insured items that are ready to be processed. * @returns All insured items that are ready to be processed.
*/ */
protected filterInsuredItems(sessionID: string, time?: number): Insurance[] protected filterInsuredItems(sessionID: string, time?: number): Insurance[] {
{
// Use the current time by default. // Use the current time by default.
const insuranceTime = time || this.timeUtil.getTimestamp(); const insuranceTime = time || this.timeUtil.getTimestamp();
const profileInsuranceDetails = this.saveServer.getProfile(sessionID).insurance; const profileInsuranceDetails = this.saveServer.getProfile(sessionID).insurance;
if (profileInsuranceDetails.length > 0) if (profileInsuranceDetails.length > 0) {
{
this.logger.debug(`Found ${profileInsuranceDetails.length} insurance packages in profile ${sessionID}`); this.logger.debug(`Found ${profileInsuranceDetails.length} insurance packages in profile ${sessionID}`);
} }
@ -124,8 +116,7 @@ export class InsuranceController
* @param sessionID The session ID that should receive the processed items. * @param sessionID The session ID that should receive the processed items.
* @returns void * @returns void
*/ */
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void {
{
this.logger.debug( this.logger.debug(
`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems( `Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems(
insuranceDetails, insuranceDetails,
@ -136,8 +127,7 @@ export class InsuranceController
const rootItemParentID = this.insuranceService.getRootItemParentID(sessionID); const rootItemParentID = this.insuranceService.getRootItemParentID(sessionID);
// Iterate over each of the insurance packages. // Iterate over each of the insurance packages.
for (const insured of insuranceDetails) for (const insured of insuranceDetails) {
{
// Find items that should be deleted from the insured items. // Find items that should be deleted from the insured items.
const itemsToDelete = this.findItemsToDelete(rootItemParentID, insured); const itemsToDelete = this.findItemsToDelete(rootItemParentID, insured);
@ -160,8 +150,7 @@ export class InsuranceController
* @param insurance * @param insurance
* @returns * @returns
*/ */
protected countAllInsuranceItems(insurance: Insurance[]): number protected countAllInsuranceItems(insurance: Insurance[]): number {
{
return this.mathUtil.arraySum(insurance.map((ins) => ins.items.length)); return this.mathUtil.arraySum(insurance.map((ins) => ins.items.length));
} }
@ -172,15 +161,14 @@ export class InsuranceController
* @param index The array index of the insurance package to remove. * @param index The array index of the insurance package to remove.
* @returns void * @returns void
*/ */
protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void {
{
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
profile.insurance = profile.insurance.filter( profile.insurance = profile.insurance.filter(
(insurance) => (insurance) =>
insurance.traderId !== insPackage.traderId insurance.traderId !== insPackage.traderId ||
|| insurance.systemData.date !== insPackage.systemData.date insurance.systemData.date !== insPackage.systemData.date ||
|| insurance.systemData.time !== insPackage.systemData.time insurance.systemData.time !== insPackage.systemData.time ||
|| insurance.systemData.location !== insPackage.systemData.location, insurance.systemData.location !== insPackage.systemData.location,
); );
this.logger.debug(`Removed processed insurance package. Remaining packages: ${profile.insurance.length}`); this.logger.debug(`Removed processed insurance package. Remaining packages: ${profile.insurance.length}`);
@ -193,8 +181,7 @@ export class InsuranceController
* @param insured - The insurance object containing the items to evaluate for deletion. * @param insured - The insurance object containing the items to evaluate for deletion.
* @returns A Set containing the IDs of items that should be deleted. * @returns A Set containing the IDs of items that should be deleted.
*/ */
protected findItemsToDelete(rootItemParentID: string, insured: Insurance): Set<string> protected findItemsToDelete(rootItemParentID: string, insured: Insurance): Set<string> {
{
const toDelete = new Set<string>(); const toDelete = new Set<string>();
// Populate a Map object of items for quick lookup by their ID and use it to populate a Map of main-parent items // Populate a Map object of items for quick lookup by their ID and use it to populate a Map of main-parent items
@ -208,14 +195,12 @@ export class InsuranceController
); );
// Process all items that are not attached, attachments; those are handled separately, by value. // Process all items that are not attached, attachments; those are handled separately, by value.
if (hasRegularItems) if (hasRegularItems) {
{
this.processRegularItems(insured, toDelete, parentAttachmentsMap); this.processRegularItems(insured, toDelete, parentAttachmentsMap);
} }
// Process attached, attachments, by value, only if there are any. // Process attached, attachments, by value, only if there are any.
if (parentAttachmentsMap.size > 0) if (parentAttachmentsMap.size > 0) {
{
// Remove attachments that can not be moddable in-raid from the parentAttachmentsMap. We only want to // Remove attachments that can not be moddable in-raid from the parentAttachmentsMap. We only want to
// process moddable attachments from here on out. // process moddable attachments from here on out.
parentAttachmentsMap = this.removeNonModdableAttachments(parentAttachmentsMap, itemsMap); parentAttachmentsMap = this.removeNonModdableAttachments(parentAttachmentsMap, itemsMap);
@ -224,8 +209,7 @@ export class InsuranceController
} }
// Log the number of items marked for deletion, if any // Log the number of items marked for deletion, if any
if (toDelete.size) if (toDelete.size) {
{
this.logger.debug(`Marked ${toDelete.size} items for deletion from insurance.`); this.logger.debug(`Marked ${toDelete.size} items for deletion from insurance.`);
} }
@ -246,65 +230,58 @@ export class InsuranceController
rootItemParentID: string, rootItemParentID: string,
insured: Insurance, insured: Insurance,
itemsMap: Map<string, Item>, itemsMap: Map<string, Item>,
): Map<string, Item[]> ): Map<string, Item[]> {
{
const mainParentToAttachmentsMap = new Map<string, Item[]>(); const mainParentToAttachmentsMap = new Map<string, Item[]>();
for (const insuredItem of insured.items) for (const insuredItem of insured.items) {
{
// Use the parent ID from the item to get the parent item. // Use the parent ID from the item to get the parent item.
const parentItem = insured.items.find((item) => item._id === insuredItem.parentId); const parentItem = insured.items.find((item) => item._id === insuredItem.parentId);
// The parent (not the hideout) could not be found. Skip and warn. // The parent (not the hideout) could not be found. Skip and warn.
if (!parentItem && insuredItem.parentId !== rootItemParentID) if (!parentItem && insuredItem.parentId !== rootItemParentID) {
{ this.logger.warning(
this.logger.warning(this.localisationService this.localisationService.getText("insurance-unable_to_find_parent_of_item", {
.getText("insurance-unable_to_find_parent_of_item", insuredItemId: insuredItem._id,
{ insuredItemTpl: insuredItem._tpl,
insuredItemId: insuredItem._id, parentId: insuredItem.parentId,
insuredItemTpl: insuredItem._tpl, }),
parentId: insuredItem.parentId, );
}));
continue; continue;
} }
// Check if this is an attachment currently attached to its parent. // Check if this is an attachment currently attached to its parent.
if (this.itemHelper.isAttachmentAttached(insuredItem)) if (this.itemHelper.isAttachmentAttached(insuredItem)) {
{
// Make sure the template for the item exists. // Make sure the template for the item exists.
if (!this.itemHelper.getItem(insuredItem._tpl)[0]) if (!this.itemHelper.getItem(insuredItem._tpl)[0]) {
{ this.logger.warning(
this.logger.warning(this.localisationService.getText("insurance-unable_to_find_attachment_in_db", this.localisationService.getText("insurance-unable_to_find_attachment_in_db", {
{
insuredItemId: insuredItem._id, insuredItemId: insuredItem._id,
insuredItemTpl: insuredItem._tpl, insuredItemTpl: insuredItem._tpl,
})); }),
);
continue; continue;
} }
// Get the main parent of this attachment. (e.g., The gun that this suppressor is attached to.) // Get the main parent of this attachment. (e.g., The gun that this suppressor is attached to.)
const mainParent = this.itemHelper.getAttachmentMainParent(insuredItem._id, itemsMap); const mainParent = this.itemHelper.getAttachmentMainParent(insuredItem._id, itemsMap);
if (!mainParent) if (!mainParent) {
{
// Odd. The parent couldn't be found. Skip this attachment and warn. // Odd. The parent couldn't be found. Skip this attachment and warn.
this.logger.warning(this.localisationService.getText("insurance-unable_to_find_main_parent_for_attachment", this.logger.warning(
{ this.localisationService.getText("insurance-unable_to_find_main_parent_for_attachment", {
insuredItemId: insuredItem._id, insuredItemId: insuredItem._id,
insuredItemTpl: insuredItem._tpl, insuredItemTpl: insuredItem._tpl,
parentId: insuredItem.parentId, parentId: insuredItem.parentId,
})); }),
);
continue; continue;
} }
// Update (or add to) the main-parent to attachments map. // Update (or add to) the main-parent to attachments map.
if (mainParentToAttachmentsMap.has(mainParent._id)) if (mainParentToAttachmentsMap.has(mainParent._id)) {
{
mainParentToAttachmentsMap.get(mainParent._id).push(insuredItem); mainParentToAttachmentsMap.get(mainParent._id).push(insuredItem);
} } else {
else
{
mainParentToAttachmentsMap.set(mainParent._id, [insuredItem]); mainParentToAttachmentsMap.set(mainParent._id, [insuredItem]);
} }
} }
@ -323,40 +300,33 @@ export class InsuranceController
protected removeNonModdableAttachments( protected removeNonModdableAttachments(
parentAttachmentsMap: Map<string, Item[]>, parentAttachmentsMap: Map<string, Item[]>,
itemsMap: Map<string, Item>, itemsMap: Map<string, Item>,
): Map<string, Item[]> ): Map<string, Item[]> {
{
const updatedMap = new Map<string, Item[]>(); const updatedMap = new Map<string, Item[]>();
for (const [parentId, attachmentItems] of parentAttachmentsMap) for (const [parentId, attachmentItems] of parentAttachmentsMap) {
{
const parentItem = itemsMap.get(parentId); const parentItem = itemsMap.get(parentId);
const moddableAttachments: Item[] = []; const moddableAttachments: Item[] = [];
for (const attachment of attachmentItems) for (const attachment of attachmentItems) {
{
// By default, assume the parent of the current attachment is the main-parent included in the map. // By default, assume the parent of the current attachment is the main-parent included in the map.
let attachmentParentItem = parentItem; let attachmentParentItem = parentItem;
// If the attachment includes a parentId, use it to find its direct parent item, even if it's another // If the attachment includes a parentId, use it to find its direct parent item, even if it's another
// attachment on the main-parent. For example, if the attachment is a stock, we need to check to see if // attachment on the main-parent. For example, if the attachment is a stock, we need to check to see if
// it's moddable in the upper receiver (attachment/parent), which is attached to the gun (main-parent). // it's moddable in the upper receiver (attachment/parent), which is attached to the gun (main-parent).
if (attachment.parentId) if (attachment.parentId) {
{
const directParentItem = itemsMap.get(attachment.parentId); const directParentItem = itemsMap.get(attachment.parentId);
if (directParentItem) if (directParentItem) {
{
attachmentParentItem = directParentItem; attachmentParentItem = directParentItem;
} }
} }
if (this.itemHelper.isRaidModdable(attachment, attachmentParentItem)) if (this.itemHelper.isRaidModdable(attachment, attachmentParentItem)) {
{
moddableAttachments.push(attachment); moddableAttachments.push(attachment);
} }
} }
// If any moddable attachments remain, add them to the updated map. // If any moddable attachments remain, add them to the updated map.
if (moddableAttachments.length > 0) if (moddableAttachments.length > 0) {
{
updatedMap.set(parentId, moddableAttachments); updatedMap.set(parentId, moddableAttachments);
} }
} }
@ -378,40 +348,32 @@ export class InsuranceController
insured: Insurance, insured: Insurance,
toDelete: Set<string>, toDelete: Set<string>,
parentAttachmentsMap: Map<string, Item[]>, parentAttachmentsMap: Map<string, Item[]>,
): void ): void {
{ for (const insuredItem of insured.items) {
for (const insuredItem of insured.items)
{
// Skip if the item is an attachment. These are handled separately. // Skip if the item is an attachment. These are handled separately.
if (this.itemHelper.isAttachmentAttached(insuredItem)) if (this.itemHelper.isAttachmentAttached(insuredItem)) {
{
continue; continue;
} }
// Roll for item deletion // Roll for item deletion
const itemRoll = this.rollForDelete(insured.traderId, insuredItem); const itemRoll = this.rollForDelete(insured.traderId, insuredItem);
if (itemRoll) if (itemRoll) {
{
// Check to see if this item is a parent in the parentAttachmentsMap. If so, do a look-up for *all* of // Check to see if this item is a parent in the parentAttachmentsMap. If so, do a look-up for *all* of
// its children and mark them for deletion as well. Additionally remove the parent (and its children) // its children and mark them for deletion as well. Additionally remove the parent (and its children)
// from the parentAttachmentsMap so that it's children are not rolled for later in the process. // from the parentAttachmentsMap so that it's children are not rolled for later in the process.
if (parentAttachmentsMap.has(insuredItem._id)) if (parentAttachmentsMap.has(insuredItem._id)) {
{
// This call will also return the parent item itself, queueing it for deletion as well. // This call will also return the parent item itself, queueing it for deletion as well.
const itemAndChildren = this.itemHelper.findAndReturnChildrenAsItems( const itemAndChildren = this.itemHelper.findAndReturnChildrenAsItems(
insured.items, insured.items,
insuredItem._id, insuredItem._id,
); );
for (const item of itemAndChildren) for (const item of itemAndChildren) {
{
toDelete.add(item._id); toDelete.add(item._id);
} }
// Remove the parent (and its children) from the parentAttachmentsMap. // Remove the parent (and its children) from the parentAttachmentsMap.
parentAttachmentsMap.delete(insuredItem._id); parentAttachmentsMap.delete(insuredItem._id);
} } else {
else
{
// This item doesn't have any children. Simply mark it for deletion. // This item doesn't have any children. Simply mark it for deletion.
toDelete.add(insuredItem._id); toDelete.add(insuredItem._id);
} }
@ -432,14 +394,11 @@ export class InsuranceController
itemsMap: Map<string, Item>, itemsMap: Map<string, Item>,
traderId: string, traderId: string,
toDelete: Set<string>, toDelete: Set<string>,
): void ): void {
{ for (const [parentId, attachmentItems] of mainParentToAttachmentsMap) {
for (const [parentId, attachmentItems] of mainParentToAttachmentsMap)
{
// Skip processing if parentId is already marked for deletion, as all attachments for that parent will // Skip processing if parentId is already marked for deletion, as all attachments for that parent will
// already be marked for deletion as well. // already be marked for deletion as well.
if (toDelete.has(parentId)) if (toDelete.has(parentId)) {
{
continue; continue;
} }
@ -464,8 +423,7 @@ export class InsuranceController
* @param toDelete The array that accumulates the IDs of the items to be deleted. * @param toDelete The array that accumulates the IDs of the items to be deleted.
* @returns void * @returns void
*/ */
protected processAttachmentByParent(attachments: Item[], traderId: string, toDelete: Set<string>): void protected processAttachmentByParent(attachments: Item[], traderId: string, toDelete: Set<string>): void {
{
// Create dict of item ids + their flea/handbook price (highest is chosen) // Create dict of item ids + their flea/handbook price (highest is chosen)
const weightedAttachmentByPrice = this.weightAttachmentsByPrice(attachments); const weightedAttachmentByPrice = this.weightAttachmentsByPrice(attachments);
@ -474,8 +432,7 @@ export class InsuranceController
// Create prob array and add all attachments with rouble price as the weight // Create prob array and add all attachments with rouble price as the weight
const attachmentsProbabilityArray = new ProbabilityObjectArray<string, number>(this.mathUtil, this.cloner); const attachmentsProbabilityArray = new ProbabilityObjectArray<string, number>(this.mathUtil, this.cloner);
for (const attachmentTpl of Object.keys(weightedAttachmentByPrice)) for (const attachmentTpl of Object.keys(weightedAttachmentByPrice)) {
{
attachmentsProbabilityArray.push( attachmentsProbabilityArray.push(
new ProbabilityObject(attachmentTpl, weightedAttachmentByPrice[attachmentTpl]), new ProbabilityObject(attachmentTpl, weightedAttachmentByPrice[attachmentTpl]),
); );
@ -483,8 +440,7 @@ export class InsuranceController
// Draw x attachments from weighted array to remove from parent, remove from pool after being picked // Draw x attachments from weighted array to remove from parent, remove from pool after being picked
const attachmentIdsToRemove = attachmentsProbabilityArray.draw(countOfAttachmentsToRemove, false); const attachmentIdsToRemove = attachmentsProbabilityArray.draw(countOfAttachmentsToRemove, false);
for (const attachmentId of attachmentIdsToRemove) for (const attachmentId of attachmentIdsToRemove) {
{
toDelete.add(attachmentId); toDelete.add(attachmentId);
} }
@ -497,11 +453,9 @@ export class InsuranceController
attachmentIdsToRemove: string[], attachmentIdsToRemove: string[],
attachments: Item[], attachments: Item[],
attachmentPrices: Record<string, number>, attachmentPrices: Record<string, number>,
): void ): void {
{
let index = 1; let index = 1;
for (const attachmentId of attachmentIdsToRemove) for (const attachmentId of attachmentIdsToRemove) {
{
this.logger.debug( this.logger.debug(
`Attachment ${index} Id: ${attachmentId} Tpl: ${ `Attachment ${index} Id: ${attachmentId} Tpl: ${
attachments.find((x) => x._id === attachmentId)?._tpl attachments.find((x) => x._id === attachmentId)?._tpl
@ -511,16 +465,13 @@ export class InsuranceController
} }
} }
protected weightAttachmentsByPrice(attachments: Item[]): Record<string, number> protected weightAttachmentsByPrice(attachments: Item[]): Record<string, number> {
{
const result: Record<string, number> = {}; const result: Record<string, number> = {};
// Get a dictionary of item tpls + their rouble price // Get a dictionary of item tpls + their rouble price
for (const attachment of attachments) for (const attachment of attachments) {
{
const price = this.ragfairPriceService.getDynamicItemPrice(attachment._tpl, Money.ROUBLES); const price = this.ragfairPriceService.getDynamicItemPrice(attachment._tpl, Money.ROUBLES);
if (price) if (price) {
{
result[attachment._id] = Math.round(price); result[attachment._id] = Math.round(price);
} }
} }
@ -536,25 +487,20 @@ export class InsuranceController
* @param traderId Trader attachment insured against * @param traderId Trader attachment insured against
* @returns Attachment count to remove * @returns Attachment count to remove
*/ */
protected getAttachmentCountToRemove(weightedAttachmentByPrice: Record<string, number>, traderId: string): number protected getAttachmentCountToRemove(weightedAttachmentByPrice: Record<string, number>, traderId: string): number {
{
let removeCount = 0; let removeCount = 0;
if (this.randomUtil.getChance100(this.insuranceConfig.chanceNoAttachmentsTakenPercent)) if (this.randomUtil.getChance100(this.insuranceConfig.chanceNoAttachmentsTakenPercent)) {
{
return removeCount; return removeCount;
} }
for (const attachmentId of Object.keys(weightedAttachmentByPrice)) for (const attachmentId of Object.keys(weightedAttachmentByPrice)) {
{
// Below min price to be taken, skip // Below min price to be taken, skip
if (weightedAttachmentByPrice[attachmentId] < this.insuranceConfig.minAttachmentRoublePriceToBeTaken) if (weightedAttachmentByPrice[attachmentId] < this.insuranceConfig.minAttachmentRoublePriceToBeTaken) {
{
continue; continue;
} }
if (this.rollForDelete(traderId)) if (this.rollForDelete(traderId)) {
{
removeCount++; removeCount++;
} }
} }
@ -569,8 +515,7 @@ export class InsuranceController
* @param toDelete The items that should be deleted. * @param toDelete The items that should be deleted.
* @returns void * @returns void
*/ */
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void {
{
insured.items = insured.items.filter((item) => !toDelete.has(item._id)); insured.items = insured.items.filter((item) => !toDelete.has(item._id));
} }
@ -581,8 +526,7 @@ export class InsuranceController
* @param insurance The context of insurance to use. * @param insurance The context of insurance to use.
* @returns void * @returns void
*/ */
protected sendMail(sessionID: string, insurance: Insurance): void protected sendMail(sessionID: string, insurance: Insurance): void {
{
const labsId = "laboratory"; const labsId = "laboratory";
// After all of the item filtering that we've done, if there are no items remaining, the insurance has // After all of the item filtering that we've done, if there are no items remaining, the insurance has
// successfully "failed" to return anything and an appropriate message should be sent to the player. // successfully "failed" to return anything and an appropriate message should be sent to the player.
@ -590,21 +534,17 @@ export class InsuranceController
// Map is labs + insurance is disabled in base.json // Map is labs + insurance is disabled in base.json
if ( if (
insurance.systemData?.location?.toLowerCase() === labsId insurance.systemData?.location?.toLowerCase() === labsId &&
&& !(this.databaseService.getLocation(labsId).base.Insurance) !this.databaseService.getLocation(labsId).base.Insurance
) ) {
{
// Trader has labs-specific messages // Trader has labs-specific messages
// Wipe out returnable items // Wipe out returnable items
if (traderDialogMessages.insuranceFailedLabs?.length > 0) if (traderDialogMessages.insuranceFailedLabs?.length > 0) {
{
const insuranceFailedLabTemplates = traderDialogMessages.insuranceFailedLabs; const insuranceFailedLabTemplates = traderDialogMessages.insuranceFailedLabs;
insurance.messageTemplateId = this.randomUtil.getArrayValue(insuranceFailedLabTemplates); insurance.messageTemplateId = this.randomUtil.getArrayValue(insuranceFailedLabTemplates);
insurance.items = []; insurance.items = [];
} }
} } else if (insurance.items.length === 0) {
else if (insurance.items.length === 0)
{
// Not labs and no items to return // Not labs and no items to return
const insuranceFailedTemplates = traderDialogMessages.insuranceFailed; const insuranceFailedTemplates = traderDialogMessages.insuranceFailed;
insurance.messageTemplateId = this.randomUtil.getArrayValue(insuranceFailedTemplates); insurance.messageTemplateId = this.randomUtil.getArrayValue(insuranceFailedTemplates);
@ -630,11 +570,9 @@ export class InsuranceController
* @param insuredItem Optional. The item to roll for. Only used for logging. * @param insuredItem Optional. The item to roll for. Only used for logging.
* @returns true if the insured item should be removed from inventory, false otherwise, or undefined on error. * @returns true if the insured item should be removed from inventory, false otherwise, or undefined on error.
*/ */
protected rollForDelete(traderId: string, insuredItem?: Item): boolean | undefined protected rollForDelete(traderId: string, insuredItem?: Item): boolean | undefined {
{
const trader = this.traderHelper.getTraderById(traderId); const trader = this.traderHelper.getTraderById(traderId);
if (!trader) if (!trader) {
{
return undefined; return undefined;
} }
@ -664,26 +602,25 @@ export class InsuranceController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse object to send to client * @returns IItemEventRouterResponse object to send to client
*/ */
public insure(pmcData: IPmcData, body: IInsureRequestData, sessionID: string): IItemEventRouterResponse public insure(pmcData: IPmcData, body: IInsureRequestData, sessionID: string): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const itemsToInsureCount = body.items.length; const itemsToInsureCount = body.items.length;
const itemsToPay = []; const itemsToPay = [];
const inventoryItemsHash = {}; const inventoryItemsHash = {};
// Create hash of player inventory items (keyed by item id) // Create hash of player inventory items (keyed by item id)
for (const item of pmcData.Inventory.items) for (const item of pmcData.Inventory.items) {
{
inventoryItemsHash[item._id] = item; inventoryItemsHash[item._id] = item;
} }
// Get price of all items being insured // Get price of all items being insured
for (const key of body.items) for (const key of body.items) {
{
itemsToPay.push({ itemsToPay.push({
id: Money.ROUBLES, // TODO: update to handle different currencies id: Money.ROUBLES, // TODO: update to handle different currencies
count: this.insuranceService.getRoublePriceToInsureItemWithTrader( count: this.insuranceService.getRoublePriceToInsureItemWithTrader(
pmcData, inventoryItemsHash[key], pmcData,
body.tid), inventoryItemsHash[key],
body.tid,
),
}); });
} }
@ -699,14 +636,12 @@ export class InsuranceController
// pay for the item insurance // pay for the item insurance
this.paymentService.payMoney(pmcData, options, sessionID, output); this.paymentService.payMoney(pmcData, options, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return output; return output;
} }
// add items to InsuredItems list once money has been paid // add items to InsuredItems list once money has been paid
for (const key of body.items) for (const key of body.items) {
{
pmcData.InsuredItems.push({ tid: body.tid, itemId: inventoryItemsHash[key]._id }); pmcData.InsuredItems.push({ tid: body.tid, itemId: inventoryItemsHash[key]._id });
} }
@ -723,33 +658,30 @@ export class InsuranceController
* @param sessionID session id * @param sessionID session id
* @returns IGetInsuranceCostResponseData object to send to client * @returns IGetInsuranceCostResponseData object to send to client
*/ */
public cost(request: IGetInsuranceCostRequestData, sessionID: string): IGetInsuranceCostResponseData public cost(request: IGetInsuranceCostRequestData, sessionID: string): IGetInsuranceCostResponseData {
{
const response: IGetInsuranceCostResponseData = {}; const response: IGetInsuranceCostResponseData = {};
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
const inventoryItemsHash: Record<string, Item> = {}; const inventoryItemsHash: Record<string, Item> = {};
for (const item of pmcData.Inventory.items) for (const item of pmcData.Inventory.items) {
{
inventoryItemsHash[item._id] = item; inventoryItemsHash[item._id] = item;
} }
// Loop over each trader in request // Loop over each trader in request
for (const trader of request.traders) for (const trader of request.traders) {
{
const items: Record<string, number> = {}; const items: Record<string, number> = {};
for (const itemId of request.items) for (const itemId of request.items) {
{
// Ensure hash has item in it // Ensure hash has item in it
if (!inventoryItemsHash[itemId]) if (!inventoryItemsHash[itemId]) {
{
this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`); this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`);
continue; continue;
} }
items[inventoryItemsHash[itemId]._tpl] = this.insuranceService.getRoublePriceToInsureItemWithTrader( items[inventoryItemsHash[itemId]._tpl] = this.insuranceService.getRoublePriceToInsureItemWithTrader(
pmcData, inventoryItemsHash[itemId], pmcData,
trader); inventoryItemsHash[itemId],
trader,
);
} }
response[trader] = items; response[trader] = items;
@ -760,8 +692,7 @@ export class InsuranceController
} }
// Represents an insurance item that has had it's common locale-name and value added to it. // Represents an insurance item that has had it's common locale-name and value added to it.
interface EnrichedItem extends Item interface EnrichedItem extends Item {
{ name: string;
name: string dynamicPrice: number;
dynamicPrice: number
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { LootGenerator } from "@spt/generators/LootGenerator"; import { LootGenerator } from "@spt/generators/LootGenerator";
import { HideoutHelper } from "@spt/helpers/HideoutHelper"; import { HideoutHelper } from "@spt/helpers/HideoutHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper"; import { InventoryHelper } from "@spt/helpers/InventoryHelper";
@ -44,14 +43,14 @@ import { LocalisationService } from "@spt/services/LocalisationService";
import { MapMarkerService } from "@spt/services/MapMarkerService"; import { MapMarkerService } from "@spt/services/MapMarkerService";
import { PlayerService } from "@spt/services/PlayerService"; import { PlayerService } from "@spt/services/PlayerService";
import { RagfairOfferService } from "@spt/services/RagfairOfferService"; import { RagfairOfferService } from "@spt/services/RagfairOfferService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class InventoryController export class InventoryController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@ -74,8 +73,7 @@ export class InventoryController
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {}
{}
/** /**
* Move Item * Move Item
@ -92,28 +90,23 @@ export class InventoryController
moveRequest: IInventoryMoveRequestData, moveRequest: IInventoryMoveRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{ if (output.warnings.length > 0) {
if (output.warnings.length > 0)
{
return; return;
} }
// Changes made to result apply to character inventory // Changes made to result apply to character inventory
const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID); const ownerInventoryItems = this.inventoryHelper.getOwnerInventoryItems(moveRequest, sessionID);
if (ownerInventoryItems.sameInventory) if (ownerInventoryItems.sameInventory) {
{
// Dont move items from trader to profile, this can happen when editing a traders preset weapons // Dont move items from trader to profile, this can happen when editing a traders preset weapons
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail) if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail) {
{
this.appendTraderExploitErrorResponse(output); this.appendTraderExploitErrorResponse(output);
return; return;
} }
// Check for item in inventory before allowing internal transfer // Check for item in inventory before allowing internal transfer
const originalItemLocation = ownerInventoryItems.from.find((item) => item._id === moveRequest.item); const originalItemLocation = ownerInventoryItems.from.find((item) => item._id === moveRequest.item);
if (!originalItemLocation) if (!originalItemLocation) {
{
// Internal item move but item never existed, possible dupe glitch // Internal item move but item never existed, possible dupe glitch
this.appendTraderExploitErrorResponse(output); this.appendTraderExploitErrorResponse(output);
return; return;
@ -122,20 +115,16 @@ export class InventoryController
const originalLocationSlotId = originalItemLocation?.slotId; const originalLocationSlotId = originalItemLocation?.slotId;
const moveResult = this.inventoryHelper.moveItemInternal(pmcData, ownerInventoryItems.from, moveRequest); const moveResult = this.inventoryHelper.moveItemInternal(pmcData, ownerInventoryItems.from, moveRequest);
if (!moveResult.success) if (!moveResult.success) {
{
this.httpResponseUtil.appendErrorToOutput(output, moveResult.errorMessage); this.httpResponseUtil.appendErrorToOutput(output, moveResult.errorMessage);
return; return;
} }
// Item is moving into or out of place of fame dogtag slot // Item is moving into or out of place of fame dogtag slot
if (moveRequest.to.container.startsWith("dogtag") || originalLocationSlotId?.startsWith("dogtag")) if (moveRequest.to.container.startsWith("dogtag") || originalLocationSlotId?.startsWith("dogtag")) {
{
this.hideoutHelper.applyPlaceOfFameDogtagBonus(pmcData); this.hideoutHelper.applyPlaceOfFameDogtagBonus(pmcData);
} }
} } else {
else
{
this.inventoryHelper.moveItemToProfile(ownerInventoryItems.from, ownerInventoryItems.to, moveRequest); this.inventoryHelper.moveItemToProfile(ownerInventoryItems.from, ownerInventoryItems.to, moveRequest);
} }
} }
@ -145,8 +134,7 @@ export class InventoryController
* @param output Item event router response * @param output Item event router response
* @returns Item event router response * @returns Item event router response
*/ */
protected appendTraderExploitErrorResponse(output: IItemEventRouterResponse): void protected appendTraderExploitErrorResponse(output: IItemEventRouterResponse): void {
{
this.httpResponseUtil.appendErrorToOutput( this.httpResponseUtil.appendErrorToOutput(
output, output,
this.localisationService.getText("inventory-edit_trader_item"), this.localisationService.getText("inventory-edit_trader_item"),
@ -164,17 +152,15 @@ export class InventoryController
request: IInventoryRemoveRequestData, request: IInventoryRemoveRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{ if (request.fromOwner?.type === "Mail") {
if (request.fromOwner?.type === "Mail")
{
this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, request, output); this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, request, output);
return; return;
} }
const profileToRemoveItemFrom const profileToRemoveItemFrom =
= !request.fromOwner || request.fromOwner.id === pmcData._id !request.fromOwner || request.fromOwner.id === pmcData._id
? pmcData ? pmcData
: this.profileHelper.getFullProfile(sessionID).characters.scav; : this.profileHelper.getFullProfile(sessionID).characters.scav;
@ -195,22 +181,19 @@ export class InventoryController
request: IInventorySplitRequestData, request: IInventorySplitRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// Changes made to result apply to character inventory // Changes made to result apply to character inventory
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(request, sessionID); const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(request, sessionID);
// Handle cartridge edge-case // Handle cartridge edge-case
if (!request.container.location && request.container.container === "cartridges") if (!request.container.location && request.container.container === "cartridges") {
{
const matchingItems = inventoryItems.to.filter((x) => x.parentId === request.container.id); const matchingItems = inventoryItems.to.filter((x) => x.parentId === request.container.id);
request.container.location = matchingItems.length; // Wrong location for first cartridge request.container.location = matchingItems.length; // Wrong location for first cartridge
} }
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one // The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one
const itemToSplit = inventoryItems.from.find((x) => x._id === request.splitItem); const itemToSplit = inventoryItems.from.find((x) => x._id === request.splitItem);
if (!itemToSplit) if (!itemToSplit) {
{
const errorMessage = `Unable to split stack as source item: ${request.splitItem} cannot be found`; const errorMessage = `Unable to split stack as source item: ${request.splitItem} cannot be found`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -258,15 +241,13 @@ export class InventoryController
body: IInventoryMergeRequestData, body: IInventoryMergeRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// Changes made to result apply to character inventory // Changes made to result apply to character inventory
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID); const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
// Get source item (can be from player or trader or mail) // Get source item (can be from player or trader or mail)
const sourceItem = inventoryItems.from.find((x) => x._id === body.item); const sourceItem = inventoryItems.from.find((x) => x._id === body.item);
if (!sourceItem) if (!sourceItem) {
{
const errorMessage = `Unable to merge stacks as source item: ${body.with} cannot be found`; const errorMessage = `Unable to merge stacks as source item: ${body.with} cannot be found`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -277,8 +258,7 @@ export class InventoryController
// Get item being merged into // Get item being merged into
const destinationItem = inventoryItems.to.find((x) => x._id === body.with); const destinationItem = inventoryItems.to.find((x) => x._id === body.with);
if (!destinationItem) if (!destinationItem) {
{
const errorMessage = `Unable to merge stacks as destination item: ${body.with} cannot be found`; const errorMessage = `Unable to merge stacks as destination item: ${body.with} cannot be found`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -287,25 +267,20 @@ export class InventoryController
return output; return output;
} }
if (!destinationItem.upd?.StackObjectsCount) if (!destinationItem.upd?.StackObjectsCount) {
{
// No stackcount on destination, add one // No stackcount on destination, add one
destinationItem.upd = { StackObjectsCount: 1 }; destinationItem.upd = { StackObjectsCount: 1 };
} }
if (!sourceItem.upd) if (!sourceItem.upd) {
{
sourceItem.upd = { StackObjectsCount: 1 }; sourceItem.upd = { StackObjectsCount: 1 };
} } else if (!sourceItem.upd.StackObjectsCount) {
else if (!sourceItem.upd.StackObjectsCount)
{
// Items pulled out of raid can have no stackcount if the stack should be 1 // Items pulled out of raid can have no stackcount if the stack should be 1
sourceItem.upd.StackObjectsCount = 1; sourceItem.upd.StackObjectsCount = 1;
} }
// Remove FiR status from destination stack when source stack has no FiR but destination does // Remove FiR status from destination stack when source stack has no FiR but destination does
if (!sourceItem.upd.SpawnedInSession && destinationItem.upd.SpawnedInSession) if (!sourceItem.upd.SpawnedInSession && destinationItem.upd.SpawnedInSession) {
{
delete destinationItem.upd.SpawnedInSession; delete destinationItem.upd.SpawnedInSession;
} }
@ -313,8 +288,7 @@ export class InventoryController
output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted output.profileChanges[sessionID].items.del.push({ _id: sourceItem._id }); // Inform client source item being deleted
const indexOfItemToRemove = inventoryItems.from.findIndex((x) => x._id === sourceItem._id); const indexOfItemToRemove = inventoryItems.from.findIndex((x) => x._id === sourceItem._id);
if (indexOfItemToRemove === -1) if (indexOfItemToRemove === -1) {
{
const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`; const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -342,14 +316,12 @@ export class InventoryController
body: IInventoryTransferRequestData, body: IInventoryTransferRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID); const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
const sourceItem = inventoryItems.from.find((item) => item._id === body.item); const sourceItem = inventoryItems.from.find((item) => item._id === body.item);
const destinationItem = inventoryItems.to.find((item) => item._id === body.with); const destinationItem = inventoryItems.to.find((item) => item._id === body.with);
if (!sourceItem) if (!sourceItem) {
{
const errorMessage = `Unable to transfer stack, cannot find source: ${body.item}`; const errorMessage = `Unable to transfer stack, cannot find source: ${body.item}`;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -358,8 +330,7 @@ export class InventoryController
return output; return output;
} }
if (!destinationItem) if (!destinationItem) {
{
const errorMessage = `Unable to transfer stack, cannot find destination: ${body.with} `; const errorMessage = `Unable to transfer stack, cannot find destination: ${body.with} `;
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -368,25 +339,20 @@ export class InventoryController
return output; return output;
} }
if (!sourceItem.upd) if (!sourceItem.upd) {
{
sourceItem.upd = { StackObjectsCount: 1 }; sourceItem.upd = { StackObjectsCount: 1 };
} }
const sourceStackCount = sourceItem.upd.StackObjectsCount; const sourceStackCount = sourceItem.upd.StackObjectsCount;
if (sourceStackCount > body.count) if (sourceStackCount > body.count) {
{
// Source items stack count greater than new desired count // Source items stack count greater than new desired count
sourceItem.upd.StackObjectsCount = sourceStackCount - body.count; sourceItem.upd.StackObjectsCount = sourceStackCount - body.count;
} } else {
else
{
// Moving a full stack onto a smaller stack // Moving a full stack onto a smaller stack
sourceItem.upd.StackObjectsCount = sourceStackCount - 1; sourceItem.upd.StackObjectsCount = sourceStackCount - 1;
} }
if (!destinationItem.upd) if (!destinationItem.upd) {
{
destinationItem.upd = { StackObjectsCount: 1 }; destinationItem.upd = { StackObjectsCount: 1 };
} }
const destinationStackCount = destinationItem.upd.StackObjectsCount; const destinationStackCount = destinationItem.upd.StackObjectsCount;
@ -404,25 +370,31 @@ export class InventoryController
pmcData: IPmcData, pmcData: IPmcData,
request: IInventorySwapRequestData, request: IInventorySwapRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// During post-raid scav transfer, the swap may be in the scav inventory // During post-raid scav transfer, the swap may be in the scav inventory
let playerData = pmcData; let playerData = pmcData;
if (request.fromOwner?.type === "Profile" && request.fromOwner.id !== playerData._id) if (request.fromOwner?.type === "Profile" && request.fromOwner.id !== playerData._id) {
{
playerData = this.profileHelper.getScavProfile(sessionID); playerData = this.profileHelper.getScavProfile(sessionID);
} }
const itemOne = playerData.Inventory.items.find((x) => x._id === request.item); const itemOne = playerData.Inventory.items.find((x) => x._id === request.item);
if (!itemOne) if (!itemOne) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("inventory-unable_to_find_item_to_swap", { item1Id: request.item, item2Id: request.item2 })); this.localisationService.getText("inventory-unable_to_find_item_to_swap", {
item1Id: request.item,
item2Id: request.item2,
}),
);
} }
const itemTwo = playerData.Inventory.items.find((x) => x._id === request.item2); const itemTwo = playerData.Inventory.items.find((x) => x._id === request.item2);
if (!itemTwo) if (!itemTwo) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("inventory-unable_to_find_item_to_swap", { item1Id: request.item2, item2Id: request.item })); this.localisationService.getText("inventory-unable_to_find_item_to_swap", {
item1Id: request.item2,
item2Id: request.item,
}),
);
} }
// to.id is the parentid // to.id is the parentid
@ -432,23 +404,17 @@ export class InventoryController
itemOne.slotId = request.to.container; itemOne.slotId = request.to.container;
// Request object has location data, add it in, otherwise remove existing location from object // Request object has location data, add it in, otherwise remove existing location from object
if (request.to.location) if (request.to.location) {
{
itemOne.location = request.to.location; itemOne.location = request.to.location;
} } else {
else
{
delete itemOne.location; delete itemOne.location;
} }
itemTwo.parentId = request.to2.id; itemTwo.parentId = request.to2.id;
itemTwo.slotId = request.to2.container; itemTwo.slotId = request.to2.container;
if (request.to2.location) if (request.to2.location) {
{
itemTwo.location = request.to2.location; itemTwo.location = request.to2.location;
} } else {
else
{
delete itemTwo.location; delete itemTwo.location;
} }
@ -463,22 +429,21 @@ export class InventoryController
pmcData: IPmcData, pmcData: IPmcData,
request: IInventoryFoldRequestData, request: IInventoryFoldRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// May need to reassign to scav profile // May need to reassign to scav profile
let playerData = pmcData; let playerData = pmcData;
// We may be folding data on scav profile, get that profile instead // We may be folding data on scav profile, get that profile instead
if (request.fromOwner?.type === "Profile" && request.fromOwner.id !== playerData._id) if (request.fromOwner?.type === "Profile" && request.fromOwner.id !== playerData._id) {
{
playerData = this.profileHelper.getScavProfile(sessionID); playerData = this.profileHelper.getScavProfile(sessionID);
} }
const itemToFold = playerData.Inventory.items.find((item) => item?._id === request.item); const itemToFold = playerData.Inventory.items.find((item) => item?._id === request.item);
if (!itemToFold) if (!itemToFold) {
{
// Item not found // Item not found
this.logger.warning(this.localisationService.getText("inventory-unable_to_fold_item_not_found_in_inventory", request.item)); this.logger.warning(
this.localisationService.getText("inventory-unable_to_fold_item_not_found_in_inventory", request.item),
);
return { warnings: [], profileChanges: {} }; return { warnings: [], profileChanges: {} };
} }
@ -502,20 +467,17 @@ export class InventoryController
pmcData: IPmcData, pmcData: IPmcData,
body: IInventoryToggleRequestData, body: IInventoryToggleRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// May need to reassign to scav profile // May need to reassign to scav profile
let playerData = pmcData; let playerData = pmcData;
// Fix for toggling items while on they're in the Scav inventory // Fix for toggling items while on they're in the Scav inventory
if (body.fromOwner?.type === "Profile" && body.fromOwner.id !== playerData._id) if (body.fromOwner?.type === "Profile" && body.fromOwner.id !== playerData._id) {
{
playerData = this.profileHelper.getScavProfile(sessionID); playerData = this.profileHelper.getScavProfile(sessionID);
} }
const itemToToggle = playerData.Inventory.items.find((x) => x._id === body.item); const itemToToggle = playerData.Inventory.items.find((x) => x._id === body.item);
if (itemToToggle) if (itemToToggle) {
{
this.itemHelper.addUpdObjectToItem( this.itemHelper.addUpdObjectToItem(
itemToToggle, itemToToggle,
this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id), this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id),
@ -538,16 +500,13 @@ export class InventoryController
* @param sessionID session id * @param sessionID session id
* @returns client response object * @returns client response object
*/ */
public tagItem(pmcData: IPmcData, body: IInventoryTagRequestData, sessionID: string): IItemEventRouterResponse public tagItem(pmcData: IPmcData, body: IInventoryTagRequestData, sessionID: string): IItemEventRouterResponse {
{
const itemToTag = pmcData.Inventory.items.find((item) => item._id === body.item); const itemToTag = pmcData.Inventory.items.find((item) => item._id === body.item);
if (!itemToTag) if (!itemToTag) {
{
return { warnings: [], profileChanges: {} }; return { warnings: [], profileChanges: {} };
} }
if (!itemToTag.upd) if (!itemToTag.upd) {
{
itemToTag.upd = {}; itemToTag.upd = {};
} }
@ -564,14 +523,11 @@ export class InventoryController
* @param sessionID Session id * @param sessionID Session id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public bindItem(pmcData: IPmcData, bindRequest: IInventoryBindRequestData, sessionID: string): void public bindItem(pmcData: IPmcData, bindRequest: IInventoryBindRequestData, sessionID: string): void {
{
// TODO - replace with single .find() call // TODO - replace with single .find() call
for (const index in pmcData.Inventory.fastPanel) for (const index in pmcData.Inventory.fastPanel) {
{
// Find item with existing item in it and remove existing binding, you cant have same item bound to more than 1 slot // Find item with existing item in it and remove existing binding, you cant have same item bound to more than 1 slot
if (pmcData.Inventory.fastPanel[index] === bindRequest.item) if (pmcData.Inventory.fastPanel[index] === bindRequest.item) {
{
pmcData.Inventory.fastPanel[index] = ""; pmcData.Inventory.fastPanel[index] = "";
break; break;
@ -595,8 +551,7 @@ export class InventoryController
request: IInventoryBindRequestData, request: IInventoryBindRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
// Remove kvp from requested fast panel index // Remove kvp from requested fast panel index
delete pmcData.Inventory.fastPanel[request.index]; delete pmcData.Inventory.fastPanel[request.index];
} }
@ -614,48 +569,37 @@ export class InventoryController
body: IInventoryExamineRequestData, body: IInventoryExamineRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
let itemId = ""; let itemId = "";
if ("fromOwner" in body) if ("fromOwner" in body) {
{ try {
try
{
itemId = this.getExaminedItemTpl(body); itemId = this.getExaminedItemTpl(body);
} } catch {
catch
{
this.logger.error(this.localisationService.getText("inventory-examine_item_does_not_exist", body.item)); this.logger.error(this.localisationService.getText("inventory-examine_item_does_not_exist", body.item));
} }
// get hideout item // get hideout item
if (body.fromOwner.type === "HideoutProduction") if (body.fromOwner.type === "HideoutProduction") {
{
itemId = body.item; itemId = body.item;
} }
} }
if (!itemId) if (!itemId) {
{
// item template // item template
if (body.item in this.databaseService.getItems()) if (body.item in this.databaseService.getItems()) {
{
itemId = body.item; itemId = body.item;
} }
} }
if (!itemId) if (!itemId) {
{
// Player inventory // Player inventory
const target = pmcData.Inventory.items.find((item) => item._id === body.item); const target = pmcData.Inventory.items.find((item) => item._id === body.item);
if (target) if (target) {
{
itemId = target._tpl; itemId = target._tpl;
} }
} }
if (itemId) if (itemId) {
{
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
this.flagItemsAsInspectedAndRewardXp([itemId], fullProfile); this.flagItemsAsInspectedAndRewardXp([itemId], fullProfile);
} }
@ -668,14 +612,13 @@ export class InventoryController
* @param itemTpls Inspected item tpls * @param itemTpls Inspected item tpls
* @param fullProfile Profile to add xp to * @param fullProfile Profile to add xp to
*/ */
protected flagItemsAsInspectedAndRewardXp(itemTpls: string[], fullProfile: ISptProfile): void protected flagItemsAsInspectedAndRewardXp(itemTpls: string[], fullProfile: ISptProfile): void {
{ for (const itemTpl of itemTpls) {
for (const itemTpl of itemTpls)
{
const item = this.itemHelper.getItem(itemTpl); const item = this.itemHelper.getItem(itemTpl);
if (!item[0]) if (!item[0]) {
{ this.logger.warning(
this.logger.warning(this.localisationService.getText("inventory-unable_to_inspect_item_not_in_db", itemTpl)); this.localisationService.getText("inventory-unable_to_inspect_item_not_in_db", itemTpl),
);
return; return;
} }
@ -700,47 +643,40 @@ export class InventoryController
* @param request Response request * @param request Response request
* @returns tplId * @returns tplId
*/ */
protected getExaminedItemTpl(request: IInventoryExamineRequestData): string protected getExaminedItemTpl(request: IInventoryExamineRequestData): string {
{ if (this.presetHelper.isPreset(request.item)) {
if (this.presetHelper.isPreset(request.item))
{
return this.presetHelper.getBaseItemTpl(request.item); return this.presetHelper.getBaseItemTpl(request.item);
} }
if (request.fromOwner.id === Traders.FENCE) if (request.fromOwner.id === Traders.FENCE) {
{
// Get tpl from fence assorts // Get tpl from fence assorts
return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === request.item)?._tpl; return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === request.item)?._tpl;
} }
if (request.fromOwner.type === "Trader") if (request.fromOwner.type === "Trader") {
{
// Not fence // Not fence
// get tpl from trader assort // get tpl from trader assort
return this.databaseService.getTrader(request.fromOwner.id).assort.items return this.databaseService
.find((item) => item._id === request.item)?._tpl; .getTrader(request.fromOwner.id)
.assort.items.find((item) => item._id === request.item)?._tpl;
} }
if (request.fromOwner.type === "RagFair") if (request.fromOwner.type === "RagFair") {
{
// Try to get tplid from items.json first // Try to get tplid from items.json first
const item = this.itemHelper.getItem(request.item); const item = this.itemHelper.getItem(request.item);
if (item[0]) if (item[0]) {
{
return item[1]._id; return item[1]._id;
} }
// Try alternate way of getting offer if first approach fails // Try alternate way of getting offer if first approach fails
let offer = this.ragfairOfferService.getOfferByOfferId(request.item); let offer = this.ragfairOfferService.getOfferByOfferId(request.item);
if (!offer) if (!offer) {
{
offer = this.ragfairOfferService.getOfferByOfferId(request.fromOwner.id); offer = this.ragfairOfferService.getOfferByOfferId(request.fromOwner.id);
} }
// Try find examine item inside offer items array // Try find examine item inside offer items array
const matchingItem = offer.items.find((offerItem) => offerItem._id === request.item); const matchingItem = offer.items.find((offerItem) => offerItem._id === request.item);
if (matchingItem) if (matchingItem) {
{
return matchingItem._tpl; return matchingItem._tpl;
} }
@ -753,10 +689,8 @@ export class InventoryController
pmcData: IPmcData, pmcData: IPmcData,
body: IInventoryReadEncyclopediaRequestData, body: IInventoryReadEncyclopediaRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{ for (const id of body.ids) {
for (const id of body.ids)
{
pmcData.Encyclopedia[id] = true; pmcData.Encyclopedia[id] = true;
} }
@ -770,26 +704,22 @@ export class InventoryController
* @param request sort request * @param request sort request
* @param sessionID Session id * @param sessionID Session id
*/ */
public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): void public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): void {
{ for (const change of request.changedItems) {
for (const change of request.changedItems)
{
const inventoryItem = pmcData.Inventory.items.find((item) => item._id === change._id); const inventoryItem = pmcData.Inventory.items.find((item) => item._id === change._id);
if (!inventoryItem) if (!inventoryItem) {
{ this.logger.error(
this.logger.error(this.localisationService.getText("inventory-unable_to_sort_inventory_restart_game", change._id)); this.localisationService.getText("inventory-unable_to_sort_inventory_restart_game", change._id),
);
continue; continue;
} }
inventoryItem.parentId = change.parentId; inventoryItem.parentId = change.parentId;
inventoryItem.slotId = change.slotId; inventoryItem.slotId = change.slotId;
if (change.location) if (change.location) {
{
inventoryItem.location = change.location; inventoryItem.location = change.location;
} } else {
else
{
delete inventoryItem.location; delete inventoryItem.location;
} }
} }
@ -808,8 +738,7 @@ export class InventoryController
request: IInventoryCreateMarkerRequestData, request: IInventoryCreateMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const adjustedMapItem = this.mapMarkerService.createMarkerOnMap(pmcData, request); const adjustedMapItem = this.mapMarkerService.createMarkerOnMap(pmcData, request);
// Sync with client // Sync with client
@ -828,8 +757,7 @@ export class InventoryController
request: IInventoryDeleteMarkerRequestData, request: IInventoryDeleteMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const mapItem = this.mapMarkerService.deleteMarkerFromMap(pmcData, request); const mapItem = this.mapMarkerService.deleteMarkerFromMap(pmcData, request);
// sync with client // sync with client
@ -848,8 +776,7 @@ export class InventoryController
request: IInventoryEditMarkerRequestData, request: IInventoryEditMarkerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const mapItem = this.mapMarkerService.editMarkerOnMap(pmcData, request); const mapItem = this.mapMarkerService.editMarkerOnMap(pmcData, request);
// sync with client // sync with client
@ -869,8 +796,7 @@ export class InventoryController
body: IOpenRandomLootContainerRequestData, body: IOpenRandomLootContainerRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
/** Container player opened in their inventory */ /** Container player opened in their inventory */
const openedItem = pmcData.Inventory.items.find((item) => item._id === body.item); const openedItem = pmcData.Inventory.items.find((item) => item._id === body.item);
const containerDetailsDb = this.itemHelper.getItem(openedItem._tpl); const containerDetailsDb = this.itemHelper.getItem(openedItem._tpl);
@ -878,37 +804,32 @@ export class InventoryController
let foundInRaid = openedItem.upd?.SpawnedInSession; let foundInRaid = openedItem.upd?.SpawnedInSession;
const rewards: Item[][] = []; const rewards: Item[][] = [];
const unlockedWeaponCrates = ["665829424de4820934746ce6", "665732e7ac60f009f270d1ef", "665888282c4a1b73af576b77"];// Temp fix for unlocked weapon crate hideout craft const unlockedWeaponCrates = [
if (isSealedWeaponBox || unlockedWeaponCrates.includes(containerDetailsDb[1]._id)) "665829424de4820934746ce6",
{ "665732e7ac60f009f270d1ef",
"665888282c4a1b73af576b77",
]; // Temp fix for unlocked weapon crate hideout craft
if (isSealedWeaponBox || unlockedWeaponCrates.includes(containerDetailsDb[1]._id)) {
const containerSettings = this.inventoryHelper.getInventoryConfig().sealedAirdropContainer; const containerSettings = this.inventoryHelper.getInventoryConfig().sealedAirdropContainer;
rewards.push(...this.lootGenerator.getSealedWeaponCaseLoot(containerSettings)); rewards.push(...this.lootGenerator.getSealedWeaponCaseLoot(containerSettings));
if (containerSettings.foundInRaid) if (containerSettings.foundInRaid) {
{
foundInRaid = containerSettings.foundInRaid; foundInRaid = containerSettings.foundInRaid;
} }
} } else {
else
{
const rewardContainerDetails = this.inventoryHelper.getRandomLootContainerRewardDetails(openedItem._tpl); const rewardContainerDetails = this.inventoryHelper.getRandomLootContainerRewardDetails(openedItem._tpl);
if (!rewardContainerDetails || !rewardContainerDetails.rewardCount) if (!rewardContainerDetails || !rewardContainerDetails.rewardCount) {
{
this.logger.error(`Unable to add loot to container: ${openedItem._tpl}, no rewards found`); this.logger.error(`Unable to add loot to container: ${openedItem._tpl}, no rewards found`);
} } else {
else
{
rewards.push(...this.lootGenerator.getRandomLootContainerLoot(rewardContainerDetails)); rewards.push(...this.lootGenerator.getRandomLootContainerLoot(rewardContainerDetails));
if (rewardContainerDetails.foundInRaid) if (rewardContainerDetails.foundInRaid) {
{
foundInRaid = rewardContainerDetails.foundInRaid; foundInRaid = rewardContainerDetails.foundInRaid;
} }
} }
} }
if (rewards.length > 0) if (rewards.length > 0) {
{
const addItemsRequest: IAddItemsDirectRequest = { const addItemsRequest: IAddItemsDirectRequest = {
itemsWithModsToAdd: rewards, itemsWithModsToAdd: rewards,
foundInRaid: foundInRaid, foundInRaid: foundInRaid,
@ -916,8 +837,7 @@ export class InventoryController
useSortingTable: true, useSortingTable: true,
}; };
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return; return;
} }
} }
@ -926,19 +846,16 @@ export class InventoryController
this.inventoryHelper.removeItem(pmcData, body.item, sessionID, output); this.inventoryHelper.removeItem(pmcData, body.item, sessionID, output);
} }
public redeemProfileReward(pmcData: IPmcData, request: IRedeemProfileRequestData, sessionId: string): void public redeemProfileReward(pmcData: IPmcData, request: IRedeemProfileRequestData, sessionId: string): void {
{
const fullProfile = this.profileHelper.getFullProfile(sessionId); const fullProfile = this.profileHelper.getFullProfile(sessionId);
for (const event of request.events) for (const event of request.events) {
{
// Hard coded to `SYSTEM` for now // Hard coded to `SYSTEM` for now
// TODO: make this dynamic // TODO: make this dynamic
const dialog = fullProfile.dialogues["59e7125688a45068a6249071"]; const dialog = fullProfile.dialogues["59e7125688a45068a6249071"];
const mail = dialog.messages.find((message) => message._id === event.MessageId); const mail = dialog.messages.find((message) => message._id === event.MessageId);
const mailEvent = mail.profileChangeEvents.find((changeEvent) => changeEvent._id === event.EventId); const mailEvent = mail.profileChangeEvents.find((changeEvent) => changeEvent._id === event.EventId);
switch (mailEvent.Type) switch (mailEvent.Type) {
{
case "TraderSalesSum": case "TraderSalesSum":
pmcData.TradersInfo[mailEvent.entity].salesSum = mailEvent.value; pmcData.TradersInfo[mailEvent.entity].salesSum = mailEvent.value;
this.traderHelper.lvlUp(mailEvent.entity, pmcData); this.traderHelper.lvlUp(mailEvent.entity, pmcData);
@ -955,11 +872,9 @@ export class InventoryController
this.traderHelper.validateTraderStandingsAndPlayerLevelForProfile(sessionId); this.traderHelper.validateTraderStandingsAndPlayerLevelForProfile(sessionId);
this.logger.success(`Set profile xp to: ${mailEvent.value}`); this.logger.success(`Set profile xp to: ${mailEvent.value}`);
break; break;
case "SkillPoints": case "SkillPoints": {
{
const profileSkill = pmcData.Skills.Common.find((x) => x.Id === mailEvent.entity); const profileSkill = pmcData.Skills.Common.find((x) => x.Id === mailEvent.entity);
if (!profileSkill) if (!profileSkill) {
{
this.logger.warning(`Unable to find skill with name: ${mailEvent.entity}`); this.logger.warning(`Unable to find skill with name: ${mailEvent.entity}`);
continue; continue;
} }
@ -967,8 +882,7 @@ export class InventoryController
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`); this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
break; break;
} }
case "ExamineAllItems": case "ExamineAllItems": {
{
const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node"); const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node");
this.flagItemsAsInspectedAndRewardXp( this.flagItemsAsInspectedAndRewardXp(
itemsToInspect.map((x) => x._id), itemsToInspect.map((x) => x._id),
@ -984,8 +898,7 @@ export class InventoryController
break; break;
case "AssortmentUnlockRule": case "AssortmentUnlockRule":
if (!fullProfile.spt.blacklistedItemTpls) if (!fullProfile.spt.blacklistedItemTpls) {
{
fullProfile.spt.blacklistedItemTpls = []; fullProfile.spt.blacklistedItemTpls = [];
} }
fullProfile.spt.blacklistedItemTpls.push(mailEvent.entity); fullProfile.spt.blacklistedItemTpls.push(mailEvent.entity);
@ -996,11 +909,9 @@ export class InventoryController
const areaName = mailEvent.entity; const areaName = mailEvent.entity;
const newValue = mailEvent.value; const newValue = mailEvent.value;
const hideoutAreaCode = HideoutAreas[areaName.toUpperCase()]; const hideoutAreaCode = HideoutAreas[areaName.toUpperCase()];
if (hideoutAreaCode !== undefined) if (hideoutAreaCode !== undefined) {
{
const desiredArea = pmcData.Hideout.Areas.find((area) => area.type === hideoutAreaCode); const desiredArea = pmcData.Hideout.Areas.find((area) => area.type === hideoutAreaCode);
if (desiredArea) if (desiredArea) {
{
desiredArea.level = newValue; desiredArea.level = newValue;
} }
} }
@ -1014,23 +925,17 @@ export class InventoryController
} }
} }
public setFavoriteItem(pmcData: IPmcData, request: ISetFavoriteItems, sessionId: string): void public setFavoriteItem(pmcData: IPmcData, request: ISetFavoriteItems, sessionId: string): void {
{ if (!pmcData.Inventory.favoriteItems) {
if (!pmcData.Inventory.favoriteItems)
{
pmcData.Inventory.favoriteItems = []; pmcData.Inventory.favoriteItems = [];
} }
for (const itemId of request.items) for (const itemId of request.items) {
{
// If id already exists in array, we're removing it // If id already exists in array, we're removing it
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId); const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId);
if (indexOfItemAlreadyFavorited > -1) if (indexOfItemAlreadyFavorited > -1) {
{
pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1); pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1);
} } else {
else
{
pmcData.Inventory.favoriteItems.push(itemId); pmcData.Inventory.favoriteItems.push(itemId);
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
@ -18,10 +17,10 @@ import { LocalisationService } from "@spt/services/LocalisationService";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class LauncherController export class LauncherController {
{
protected coreConfig: ICoreConfig; protected coreConfig: ICoreConfig;
constructor( constructor(
@ -36,16 +35,15 @@ export class LauncherController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("PreSptModLoader") protected preSptModLoader: PreSptModLoader, @inject("PreSptModLoader") protected preSptModLoader: PreSptModLoader,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
} }
public connect(): IConnectResponse public connect(): IConnectResponse {
{
// Get all possible profile types + filter out any that are blacklisted // Get all possible profile types + filter out any that are blacklisted
const profileKeys = Object.keys(this.databaseService.getProfiles()) const profileKeys = Object.keys(this.databaseService.getProfiles()).filter(
.filter((key) => !this.coreConfig.features.createNewProfileTypesBlacklist.includes(key)); (key) => !this.coreConfig.features.createNewProfileTypesBlacklist.includes(key),
);
return { return {
backendUrl: this.httpServerHelper.getBackendUrl(), backendUrl: this.httpServerHelper.getBackendUrl(),
@ -59,15 +57,12 @@ export class LauncherController
* Get descriptive text for each of the profile edtions a player can choose, keyed by profile.json profile type e.g. "Edge Of Darkness" * Get descriptive text for each of the profile edtions a player can choose, keyed by profile.json profile type e.g. "Edge Of Darkness"
* @returns Dictionary of profile types with related descriptive text * @returns Dictionary of profile types with related descriptive text
*/ */
protected getProfileDescriptions(): Record<string, string> protected getProfileDescriptions(): Record<string, string> {
{
const result = {}; const result = {};
const dbProfiles = this.databaseService.getProfiles(); const dbProfiles = this.databaseService.getProfiles();
for (const profileKey in dbProfiles) for (const profileKey in dbProfiles) {
{
const localeKey = dbProfiles[profileKey]?.descriptionLocaleKey; const localeKey = dbProfiles[profileKey]?.descriptionLocaleKey;
if (!localeKey) if (!localeKey) {
{
this.logger.warning(this.localisationService.getText("launcher-missing_property", profileKey)); this.logger.warning(this.localisationService.getText("launcher-missing_property", profileKey));
continue; continue;
} }
@ -78,18 +73,14 @@ export class LauncherController
return result; return result;
} }
public find(sessionId: string): Info public find(sessionId: string): Info {
{
return this.saveServer.getProfiles()[sessionId]?.info; return this.saveServer.getProfiles()[sessionId]?.info;
} }
public login(info: ILoginRequestData): string public login(info: ILoginRequestData): string {
{ for (const sessionID in this.saveServer.getProfiles()) {
for (const sessionID in this.saveServer.getProfiles())
{
const account = this.saveServer.getProfile(sessionID).info; const account = this.saveServer.getProfile(sessionID).info;
if (info.username === account.username) if (info.username === account.username) {
{
return sessionID; return sessionID;
} }
} }
@ -97,12 +88,9 @@ export class LauncherController
return ""; return "";
} }
public register(info: IRegisterData): string public register(info: IRegisterData): string {
{ for (const sessionID in this.saveServer.getProfiles()) {
for (const sessionID in this.saveServer.getProfiles()) if (info.username === this.saveServer.getProfile(sessionID).info.username) {
{
if (info.username === this.saveServer.getProfile(sessionID).info.username)
{
return ""; return "";
} }
} }
@ -110,8 +98,7 @@ export class LauncherController
return this.createAccount(info); return this.createAccount(info);
} }
protected createAccount(info: IRegisterData): string protected createAccount(info: IRegisterData): string {
{
const profileId = this.generateProfileId(); const profileId = this.generateProfileId();
const scavId = this.generateProfileId(); const scavId = this.generateProfileId();
const newProfileDetails: Info = { const newProfileDetails: Info = {
@ -131,39 +118,33 @@ export class LauncherController
return profileId; return profileId;
} }
protected generateProfileId(): string protected generateProfileId(): string {
{
const timestamp = this.timeUtil.getTimestamp(); const timestamp = this.timeUtil.getTimestamp();
return this.formatID(timestamp, timestamp * this.randomUtil.getInt(1, 1000000)); return this.formatID(timestamp, timestamp * this.randomUtil.getInt(1, 1000000));
} }
protected formatID(timeStamp: number, counter: number): string protected formatID(timeStamp: number, counter: number): string {
{
const timeStampStr = timeStamp.toString(16).padStart(8, "0"); const timeStampStr = timeStamp.toString(16).padStart(8, "0");
const counterStr = counter.toString(16).padStart(16, "0"); const counterStr = counter.toString(16).padStart(16, "0");
return timeStampStr.toLowerCase() + counterStr.toLowerCase(); return timeStampStr.toLowerCase() + counterStr.toLowerCase();
} }
public changeUsername(info: IChangeRequestData): string public changeUsername(info: IChangeRequestData): string {
{
const sessionID = this.login(info); const sessionID = this.login(info);
if (sessionID) if (sessionID) {
{
this.saveServer.getProfile(sessionID).info.username = info.change; this.saveServer.getProfile(sessionID).info.username = info.change;
} }
return sessionID; return sessionID;
} }
public changePassword(info: IChangeRequestData): string public changePassword(info: IChangeRequestData): string {
{
const sessionID = this.login(info); const sessionID = this.login(info);
if (sessionID) if (sessionID) {
{
this.saveServer.getProfile(sessionID).info.password = info.change; this.saveServer.getProfile(sessionID).info.password = info.change;
} }
@ -175,17 +156,14 @@ export class LauncherController
* @param info IRegisterData * @param info IRegisterData
* @returns Session id * @returns Session id
*/ */
public wipe(info: IRegisterData): string public wipe(info: IRegisterData): string {
{ if (!this.coreConfig.allowProfileWipe) {
if (!this.coreConfig.allowProfileWipe)
{
return; return;
} }
const sessionID = this.login(info); const sessionID = this.login(info);
if (sessionID) if (sessionID) {
{
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
profile.info.edition = info.edition; profile.info.edition = info.edition;
profile.info.wipe = true; profile.info.wipe = true;
@ -194,8 +172,7 @@ export class LauncherController
return sessionID; return sessionID;
} }
public getCompatibleTarkovVersion(): string public getCompatibleTarkovVersion(): string {
{
return this.coreConfig.compatibleTarkovVersion; return this.coreConfig.compatibleTarkovVersion;
} }
@ -203,8 +180,7 @@ export class LauncherController
* Get the mods the server has currently loaded * Get the mods the server has currently loaded
* @returns Dictionary of mod name and mod details * @returns Dictionary of mod name and mod details
*/ */
public getLoadedServerMods(): Record<string, IPackageJsonData> public getLoadedServerMods(): Record<string, IPackageJsonData> {
{
return this.preSptModLoader.getImportedModDetails(); return this.preSptModLoader.getImportedModDetails();
} }
@ -213,12 +189,10 @@ export class LauncherController
* @param sessionId Player id * @param sessionId Player id
* @returns Array of mod details * @returns Array of mod details
*/ */
public getServerModsProfileUsed(sessionId: string): ModDetails[] public getServerModsProfileUsed(sessionId: string): ModDetails[] {
{
const profile = this.profileHelper.getFullProfile(sessionId); const profile = this.profileHelper.getFullProfile(sessionId);
if (profile?.spt?.mods) if (profile?.spt?.mods) {
{
return this.preSptModLoader.getProfileModsGroupedByModName(profile?.spt?.mods); return this.preSptModLoader.getProfileModsGroupedByModName(profile?.spt?.mods);
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase"; import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse"; import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
@ -9,10 +8,10 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
import { AirdropService } from "@spt/services/AirdropService"; import { AirdropService } from "@spt/services/AirdropService";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { ICloner } from "@spt/utils/cloners/ICloner"; import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class LocationController export class LocationController {
{
protected locationConfig: ILocationConfig; protected locationConfig: ILocationConfig;
constructor( constructor(
@ -21,8 +20,7 @@ export class LocationController
@inject("AirdropService") protected airdropService: AirdropService, @inject("AirdropService") protected airdropService: AirdropService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION); this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
} }
@ -32,15 +30,12 @@ export class LocationController
* @param sessionId Players Id * @param sessionId Players Id
* @returns ILocationsGenerateAllResponse * @returns ILocationsGenerateAllResponse
*/ */
public generateAll(sessionId: string): ILocationsGenerateAllResponse public generateAll(sessionId: string): ILocationsGenerateAllResponse {
{
const locationsFromDb = this.databaseService.getLocations(); const locationsFromDb = this.databaseService.getLocations();
const locations: ILocations = {}; const locations: ILocations = {};
for (const mapName in locationsFromDb) for (const mapName in locationsFromDb) {
{
const mapBase = locationsFromDb[mapName]?.base; const mapBase = locationsFromDb[mapName]?.base;
if (!mapBase) if (!mapBase) {
{
this.logger.debug(`Map: ${mapName} has no base json file, skipping generation`); this.logger.debug(`Map: ${mapName} has no base json file, skipping generation`);
continue; continue;
} }
@ -55,8 +50,7 @@ export class LocationController
} }
/** Handle client/airdrop/loot */ /** Handle client/airdrop/loot */
public getAirdropLoot(): IGetAirdropLootResponse public getAirdropLoot(): IGetAirdropLootResponse {
{
return this.airdropService.generateAirdropLoot(); return this.airdropService.generateAirdropLoot();
} }
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt/context/ApplicationContext"; import { ApplicationContext } from "@spt/context/ApplicationContext";
import { ContextVariableType } from "@spt/context/ContextVariableType"; import { ContextVariableType } from "@spt/context/ContextVariableType";
import { IEndLocalRaidRequestData } from "@spt/models/eft/match/IEndLocalRaidRequestData"; import { IEndLocalRaidRequestData } from "@spt/models/eft/match/IEndLocalRaidRequestData";
@ -19,10 +18,10 @@ import { LocationLifecycleService } from "@spt/services/LocationLifecycleService
import { MatchLocationService } from "@spt/services/MatchLocationService"; import { MatchLocationService } from "@spt/services/MatchLocationService";
import { ProfileSnapshotService } from "@spt/services/ProfileSnapshotService"; import { ProfileSnapshotService } from "@spt/services/ProfileSnapshotService";
import { ICloner } from "@spt/utils/cloners/ICloner"; import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class MatchController export class MatchController {
{
protected matchConfig: IMatchConfig; protected matchConfig: IMatchConfig;
protected pmcConfig: IPmcConfig; protected pmcConfig: IPmcConfig;
@ -35,26 +34,22 @@ export class MatchController
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("LocationLifecycleService") protected locationLifecycleService: LocationLifecycleService, @inject("LocationLifecycleService") protected locationLifecycleService: LocationLifecycleService,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH); this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC); this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
} }
public getEnabled(): boolean public getEnabled(): boolean {
{
return this.matchConfig.enabled; return this.matchConfig.enabled;
} }
/** Handle client/match/group/delete */ /** Handle client/match/group/delete */
public deleteGroup(info: any): void public deleteGroup(info: any): void {
{
this.matchLocationService.deleteGroup(info); this.matchLocationService.deleteGroup(info);
} }
/** Handle match/group/start_game */ /** Handle match/group/start_game */
public joinMatch(info: IMatchGroupStartGameRequest, sessionId: string): IProfileStatusResponse public joinMatch(info: IMatchGroupStartGameRequest, sessionId: string): IProfileStatusResponse {
{
const output: IProfileStatusResponse = { maxPveCountExceeded: false, profiles: [] }; const output: IProfileStatusResponse = { maxPveCountExceeded: false, profiles: [] };
// get list of players joining into the match // get list of players joining into the match
@ -77,8 +72,7 @@ export class MatchController
} }
/** Handle client/match/group/status */ /** Handle client/match/group/status */
public getGroupStatus(info: IMatchGroupStatusRequest): IMatchGroupStatusResponse public getGroupStatus(info: IMatchGroupStatusRequest): IMatchGroupStatusResponse {
{
return { players: [], maxPveCountExceeded: false }; return { players: [], maxPveCountExceeded: false };
} }
@ -87,16 +81,14 @@ export class MatchController
* @param request Raid config request * @param request Raid config request
* @param sessionID Session id * @param sessionID Session id
*/ */
public configureOfflineRaid(request: IGetRaidConfigurationRequestData, sessionID: string): void public configureOfflineRaid(request: IGetRaidConfigurationRequestData, sessionID: string): void {
{
// Store request data for access during bot generation // Store request data for access during bot generation
this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request); this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request);
// TODO: add code to strip PMC of equipment now they've started the raid // TODO: add code to strip PMC of equipment now they've started the raid
// Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled // Set pmcs to difficulty set in pre-raid screen if override in bot config isnt enabled
if (!this.pmcConfig.useDifficultyOverride) if (!this.pmcConfig.useDifficultyOverride) {
{
this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty( this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(
request.wavesSettings.botDifficulty, request.wavesSettings.botDifficulty,
); );
@ -112,11 +104,9 @@ export class MatchController
* @param botDifficulty dropdown difficulty value * @param botDifficulty dropdown difficulty value
* @returns bot difficulty * @returns bot difficulty
*/ */
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string {
{
// Edge case medium - must be altered // Edge case medium - must be altered
if (botDifficulty.toLowerCase() === "medium") if (botDifficulty.toLowerCase() === "medium") {
{
return "normal"; return "normal";
} }
@ -124,14 +114,12 @@ export class MatchController
} }
/** Handle client/match/local/start */ /** Handle client/match/local/start */
public startLocalRaid(sessionId: string, request: IStartLocalRaidRequestData): IStartLocalRaidResponseData public startLocalRaid(sessionId: string, request: IStartLocalRaidRequestData): IStartLocalRaidResponseData {
{
return this.locationLifecycleService.startLocalRaid(sessionId, request); return this.locationLifecycleService.startLocalRaid(sessionId, request);
} }
/** Handle client/match/local/end */ /** Handle client/match/local/end */
public endLocalRaid(sessionId: string, request: IEndLocalRaidRequestData): void public endLocalRaid(sessionId: string, request: IEndLocalRaidRequestData): void {
{
return this.locationLifecycleService.endLocalRaid(sessionId, request); return this.locationLifecycleService.endLocalRaid(sessionId, request);
} }
} }

View File

@ -1,26 +1,22 @@
import { inject, injectable } from "tsyringe";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { Note } from "@spt/models/eft/common/tables/IBotBase"; import { Note } from "@spt/models/eft/common/tables/IBotBase";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { INoteActionData } from "@spt/models/eft/notes/INoteActionData"; import { INoteActionData } from "@spt/models/eft/notes/INoteActionData";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class NoteController export class NoteController {
{ constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder) {}
constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder)
{}
public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public addNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
const newNote: Note = { Time: body.note.Time, Text: body.note.Text }; const newNote: Note = { Time: body.note.Time, Text: body.note.Text };
pmcData.Notes.Notes.push(newNote); pmcData.Notes.Notes.push(newNote);
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
public editNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public editNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
const noteToEdit: Note = pmcData.Notes.Notes[body.index]; const noteToEdit: Note = pmcData.Notes.Notes[body.index];
noteToEdit.Time = body.note.Time; noteToEdit.Time = body.note.Time;
noteToEdit.Text = body.note.Text; noteToEdit.Text = body.note.Text;
@ -28,8 +24,7 @@ export class NoteController
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
public deleteNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse public deleteNote(pmcData: IPmcData, body: INoteActionData, sessionID: string): IItemEventRouterResponse {
{
pmcData.Notes.Notes.splice(body.index, 1); pmcData.Notes.Notes.splice(body.index, 1);
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }

View File

@ -1,12 +1,11 @@
import { inject, injectable } from "tsyringe";
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
import { NotifierHelper } from "@spt/helpers/NotifierHelper"; import { NotifierHelper } from "@spt/helpers/NotifierHelper";
import { INotifierChannel } from "@spt/models/eft/notifier/INotifier"; import { INotifierChannel } from "@spt/models/eft/notifier/INotifier";
import { NotificationService } from "@spt/services/NotificationService"; import { NotificationService } from "@spt/services/NotificationService";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class NotifierController export class NotifierController {
{
protected pollInterval = 300; protected pollInterval = 300;
protected timeout = 15000; protected timeout = 15000;
@ -14,8 +13,7 @@ export class NotifierController
@inject("NotifierHelper") protected notifierHelper: NotifierHelper, @inject("NotifierHelper") protected notifierHelper: NotifierHelper,
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper, @inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("NotificationService") protected notificationService: NotificationService, @inject("NotificationService") protected notificationService: NotificationService,
) ) {}
{}
/** /**
* Resolve an array of session notifications. * Resolve an array of session notifications.
@ -24,10 +22,8 @@ export class NotifierController
* one or more appear or when a timeout expires. * one or more appear or when a timeout expires.
* If no notifications are available after the timeout, use a default message. * If no notifications are available after the timeout, use a default message.
*/ */
public async notifyAsync(sessionID: string): Promise<unknown> public async notifyAsync(sessionID: string): Promise<unknown> {
{ return new Promise((resolve) => {
return new Promise((resolve) =>
{
// keep track of our timeout // keep track of our timeout
let counter = 0; let counter = 0;
@ -35,17 +31,14 @@ export class NotifierController
* Check for notifications, resolve if any, otherwise poll * Check for notifications, resolve if any, otherwise poll
* intermittently for a period of time. * intermittently for a period of time.
*/ */
const checkNotifications = () => const checkNotifications = () => {
{
/** /**
* If there are no pending messages we should either check again later * If there are no pending messages we should either check again later
* or timeout now with a default response. * or timeout now with a default response.
*/ */
if (!this.notificationService.has(sessionID)) if (!this.notificationService.has(sessionID)) {
{
// have we exceeded timeout? if so reply with default ping message // have we exceeded timeout? if so reply with default ping message
if (counter > this.timeout) if (counter > this.timeout) {
{
return resolve([this.notifierHelper.getDefaultNotification()]); return resolve([this.notifierHelper.getDefaultNotification()]);
} }
@ -71,14 +64,12 @@ export class NotifierController
}); });
} }
public getServer(sessionID: string): string public getServer(sessionID: string): string {
{
return `${this.httpServerHelper.getBackendUrl()}/notifierServer/get/${sessionID}`; return `${this.httpServerHelper.getBackendUrl()}/notifierServer/get/${sessionID}`;
} }
/** Handle client/notifier/channel/create */ /** Handle client/notifier/channel/create */
public getChannel(sessionID: string): INotifierChannel public getChannel(sessionID: string): INotifierChannel {
{
return { return {
server: this.httpServerHelper.buildUrl(), server: this.httpServerHelper.buildUrl(),
channel_id: sessionID, channel_id: sessionID,

View File

@ -1,28 +1,23 @@
import { inject, injectable } from "tsyringe";
import { PresetHelper } from "@spt/helpers/PresetHelper"; import { PresetHelper } from "@spt/helpers/PresetHelper";
import { IPreset } from "@spt/models/eft/common/IGlobals"; import { IPreset } from "@spt/models/eft/common/IGlobals";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class PresetController export class PresetController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("DatabaseService") protected databaseService: DatabaseService, @inject("DatabaseService") protected databaseService: DatabaseService,
) ) {}
{}
public initialize(): void public initialize(): void {
{
const presets: [string, IPreset][] = Object.entries(this.databaseService.getGlobals().ItemPresets); const presets: [string, IPreset][] = Object.entries(this.databaseService.getGlobals().ItemPresets);
const reverse: Record<string, string[]> = {}; const reverse: Record<string, string[]> = {};
for (const [id, preset] of presets) for (const [id, preset] of presets) {
{ if (id !== preset._id) {
if (id !== preset._id)
{
this.logger.error( this.logger.error(
`Preset for template tpl: '${preset._items[0]._tpl} ${preset._name}' has invalid key: (${id} != ${preset._id}). Skipping`, `Preset for template tpl: '${preset._items[0]._tpl} ${preset._name}' has invalid key: (${id} != ${preset._id}). Skipping`,
); );
@ -32,8 +27,7 @@ export class PresetController
const tpl = preset._items[0]._tpl; const tpl = preset._items[0]._tpl;
if (!(tpl in reverse)) if (!(tpl in reverse)) {
{
reverse[tpl] = []; reverse[tpl] = [];
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator"; import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { DialogueHelper } from "@spt/helpers/DialogueHelper"; import { DialogueHelper } from "@spt/helpers/DialogueHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
@ -31,13 +30,13 @@ import { LocalisationService } from "@spt/services/LocalisationService";
import { MailSendService } from "@spt/services/MailSendService"; import { MailSendService } from "@spt/services/MailSendService";
import { ProfileFixerService } from "@spt/services/ProfileFixerService"; import { ProfileFixerService } from "@spt/services/ProfileFixerService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService"; import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HashUtil } from "@spt/utils/HashUtil"; import { HashUtil } from "@spt/utils/HashUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class ProfileController export class ProfileController {
{
constructor( constructor(
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@ -56,14 +55,12 @@ export class ProfileController
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper, @inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
@inject("QuestHelper") protected questHelper: QuestHelper, @inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) ) {}
{}
/** /**
* Handle /launcher/profiles * Handle /launcher/profiles
*/ */
public getMiniProfiles(): IMiniProfile[] public getMiniProfiles(): IMiniProfile[] {
{
const allProfiles = Object.keys(this.saveServer.getProfiles()); const allProfiles = Object.keys(this.saveServer.getProfiles());
return allProfiles.map((sessionId) => this.getMiniProfile(sessionId)); return allProfiles.map((sessionId) => this.getMiniProfile(sessionId));
@ -72,11 +69,9 @@ export class ProfileController
/** /**
* Handle launcher/profile/info * Handle launcher/profile/info
*/ */
public getMiniProfile(sessionID: string): IMiniProfile public getMiniProfile(sessionID: string): IMiniProfile {
{
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
if (!profile || !profile.characters) if (!profile || !profile.characters) {
{
throw new Error(`Unable to find character data for id: ${sessionID}. Profile may be corrupt`); throw new Error(`Unable to find character data for id: ${sessionID}. Profile may be corrupt`);
} }
@ -84,8 +79,7 @@ export class ProfileController
const maxlvl = this.profileHelper.getMaxLevel(); const maxlvl = this.profileHelper.getMaxLevel();
// Player hasn't completed profile creation process, send defaults // Player hasn't completed profile creation process, send defaults
if (!pmc?.Info?.Level) if (!pmc?.Info?.Level) {
{
return { return {
username: profile.info?.username ?? "", username: profile.info?.username ?? "",
nickname: "unknown", nickname: "unknown",
@ -109,9 +103,7 @@ export class ProfileController
side: pmc.Info.Side, side: pmc.Info.Side,
currlvl: pmc.Info.Level, currlvl: pmc.Info.Level,
currexp: pmc.Info.Experience ?? 0, currexp: pmc.Info.Experience ?? 0,
prevexp: currlvl === 0 prevexp: currlvl === 0 ? 0 : this.profileHelper.getExperience(currlvl),
? 0
: this.profileHelper.getExperience(currlvl),
nextlvl: nextlvl, nextlvl: nextlvl,
maxlvl: maxlvl, maxlvl: maxlvl,
edition: profile.info?.edition ?? "", edition: profile.info?.edition ?? "",
@ -123,8 +115,7 @@ export class ProfileController
/** /**
* Handle client/game/profile/list * Handle client/game/profile/list
*/ */
public getCompleteProfile(sessionID: string): IPmcData[] public getCompleteProfile(sessionID: string): IPmcData[] {
{
return this.profileHelper.getCompleteProfile(sessionID); return this.profileHelper.getCompleteProfile(sessionID);
} }
@ -134,11 +125,11 @@ export class ProfileController
* @param sessionID Player id * @param sessionID Player id
* @returns Profiles _id value * @returns Profiles _id value
*/ */
public createProfile(info: IProfileCreateRequestData, sessionID: string): string public createProfile(info: IProfileCreateRequestData, sessionID: string): string {
{
const account = this.saveServer.getProfile(sessionID).info; const account = this.saveServer.getProfile(sessionID).info;
const profileTemplate: ITemplateSide const profileTemplate: ITemplateSide = this.cloner.clone(
= this.cloner.clone(this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()]); this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()],
);
const pmcData = profileTemplate.character; const pmcData = profileTemplate.character;
// Delete existing profile // Delete existing profile
@ -166,8 +157,7 @@ export class ProfileController
this.updateInventoryEquipmentId(pmcData); this.updateInventoryEquipmentId(pmcData);
if (!pmcData.UnlockedInfo) if (!pmcData.UnlockedInfo) {
{
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] }; pmcData.UnlockedInfo = { unlockedProductionRecipe: [] };
} }
@ -200,14 +190,12 @@ export class ProfileController
this.saveServer.addProfile(profileDetails); this.saveServer.addProfile(profileDetails);
if (profileTemplate.trader.setQuestsAvailableForStart) if (profileTemplate.trader.setQuestsAvailableForStart) {
{
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]); this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
} }
// Profile is flagged as wanting quests set to ready to hand in and collect rewards // Profile is flagged as wanting quests set to ready to hand in and collect rewards
if (profileTemplate.trader.setQuestsAvailableForFinish) if (profileTemplate.trader.setQuestsAvailableForFinish) {
{
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [ this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [
QuestStatus.AvailableForStart, QuestStatus.AvailableForStart,
QuestStatus.Started, QuestStatus.Started,
@ -234,8 +222,7 @@ export class ProfileController
this.saveServer.saveProfile(sessionID); this.saveServer.saveProfile(sessionID);
// Requires to enable seasonal changes after creating fresh profile // Requires to enable seasonal changes after creating fresh profile
if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) {
{
this.seasonalEventService.enableSeasonalEvents(sessionID); this.seasonalEventService.enableSeasonalEvents(sessionID);
} }
@ -246,22 +233,18 @@ export class ProfileController
* make profiles pmcData.Inventory.equipment unique * make profiles pmcData.Inventory.equipment unique
* @param pmcData Profile to update * @param pmcData Profile to update
*/ */
protected updateInventoryEquipmentId(pmcData: IPmcData): void protected updateInventoryEquipmentId(pmcData: IPmcData): void {
{
const oldEquipmentId = pmcData.Inventory.equipment; const oldEquipmentId = pmcData.Inventory.equipment;
pmcData.Inventory.equipment = this.hashUtil.generate(); pmcData.Inventory.equipment = this.hashUtil.generate();
for (const item of pmcData.Inventory.items) for (const item of pmcData.Inventory.items) {
{ if (item.parentId === oldEquipmentId) {
if (item.parentId === oldEquipmentId)
{
item.parentId = pmcData.Inventory.equipment; item.parentId = pmcData.Inventory.equipment;
continue; continue;
} }
if (item._id === oldEquipmentId) if (item._id === oldEquipmentId) {
{
item._id = pmcData.Inventory.equipment; item._id = pmcData.Inventory.equipment;
} }
} }
@ -271,14 +254,10 @@ export class ProfileController
* Delete a profile * Delete a profile
* @param sessionID Id of profile to delete * @param sessionID Id of profile to delete
*/ */
protected deleteProfileBySessionId(sessionID: string): void protected deleteProfileBySessionId(sessionID: string): void {
{ if (sessionID in this.saveServer.getProfiles()) {
if (sessionID in this.saveServer.getProfiles())
{
this.saveServer.deleteProfileById(sessionID); this.saveServer.deleteProfileById(sessionID);
} } else {
else
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID), this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID),
); );
@ -296,10 +275,8 @@ export class ProfileController
profileDetails: ISptProfile, profileDetails: ISptProfile,
sessionID: string, sessionID: string,
response: IItemEventRouterResponse, response: IItemEventRouterResponse,
): void ): void {
{ for (const quest of profileDetails.characters.pmc.Quests) {
for (const quest of profileDetails.characters.pmc.Quests)
{
const questFromDb = this.questHelper.getQuestFromDb(quest.qid, profileDetails.characters.pmc); const questFromDb = this.questHelper.getQuestFromDb(quest.qid, profileDetails.characters.pmc);
// Get messageId of text to send to player as text message in game // Get messageId of text to send to player as text message in game
@ -331,10 +308,8 @@ export class ProfileController
* For each trader reset their state to what a level 1 player would see * For each trader reset their state to what a level 1 player would see
* @param sessionId Session id of profile to reset * @param sessionId Session id of profile to reset
*/ */
protected resetAllTradersInProfile(sessionId: string): void protected resetAllTradersInProfile(sessionId: string): void {
{ for (const traderId in this.databaseService.getTraders()) {
for (const traderId in this.databaseService.getTraders())
{
this.traderHelper.resetTrader(sessionId, traderId); this.traderHelper.resetTrader(sessionId, traderId);
} }
} }
@ -345,23 +320,19 @@ export class ProfileController
* @param sessionID * @param sessionID
* @returns IPmcData object * @returns IPmcData object
*/ */
public generatePlayerScav(sessionID: string): IPmcData public generatePlayerScav(sessionID: string): IPmcData {
{
return this.playerScavGenerator.generate(sessionID); return this.playerScavGenerator.generate(sessionID);
} }
/** /**
* Handle client/game/profile/nickname/validate * Handle client/game/profile/nickname/validate
*/ */
public validateNickname(info: IValidateNicknameRequestData, sessionID: string): string public validateNickname(info: IValidateNicknameRequestData, sessionID: string): string {
{ if (info.nickname.length < 3) {
if (info.nickname.length < 3)
{
return "tooshort"; return "tooshort";
} }
if (this.profileHelper.isNicknameTaken(info, sessionID)) if (this.profileHelper.isNicknameTaken(info, sessionID)) {
{
return "taken"; return "taken";
} }
@ -372,12 +343,10 @@ export class ProfileController
* Handle client/game/profile/nickname/change event * Handle client/game/profile/nickname/change event
* Client allows player to adjust their profile name * Client allows player to adjust their profile name
*/ */
public changeNickname(info: IProfileChangeNicknameRequestData, sessionID: string): string public changeNickname(info: IProfileChangeNicknameRequestData, sessionID: string): string {
{
const output = this.validateNickname(info, sessionID); const output = this.validateNickname(info, sessionID);
if (output === "OK") if (output === "OK") {
{
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
pmcData.Info.Nickname = info.nickname; pmcData.Info.Nickname = info.nickname;
@ -390,8 +359,7 @@ export class ProfileController
/** /**
* Handle client/game/profile/voice/change event * Handle client/game/profile/voice/change event
*/ */
public changeVoice(info: IProfileChangeVoiceRequestData, sessionID: string): void public changeVoice(info: IProfileChangeVoiceRequestData, sessionID: string): void {
{
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
pmcData.Info.Voice = info.voice; pmcData.Info.Voice = info.voice;
} }
@ -399,8 +367,7 @@ export class ProfileController
/** /**
* Handle client/game/profile/search * Handle client/game/profile/search
*/ */
public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[] public getFriends(info: ISearchFriendRequestData, sessionID: string): ISearchFriendResponse[] {
{
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
// return some of the current player info for now // return some of the current player info for now
@ -421,8 +388,7 @@ export class ProfileController
/** /**
* Handle client/profile/status * Handle client/profile/status
*/ */
public getProfileStatus(sessionId: string): GetProfileStatusResponseData public getProfileStatus(sessionId: string): GetProfileStatusResponseData {
{
const account = this.saveServer.getProfile(sessionId).info; const account = this.saveServer.getProfile(sessionId).info;
const response: GetProfileStatusResponseData = { const response: GetProfileStatusResponseData = {
maxPveCountExceeded: false, maxPveCountExceeded: false,
@ -442,8 +408,7 @@ export class ProfileController
return response; return response;
} }
public getOtherProfile(sessionId: string, request: IGetOtherProfileRequest): IGetOtherProfileResponse public getOtherProfile(sessionId: string, request: IGetOtherProfileRequest): IGetOtherProfileResponse {
{
const player = this.profileHelper.getFullProfile(sessionId); const player = this.profileHelper.getFullProfile(sessionId);
const playerPmc = player.characters.pmc; const playerPmc = player.characters.pmc;
const playerScav = player.characters.scav; const playerScav = player.characters.scav;
@ -493,21 +458,17 @@ export class ProfileController
/** /**
* Handle client/profile/settings * Handle client/profile/settings
*/ */
public setChosenProfileIcon(sessionId: string, request: IGetProfileSettingsRequest): boolean public setChosenProfileIcon(sessionId: string, request: IGetProfileSettingsRequest): boolean {
{
const profileToUpdate = this.profileHelper.getPmcProfile(sessionId); const profileToUpdate = this.profileHelper.getPmcProfile(sessionId);
if (!profileToUpdate) if (!profileToUpdate) {
{
return false; return false;
} }
if (request.memberCategory !== null) if (request.memberCategory !== null) {
{
profileToUpdate.Info.SelectedMemberCategory = request.memberCategory; profileToUpdate.Info.SelectedMemberCategory = request.memberCategory;
} }
if (request.squadInviteRestriction !== null) if (request.squadInviteRestriction !== null) {
{
profileToUpdate.Info.SquadInviteRestriction = request.squadInviteRestriction; profileToUpdate.Info.SquadInviteRestriction = request.squadInviteRestriction;
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { DialogueHelper } from "@spt/helpers/DialogueHelper"; import { DialogueHelper } from "@spt/helpers/DialogueHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
@ -27,13 +26,13 @@ import { LocaleService } from "@spt/services/LocaleService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { MailSendService } from "@spt/services/MailSendService"; import { MailSendService } from "@spt/services/MailSendService";
import { PlayerService } from "@spt/services/PlayerService"; import { PlayerService } from "@spt/services/PlayerService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class QuestController export class QuestController {
{
protected questConfig: IQuestConfig; protected questConfig: IQuestConfig;
constructor( constructor(
@ -54,8 +53,7 @@ export class QuestController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
} }
@ -66,44 +64,37 @@ export class QuestController
* @param sessionID session id * @param sessionID session id
* @returns array of IQuest * @returns array of IQuest
*/ */
public getClientQuests(sessionID: string): IQuest[] public getClientQuests(sessionID: string): IQuest[] {
{
const questsToShowPlayer: IQuest[] = []; const questsToShowPlayer: IQuest[] = [];
const allQuests = this.questHelper.getQuestsFromDb(); const allQuests = this.questHelper.getQuestsFromDb();
const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID); const profile: IPmcData = this.profileHelper.getPmcProfile(sessionID);
for (const quest of allQuests) for (const quest of allQuests) {
{
// Player already accepted the quest, show it regardless of status // Player already accepted the quest, show it regardless of status
const questInProfile = profile.Quests.find((x) => x.qid === quest._id); const questInProfile = profile.Quests.find((x) => x.qid === quest._id);
if (questInProfile) if (questInProfile) {
{
quest.sptStatus = questInProfile.status; quest.sptStatus = questInProfile.status;
questsToShowPlayer.push(quest); questsToShowPlayer.push(quest);
continue; continue;
} }
// Filter out bear quests for usec and vice versa // Filter out bear quests for usec and vice versa
if (this.questHelper.questIsForOtherSide(profile.Info.Side, quest._id)) if (this.questHelper.questIsForOtherSide(profile.Info.Side, quest._id)) {
{
continue; continue;
} }
if (!this.questHelper.showEventQuestToPlayer(quest._id)) if (!this.questHelper.showEventQuestToPlayer(quest._id)) {
{
continue; continue;
} }
// Don't add quests that have a level higher than the user's // Don't add quests that have a level higher than the user's
if (!this.playerLevelFulfillsQuestRequirement(quest, profile.Info.Level)) if (!this.playerLevelFulfillsQuestRequirement(quest, profile.Info.Level)) {
{
continue; continue;
} }
// Player can use trader mods then remove them, leaving quests behind // Player can use trader mods then remove them, leaving quests behind
const trader = profile.TradersInfo[quest.traderId]; const trader = profile.TradersInfo[quest.traderId];
if (!trader) if (!trader) {
{
this.logger.debug( this.logger.debug(
`Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`, `Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`,
); );
@ -121,11 +112,10 @@ export class QuestController
// Quest has no conditions, standing or loyalty conditions, add to visible quest list // Quest has no conditions, standing or loyalty conditions, add to visible quest list
if ( if (
questRequirements.length === 0 questRequirements.length === 0 &&
&& loyaltyRequirements.length === 0 loyaltyRequirements.length === 0 &&
&& standingRequirements.length === 0 standingRequirements.length === 0
) ) {
{
quest.sptStatus = QuestStatus.AvailableForStart; quest.sptStatus = QuestStatus.AvailableForStart;
questsToShowPlayer.push(quest); questsToShowPlayer.push(quest);
continue; continue;
@ -134,34 +124,29 @@ export class QuestController
// Check the status of each quest condition, if any are not completed // Check the status of each quest condition, if any are not completed
// then this quest should not be visible // then this quest should not be visible
let haveCompletedPreviousQuest = true; let haveCompletedPreviousQuest = true;
for (const conditionToFulfil of questRequirements) for (const conditionToFulfil of questRequirements) {
{
// If the previous quest isn't in the user profile, it hasn't been completed or started // If the previous quest isn't in the user profile, it hasn't been completed or started
const prerequisiteQuest = profile.Quests.find((profileQuest) => const prerequisiteQuest = profile.Quests.find((profileQuest) =>
conditionToFulfil.target.includes(profileQuest.qid), conditionToFulfil.target.includes(profileQuest.qid),
); );
if (!prerequisiteQuest) if (!prerequisiteQuest) {
{
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
break; break;
} }
// Prereq does not have its status requirement fulfilled // Prereq does not have its status requirement fulfilled
// Some bsg status ids are strings, MUST convert to number before doing includes check // Some bsg status ids are strings, MUST convert to number before doing includes check
if (!conditionToFulfil.status.map((status) => Number(status)).includes(prerequisiteQuest.status)) if (!conditionToFulfil.status.map((status) => Number(status)).includes(prerequisiteQuest.status)) {
{
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
break; break;
} }
// Has a wait timer // Has a wait timer
if (conditionToFulfil.availableAfter > 0) if (conditionToFulfil.availableAfter > 0) {
{
// Compare current time to unlock time for previous quest // Compare current time to unlock time for previous quest
const previousQuestCompleteTime = prerequisiteQuest.statusTimers[prerequisiteQuest.status]; const previousQuestCompleteTime = prerequisiteQuest.statusTimers[prerequisiteQuest.status];
const unlockTime = previousQuestCompleteTime + conditionToFulfil.availableAfter; const unlockTime = previousQuestCompleteTime + conditionToFulfil.availableAfter;
if (unlockTime > this.timeUtil.getTimestamp()) if (unlockTime > this.timeUtil.getTimestamp()) {
{
this.logger.debug( this.logger.debug(
`Quest ${quest.QuestName} is locked for another ${ `Quest ${quest.QuestName} is locked for another ${
unlockTime - this.timeUtil.getTimestamp() unlockTime - this.timeUtil.getTimestamp()
@ -172,33 +157,27 @@ export class QuestController
} }
// Previous quest not completed, skip // Previous quest not completed, skip
if (!haveCompletedPreviousQuest) if (!haveCompletedPreviousQuest) {
{
continue; continue;
} }
let passesLoyaltyRequirements = true; let passesLoyaltyRequirements = true;
for (const condition of loyaltyRequirements) for (const condition of loyaltyRequirements) {
{ if (!this.questHelper.traderLoyaltyLevelRequirementCheck(condition, profile)) {
if (!this.questHelper.traderLoyaltyLevelRequirementCheck(condition, profile))
{
passesLoyaltyRequirements = false; passesLoyaltyRequirements = false;
break; break;
} }
} }
let passesStandingRequirements = true; let passesStandingRequirements = true;
for (const condition of standingRequirements) for (const condition of standingRequirements) {
{ if (!this.questHelper.traderStandingRequirementCheck(condition, profile)) {
if (!this.questHelper.traderStandingRequirementCheck(condition, profile))
{
passesStandingRequirements = false; passesStandingRequirements = false;
break; break;
} }
} }
if (haveCompletedPreviousQuest && passesLoyaltyRequirements && passesStandingRequirements) if (haveCompletedPreviousQuest && passesLoyaltyRequirements && passesStandingRequirements) {
{
quest.sptStatus = QuestStatus.AvailableForStart; quest.sptStatus = QuestStatus.AvailableForStart;
questsToShowPlayer.push(quest); questsToShowPlayer.push(quest);
} }
@ -213,21 +192,16 @@ export class QuestController
* @param playerLevel level of player to test against quest * @param playerLevel level of player to test against quest
* @returns true if quest can be seen/accepted by player of defined level * @returns true if quest can be seen/accepted by player of defined level
*/ */
protected playerLevelFulfillsQuestRequirement(quest: IQuest, playerLevel: number): boolean protected playerLevelFulfillsQuestRequirement(quest: IQuest, playerLevel: number): boolean {
{ if (!quest.conditions) {
if (!quest.conditions)
{
// No conditions // No conditions
return true; return true;
} }
const levelConditions = this.questConditionHelper.getLevelConditions(quest.conditions.AvailableForStart); const levelConditions = this.questConditionHelper.getLevelConditions(quest.conditions.AvailableForStart);
if (levelConditions.length) if (levelConditions.length) {
{ for (const levelCondition of levelConditions) {
for (const levelCondition of levelConditions) if (!this.questHelper.doesPlayerLevelFulfilCondition(playerLevel, levelCondition)) {
{
if (!this.questHelper.doesPlayerLevelFulfilCondition(playerLevel, levelCondition))
{
// Not valid, exit out // Not valid, exit out
return false; return false;
} }
@ -252,23 +226,19 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData, acceptedQuest: IAcceptQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
// Does quest exist in profile // Does quest exist in profile
// Restarting a failed quest can mean quest exists in profile // Restarting a failed quest can mean quest exists in profile
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid); const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid);
if (existingQuestStatus) if (existingQuestStatus) {
{
// Update existing // Update existing
this.questHelper.resetQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid); this.questHelper.resetQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid);
// Need to send client an empty list of completedConditions (Unsure if this does anything) // Need to send client an empty list of completedConditions (Unsure if this does anything)
acceptQuestResponse.profileChanges[sessionID].questsStatus.push(existingQuestStatus); acceptQuestResponse.profileChanges[sessionID].questsStatus.push(existingQuestStatus);
} } else {
else
{
// Add new quest to server profile // Add new quest to server profile
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, QuestStatus.Started, acceptedQuest); const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, QuestStatus.Started, acceptedQuest);
pmcData.Quests.push(newQuest); pmcData.Quests.push(newQuest);
@ -324,8 +294,7 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData, acceptedQuest: IAcceptQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
// Create and store quest status object inside player profile // Create and store quest status object inside player profile
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile( const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(
pmcData, pmcData,
@ -336,8 +305,7 @@ export class QuestController
// Look for the generated quest cache in profile.RepeatableQuests // Look for the generated quest cache in profile.RepeatableQuests
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest); const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
if (!repeatableQuestProfile) if (!repeatableQuestProfile) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"repeatable-accepted_repeatable_quest_not_found_in_active_quests", "repeatable-accepted_repeatable_quest_not_found_in_active_quests",
@ -350,13 +318,11 @@ export class QuestController
// Some scav quests need to be added to scav profile for them to show up in-raid // Some scav quests need to be added to scav profile for them to show up in-raid
if ( if (
repeatableQuestProfile.side === "Scav" repeatableQuestProfile.side === "Scav" &&
&& ["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type) ["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)
) ) {
{
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
if (!fullProfile.characters.scav.Quests) if (!fullProfile.characters.scav.Quests) {
{
fullProfile.characters.scav.Quests = []; fullProfile.characters.scav.Quests = [];
} }
@ -371,20 +337,21 @@ export class QuestController
protected createAcceptedQuestClientResponse( protected createAcceptedQuestClientResponse(
sessionID: string, sessionID: string,
pmcData: IPmcData, pmcData: IPmcData,
repeatableQuestProfile: IRepeatableQuest): IItemEventRouterResponse repeatableQuestProfile: IRepeatableQuest,
{ ): IItemEventRouterResponse {
const repeatableSettings = pmcData.RepeatableQuests const repeatableSettings = pmcData.RepeatableQuests.find(
.find((quest) => quest.name === repeatableQuestProfile.sptRepatableGroupName); (quest) => quest.name === repeatableQuestProfile.sptRepatableGroupName,
);
const change = {}; const change = {};
change[repeatableQuestProfile._id] = repeatableSettings!.changeRequirement[repeatableQuestProfile._id]; change[repeatableQuestProfile._id] = repeatableSettings!.changeRequirement[repeatableQuestProfile._id];
const repeatableData: IPmcDataRepeatableQuest = { const repeatableData: IPmcDataRepeatableQuest = {
id: id:
repeatableSettings.id repeatableSettings.id ??
?? this.questConfig.repeatableQuests this.questConfig.repeatableQuests.find(
.find((repeatableQuest) => repeatableQuest.name === repeatableQuestProfile.sptRepatableGroupName) (repeatableQuest) => repeatableQuest.name === repeatableQuestProfile.sptRepatableGroupName,
.id, ).id,
name: repeatableSettings.name, name: repeatableSettings.name,
endTime: repeatableSettings.endTime, endTime: repeatableSettings.endTime,
changeRequirement: change, changeRequirement: change,
@ -396,8 +363,7 @@ export class QuestController
// Nullguard // Nullguard
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests) if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests) {
{
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = []; acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [];
} }
@ -416,13 +382,10 @@ export class QuestController
protected getRepeatableQuestFromProfile( protected getRepeatableQuestFromProfile(
pmcData: IPmcData, pmcData: IPmcData,
acceptedQuest: IAcceptQuestRequestData, acceptedQuest: IAcceptQuestRequestData,
): IRepeatableQuest ): IRepeatableQuest {
{ for (const repeatableQuest of pmcData.RepeatableQuests) {
for (const repeatableQuest of pmcData.RepeatableQuests)
{
const matchingQuest = repeatableQuest.activeQuests.find((x) => x._id === acceptedQuest.qid); const matchingQuest = repeatableQuest.activeQuests.find((x) => x._id === acceptedQuest.qid);
if (matchingQuest) if (matchingQuest) {
{
this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`); this.logger.debug(`Accepted repeatable quest ${acceptedQuest.qid} from ${repeatableQuest.name}`);
matchingQuest.sptRepatableGroupName = repeatableQuest.name; matchingQuest.sptRepatableGroupName = repeatableQuest.name;
@ -447,8 +410,7 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
body: ICompleteQuestRequestData, body: ICompleteQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID); const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
const completedQuest = this.questHelper.getQuestFromDb(body.qid, pmcData); const completedQuest = this.questHelper.getQuestFromDb(body.qid, pmcData);
@ -469,8 +431,7 @@ export class QuestController
// Check for linked failed + unrestartable quests (only get quests not already failed // Check for linked failed + unrestartable quests (only get quests not already failed
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId, pmcData); const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId, pmcData);
if (questsToFail?.length > 0) if (questsToFail?.length > 0) {
{
this.failQuests(sessionID, pmcData, questsToFail, completeQuestResponse); this.failQuests(sessionID, pmcData, questsToFail, completeQuestResponse);
} }
@ -487,16 +448,13 @@ export class QuestController
completeQuestResponse.profileChanges[sessionID].quests.push(...questDelta); completeQuestResponse.profileChanges[sessionID].quests.push(...questDelta);
// Check if it's a repeatable quest. If so, remove from Quests // Check if it's a repeatable quest. If so, remove from Quests
for (const currentRepeatable of pmcData.RepeatableQuests) for (const currentRepeatable of pmcData.RepeatableQuests) {
{
const repeatableQuest = currentRepeatable.activeQuests.find( const repeatableQuest = currentRepeatable.activeQuests.find(
(activeRepeatable) => activeRepeatable._id === completedQuestId, (activeRepeatable) => activeRepeatable._id === completedQuestId,
); );
if (repeatableQuest) if (repeatableQuest) {
{
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile // Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
if (repeatableQuest.side === "Scav") if (repeatableQuest.side === "Scav") {
{
this.removeQuestFromScavProfile(sessionID, repeatableQuest._id); this.removeQuestFromScavProfile(sessionID, repeatableQuest._id);
} }
} }
@ -504,8 +462,7 @@ export class QuestController
// Hydrate client response questsStatus array with data // Hydrate client response questsStatus array with data
const questStatusChanges = this.getQuestsWithDifferentStatuses(preCompleteProfileQuests, pmcData.Quests); const questStatusChanges = this.getQuestsWithDifferentStatuses(preCompleteProfileQuests, pmcData.Quests);
if (questStatusChanges) if (questStatusChanges) {
{
completeQuestResponse.profileChanges[sessionID].questsStatus.push(...questStatusChanges); completeQuestResponse.profileChanges[sessionID].questsStatus.push(...questStatusChanges);
} }
@ -520,14 +477,11 @@ export class QuestController
* @param completedQuestId quest completed id * @param completedQuestId quest completed id
* @returns array of IQuest objects * @returns array of IQuest objects
*/ */
protected getQuestsFailedByCompletingQuest(completedQuestId: string, pmcProfile: IPmcData): IQuest[] protected getQuestsFailedByCompletingQuest(completedQuestId: string, pmcProfile: IPmcData): IQuest[] {
{
const questsInDb = this.questHelper.getQuestsFromDb(); const questsInDb = this.questHelper.getQuestsFromDb();
return questsInDb.filter((quest) => return questsInDb.filter((quest) => {
{
// No fail conditions, skip // No fail conditions, skip
if (!quest.conditions.Fail || quest.conditions.Fail.length === 0) if (!quest.conditions.Fail || quest.conditions.Fail.length === 0) {
{
return false; return false;
} }
@ -536,8 +490,7 @@ export class QuestController
pmcProfile.Quests.some( pmcProfile.Quests.some(
(profileQuest) => profileQuest.qid === quest._id && profileQuest.status === QuestStatus.Fail, (profileQuest) => profileQuest.qid === quest._id && profileQuest.status === QuestStatus.Fail,
) )
) ) {
{
return false; return false;
} }
@ -550,17 +503,16 @@ export class QuestController
* @param sessionId Player id * @param sessionId Player id
* @param questIdToRemove Qid of quest to remove * @param questIdToRemove Qid of quest to remove
*/ */
protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void protected removeQuestFromScavProfile(sessionId: string, questIdToRemove: string): void {
{
const fullProfile = this.profileHelper.getFullProfile(sessionId); const fullProfile = this.profileHelper.getFullProfile(sessionId);
const repeatableInScavProfile = fullProfile.characters.scav.Quests?.find((x) => x.qid === questIdToRemove); const repeatableInScavProfile = fullProfile.characters.scav.Quests?.find((x) => x.qid === questIdToRemove);
if (!repeatableInScavProfile) if (!repeatableInScavProfile) {
{ this.logger.warning(
this.logger.warning(this.localisationService.getText("quest-unable_to_remove_scav_quest_from_profile", this.localisationService.getText("quest-unable_to_remove_scav_quest_from_profile", {
{
scavQuestId: questIdToRemove, scavQuestId: questIdToRemove,
profileId: sessionId, profileId: sessionId,
})); }),
);
return; return;
} }
@ -580,22 +532,18 @@ export class QuestController
protected getQuestsWithDifferentStatuses( protected getQuestsWithDifferentStatuses(
preQuestStatusus: IQuestStatus[], preQuestStatusus: IQuestStatus[],
postQuestStatuses: IQuestStatus[], postQuestStatuses: IQuestStatus[],
): IQuestStatus[] | undefined ): IQuestStatus[] | undefined {
{
const result: IQuestStatus[] = []; const result: IQuestStatus[] = [];
for (const quest of postQuestStatuses) for (const quest of postQuestStatuses) {
{
// Add quest if status differs or quest not found // Add quest if status differs or quest not found
const preQuest = preQuestStatusus.find((x) => x.qid === quest.qid); const preQuest = preQuestStatusus.find((x) => x.qid === quest.qid);
if (!preQuest || preQuest.status !== quest.status) if (!preQuest || preQuest.status !== quest.status) {
{
result.push(quest); result.push(quest);
} }
} }
if (result.length === 0) if (result.length === 0) {
{
return undefined; return undefined;
} }
@ -614,8 +562,7 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
completedQuestId: string, completedQuestId: string,
questRewards: Item[], questRewards: Item[],
): void ): void {
{
const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData); const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData);
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
@ -634,24 +581,20 @@ export class QuestController
* @param quests Quests to look for wait conditions in * @param quests Quests to look for wait conditions in
* @param completedQuestId Quest just completed * @param completedQuestId Quest just completed
*/ */
protected addTimeLockedQuestsToProfile(pmcData: IPmcData, quests: IQuest[], completedQuestId: string): void protected addTimeLockedQuestsToProfile(pmcData: IPmcData, quests: IQuest[], completedQuestId: string): void {
{
// Iterate over quests, look for quests with right criteria // Iterate over quests, look for quests with right criteria
for (const quest of quests) for (const quest of quests) {
{
// If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time) // If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time)
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find( const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(
(x) => x.target?.includes(completedQuestId) && x.availableAfter > 0, (x) => x.target?.includes(completedQuestId) && x.availableAfter > 0,
); );
if (nextQuestWaitCondition) if (nextQuestWaitCondition) {
{
// Now + wait time // Now + wait time
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter; const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
// Update quest in profile with status of AvailableAfter // Update quest in profile with status of AvailableAfter
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id); const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
if (existingQuestInProfile) if (existingQuestInProfile) {
{
existingQuestInProfile.availableAfter = availableAfterTimestamp; existingQuestInProfile.availableAfter = availableAfterTimestamp;
existingQuestInProfile.status = QuestStatus.AvailableAfter; existingQuestInProfile.status = QuestStatus.AvailableAfter;
existingQuestInProfile.startTime = 0; existingQuestInProfile.startTime = 0;
@ -686,21 +629,16 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
questsToFail: IQuest[], questsToFail: IQuest[],
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{ for (const questToFail of questsToFail) {
for (const questToFail of questsToFail)
{
// Skip failing a quest that has a fail status of something other than success // Skip failing a quest that has a fail status of something other than success
if (questToFail.conditions.Fail?.some((x) => x.status?.some((status) => status !== QuestStatus.Success))) if (questToFail.conditions.Fail?.some((x) => x.status?.some((status) => status !== QuestStatus.Success))) {
{
continue; continue;
} }
const isActiveQuestInPlayerProfile = pmcData.Quests.find((quest) => quest.qid === questToFail._id); const isActiveQuestInPlayerProfile = pmcData.Quests.find((quest) => quest.qid === questToFail._id);
if (isActiveQuestInPlayerProfile) if (isActiveQuestInPlayerProfile) {
{ if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail) {
if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail)
{
const failBody: IFailQuestRequestData = { const failBody: IFailQuestRequestData = {
Action: "QuestFail", Action: "QuestFail",
qid: questToFail._id, qid: questToFail._id,
@ -708,9 +646,7 @@ export class QuestController
}; };
this.questHelper.failQuest(pmcData, failBody, sessionID, output); this.questHelper.failQuest(pmcData, failBody, sessionID, output);
} }
} } else {
else
{
// Failing an entirely new quest that doesnt exist in profile // Failing an entirely new quest that doesnt exist in profile
const statusTimers = {}; const statusTimers = {};
statusTimers[QuestStatus.Fail] = this.timeUtil.getTimestamp(); statusTimers[QuestStatus.Fail] = this.timeUtil.getTimestamp();
@ -736,8 +672,7 @@ export class QuestController
pmcData: IPmcData, pmcData: IPmcData,
handoverQuestRequest: IHandoverQuestRequestData, handoverQuestRequest: IHandoverQuestRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData); const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData);
const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"]; const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
@ -747,25 +682,22 @@ export class QuestController
// Decrement number of items handed in // Decrement number of items handed in
let handoverRequirements: IQuestCondition; let handoverRequirements: IQuestCondition;
for (const condition of quest.conditions.AvailableForFinish) for (const condition of quest.conditions.AvailableForFinish) {
{
if ( if (
condition.id === handoverQuestRequest.conditionId condition.id === handoverQuestRequest.conditionId &&
&& handoverQuestTypes.includes(condition.conditionType) handoverQuestTypes.includes(condition.conditionType)
) ) {
{
handedInCount = Number.parseInt(<string>condition.value); handedInCount = Number.parseInt(<string>condition.value);
isItemHandoverQuest = condition.conditionType === handoverQuestTypes[0]; isItemHandoverQuest = condition.conditionType === handoverQuestTypes[0];
handoverRequirements = condition; handoverRequirements = condition;
const profileCounter const profileCounter =
= handoverQuestRequest.conditionId in pmcData.TaskConditionCounters handoverQuestRequest.conditionId in pmcData.TaskConditionCounters
? pmcData.TaskConditionCounters[handoverQuestRequest.conditionId].value ? pmcData.TaskConditionCounters[handoverQuestRequest.conditionId].value
: 0; : 0;
handedInCount -= profileCounter; handedInCount -= profileCounter;
if (handedInCount <= 0) if (handedInCount <= 0) {
{
this.logger.error( this.logger.error(
this.localisationService.getText( this.localisationService.getText(
"repeatable-quest_handover_failed_condition_already_satisfied", "repeatable-quest_handover_failed_condition_already_satisfied",
@ -785,17 +717,14 @@ export class QuestController
} }
} }
if (isItemHandoverQuest && handedInCount === 0) if (isItemHandoverQuest && handedInCount === 0) {
{
return this.showRepeatableQuestInvalidConditionError(handoverQuestRequest, output); return this.showRepeatableQuestInvalidConditionError(handoverQuestRequest, output);
} }
let totalItemCountToRemove = 0; let totalItemCountToRemove = 0;
for (const itemHandover of handoverQuestRequest.items) for (const itemHandover of handoverQuestRequest.items) {
{
const matchingItemInProfile = pmcData.Inventory.items.find((item) => item._id === itemHandover.id); const matchingItemInProfile = pmcData.Inventory.items.find((item) => item._id === itemHandover.id);
if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl))) if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl))) {
{
// Item handed in by player doesnt match what was requested // Item handed in by player doesnt match what was requested
return this.showQuestItemHandoverMatchError( return this.showQuestItemHandoverMatchError(
handoverQuestRequest, handoverQuestRequest,
@ -808,8 +737,7 @@ export class QuestController
// Remove the right quantity of given items // Remove the right quantity of given items
const itemCountToRemove = Math.min(itemHandover.count, handedInCount - totalItemCountToRemove); const itemCountToRemove = Math.min(itemHandover.count, handedInCount - totalItemCountToRemove);
totalItemCountToRemove += itemCountToRemove; totalItemCountToRemove += itemCountToRemove;
if (itemHandover.count - itemCountToRemove > 0) if (itemHandover.count - itemCountToRemove > 0) {
{
// Remove single item with no children // Remove single item with no children
this.questHelper.changeItemStack( this.questHelper.changeItemStack(
pmcData, pmcData,
@ -818,13 +746,10 @@ export class QuestController
sessionID, sessionID,
output, output,
); );
if (totalItemCountToRemove === handedInCount) if (totalItemCountToRemove === handedInCount) {
{
break; break;
} }
} } else {
else
{
// Remove item with children // Remove item with children
const toRemove = this.itemHelper.findAndReturnChildrenByItems(pmcData.Inventory.items, itemHandover.id); const toRemove = this.itemHelper.findAndReturnChildrenByItems(pmcData.Inventory.items, itemHandover.id);
let index = pmcData.Inventory.items.length; let index = pmcData.Inventory.items.length;
@ -833,17 +758,14 @@ export class QuestController
output.profileChanges[sessionID].items.del.push({ _id: itemHandover.id }); output.profileChanges[sessionID].items.del.push({ _id: itemHandover.id });
// Important: loop backward when removing items from the array we're looping on // Important: loop backward when removing items from the array we're looping on
while (index-- > 0) while (index-- > 0) {
{ if (toRemove.includes(pmcData.Inventory.items[index]._id)) {
if (toRemove.includes(pmcData.Inventory.items[index]._id))
{
// Remove the item // Remove the item
const removedItem = pmcData.Inventory.items.splice(index, 1)[0]; const removedItem = pmcData.Inventory.items.splice(index, 1)[0];
// If the removed item has a numeric `location` property, re-calculate all the child // If the removed item has a numeric `location` property, re-calculate all the child
// element `location` properties of the parent so they are sequential, while retaining order // element `location` properties of the parent so they are sequential, while retaining order
if (typeof removedItem.location === "number") if (typeof removedItem.location === "number") {
{
const childItems = this.itemHelper.findAndReturnChildrenAsItems( const childItems = this.itemHelper.findAndReturnChildrenAsItems(
pmcData.Inventory.items, pmcData.Inventory.items,
removedItem.parentId, removedItem.parentId,
@ -852,8 +774,7 @@ export class QuestController
// Sort by the current `location` and update // Sort by the current `location` and update
childItems.sort((a, b) => (a.location > b.location ? 1 : -1)); childItems.sort((a, b) => (a.location > b.location ? 1 : -1));
for (const [index, item] of childItems.entries()) for (const [index, item] of childItems.entries()) {
{
item.location = index; item.location = index;
} }
} }
@ -881,8 +802,7 @@ export class QuestController
protected showRepeatableQuestInvalidConditionError( protected showRepeatableQuestInvalidConditionError(
handoverQuestRequest: IHandoverQuestRequestData, handoverQuestRequest: IHandoverQuestRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", { const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", {
questId: handoverQuestRequest.qid, questId: handoverQuestRequest.qid,
conditionId: handoverQuestRequest.conditionId, conditionId: handoverQuestRequest.conditionId,
@ -905,8 +825,7 @@ export class QuestController
itemHandedOver: Item, itemHandedOver: Item,
handoverRequirements: IQuestCondition, handoverRequirements: IQuestCondition,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const errorMessage = this.localisationService.getText("quest-handover_wrong_item", { const errorMessage = this.localisationService.getText("quest-handover_wrong_item", {
questId: handoverQuestRequest.qid, questId: handoverQuestRequest.qid,
handedInTpl: itemHandedOver?._tpl ?? "UNKNOWN", handedInTpl: itemHandedOver?._tpl ?? "UNKNOWN",
@ -930,10 +849,8 @@ export class QuestController
conditionId: string, conditionId: string,
questId: string, questId: string,
counterValue: number, counterValue: number,
): void ): void {
{ if (pmcData.TaskConditionCounters[conditionId] !== undefined) {
if (pmcData.TaskConditionCounters[conditionId] !== undefined)
{
pmcData.TaskConditionCounters[conditionId].value += counterValue; pmcData.TaskConditionCounters[conditionId].value += counterValue;
return; return;
@ -959,8 +876,7 @@ export class QuestController
request: IFailQuestRequestData, request: IFailQuestRequestData,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
this.questHelper.failQuest(pmcData, request, sessionID, output); this.questHelper.failQuest(pmcData, request, sessionID, output);
return output; return output;

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator"; import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
import { HandbookHelper } from "@spt/helpers/HandbookHelper"; import { HandbookHelper } from "@spt/helpers/HandbookHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper"; import { InventoryHelper } from "@spt/helpers/InventoryHelper";
@ -43,13 +42,13 @@ import { RagfairRequiredItemsService } from "@spt/services/RagfairRequiredItemsS
import { RagfairTaxService } from "@spt/services/RagfairTaxService"; import { RagfairTaxService } from "@spt/services/RagfairTaxService";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
/** /**
* Handle RagfairCallback events * Handle RagfairCallback events
*/ */
@injectable() @injectable()
export class RagfairController export class RagfairController {
{
protected ragfairConfig: IRagfairConfig; protected ragfairConfig: IRagfairConfig;
constructor( constructor(
@ -78,8 +77,7 @@ export class RagfairController
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator, @inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
} }
@ -90,8 +88,7 @@ export class RagfairController
* @param searchRequest Search request data * @param searchRequest Search request data
* @returns IGetOffersResult * @returns IGetOffersResult
*/ */
public getOffers(sessionID: string, searchRequest: ISearchRequestData): IGetOffersResult public getOffers(sessionID: string, searchRequest: ISearchRequestData): IGetOffersResult {
{
const profile = this.profileHelper.getFullProfile(sessionID); const profile = this.profileHelper.getFullProfile(sessionID);
const itemsToAdd = this.ragfairHelper.filterCategories(sessionID, searchRequest); const itemsToAdd = this.ragfairHelper.filterCategories(sessionID, searchRequest);
@ -105,8 +102,7 @@ export class RagfairController
result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, profile.characters.pmc); result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, profile.characters.pmc);
// Client requested a category refresh // Client requested a category refresh
if (searchRequest.updateOfferCount) if (searchRequest.updateOfferCount) {
{
result.categories = this.getSpecificCategories(profile.characters.pmc, searchRequest, result.offers); result.categories = this.getSpecificCategories(profile.characters.pmc, searchRequest, result.offers);
} }
@ -120,14 +116,11 @@ export class RagfairController
); );
// Match offers with quests and lock unfinished quests // Match offers with quests and lock unfinished quests
for (const offer of result.offers) for (const offer of result.offers) {
{ if (offer.user.memberType === MemberCategory.TRADER) {
if (offer.user.memberType === MemberCategory.TRADER)
{
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked // for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
// to true if the quest is not completed yet // to true if the quest is not completed yet
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts)) if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts)) {
{
offer.locked = true; offer.locked = true;
} }
@ -140,8 +133,7 @@ export class RagfairController
result.offersCount = result.offers.length; result.offersCount = result.offers.length;
// Handle paging before returning results only if searching for general items, not preset items // Handle paging before returning results only if searching for general items, not preset items
if (searchRequest.buildCount === 0) if (searchRequest.buildCount === 0) {
{
const start = searchRequest.page * searchRequest.limit; const start = searchRequest.page * searchRequest.limit;
const end = Math.min((searchRequest.page + 1) * searchRequest.limit, result.offers.length); const end = Math.min((searchRequest.page + 1) * searchRequest.limit, result.offers.length);
result.offers = result.offers.slice(start, end); result.offers = result.offers.slice(start, end);
@ -156,8 +148,7 @@ export class RagfairController
* @param request Request data * @param request Request data
* @returns IRagfairOffer * @returns IRagfairOffer
*/ */
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer {
{
const offers = this.ragfairOfferService.getOffers(); const offers = this.ragfairOfferService.getOffers();
const offerToReturn = offers.find((offer) => offer.intId === request.id); const offerToReturn = offers.find((offer) => offer.intId === request.id);
@ -177,16 +168,13 @@ export class RagfairController
itemsToAdd: string[], itemsToAdd: string[],
traderAssorts: Record<string, ITraderAssort>, traderAssorts: Record<string, ITraderAssort>,
pmcProfile: IPmcData, pmcProfile: IPmcData,
): IRagfairOffer[] ): IRagfairOffer[] {
{
// Searching for items in preset menu // Searching for items in preset menu
if (searchRequest.buildCount) if (searchRequest.buildCount) {
{
return this.ragfairOfferHelper.getOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile); return this.ragfairOfferHelper.getOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
} }
if (searchRequest.neededSearchId?.length > 0) if (searchRequest.neededSearchId?.length > 0) {
{
return this.ragfairOfferHelper.getOffersThatRequireItem(searchRequest, pmcProfile); return this.ragfairOfferHelper.getOffersThatRequireItem(searchRequest, pmcProfile);
} }
@ -204,23 +192,17 @@ export class RagfairController
pmcProfile: IPmcData, pmcProfile: IPmcData,
searchRequest: ISearchRequestData, searchRequest: ISearchRequestData,
offers: IRagfairOffer[], offers: IRagfairOffer[],
): Record<string, number> ): Record<string, number> {
{
// Linked/required search categories // Linked/required search categories
const playerHasFleaUnlocked const playerHasFleaUnlocked =
= pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel; pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel;
let offerPool = []; let offerPool = [];
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) {
{
offerPool = offers; offerPool = offers;
} } else if (!(this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))) {
else if (!(this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)))
{
// Get all categories // Get all categories
offerPool = this.ragfairOfferService.getOffers(); offerPool = this.ragfairOfferService.getOffers();
} } else {
else
{
this.logger.error(this.localisationService.getText("ragfair-unable_to_get_categories")); this.logger.error(this.localisationService.getText("ragfair-unable_to_get_categories"));
this.logger.debug(JSON.stringify(searchRequest)); this.logger.debug(JSON.stringify(searchRequest));
return {}; return {};
@ -233,12 +215,10 @@ export class RagfairController
* Add index to all offers passed in (0-indexed) * Add index to all offers passed in (0-indexed)
* @param offers Offers to add index value to * @param offers Offers to add index value to
*/ */
protected addIndexValueToOffers(offers: IRagfairOffer[]): void protected addIndexValueToOffers(offers: IRagfairOffer[]): void {
{
let counter = 0; let counter = 0;
for (const offer of offers) for (const offer of offers) {
{
offer.intId = ++counter; offer.intId = ++counter;
offer.items[0].parentId = ""; // Without this it causes error: "Item deserialization error: No parent with id hideout found for item x" offer.items[0].parentId = ""; // Without this it causes error: "Item deserialization error: No parent with id hideout found for item x"
} }
@ -249,8 +229,7 @@ export class RagfairController
* @param offer Flea offer to update * @param offer Flea offer to update
* @param fullProfile Players full profile * @param fullProfile Players full profile
*/ */
protected setTraderOfferPurchaseLimits(offer: IRagfairOffer, fullProfile: ISptProfile): void protected setTraderOfferPurchaseLimits(offer: IRagfairOffer, fullProfile: ISptProfile): void {
{
// No trader found, create a blank record for them // No trader found, create a blank record for them
fullProfile.traderPurchases[offer.user.id] ||= {}; fullProfile.traderPurchases[offer.user.id] ||= {};
@ -270,14 +249,12 @@ export class RagfairController
* Adjust ragfair offer stack count to match same value as traders assort stack count * Adjust ragfair offer stack count to match same value as traders assort stack count
* @param offer Flea offer to adjust stack size of * @param offer Flea offer to adjust stack size of
*/ */
protected setTraderOfferStackSize(offer: IRagfairOffer): void protected setTraderOfferStackSize(offer: IRagfairOffer): void {
{
const firstItem = offer.items[0]; const firstItem = offer.items[0];
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items; const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
const assortPurchased = traderAssorts.find((x) => x._id === offer.items[0]._id); const assortPurchased = traderAssorts.find((x) => x._id === offer.items[0]._id);
if (!assortPurchased) if (!assortPurchased) {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", { this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {
offerId: offer.items[0]._id, offerId: offer.items[0]._id,
@ -296,8 +273,7 @@ export class RagfairController
* @param info Search request * @param info Search request
* @returns True if it is a 'linked' search type * @returns True if it is a 'linked' search type
*/ */
protected isLinkedSearch(info: ISearchRequestData): boolean protected isLinkedSearch(info: ISearchRequestData): boolean {
{
return info.linkedSearchId !== ""; return info.linkedSearchId !== "";
} }
@ -306,26 +282,22 @@ export class RagfairController
* @param info Search request * @param info Search request
* @returns True if it is a 'required' search type * @returns True if it is a 'required' search type
*/ */
protected isRequiredSearch(info: ISearchRequestData): boolean protected isRequiredSearch(info: ISearchRequestData): boolean {
{
return info.neededSearchId !== ""; return info.neededSearchId !== "";
} }
/** /**
* Check all profiles and sell player offers / send player money for listing if it sold * Check all profiles and sell player offers / send player money for listing if it sold
*/ */
public update(): void public update(): void {
{
const profilesDict = this.saveServer.getProfiles(); const profilesDict = this.saveServer.getProfiles();
for (const sessionID in this.saveServer.getProfiles()) for (const sessionID in this.saveServer.getProfiles()) {
{
// Check profile is capable of creating offers // Check profile is capable of creating offers
const pmcProfile = profilesDict[sessionID].characters.pmc; const pmcProfile = profilesDict[sessionID].characters.pmc;
if ( if (
pmcProfile.RagfairInfo !== undefined pmcProfile.RagfairInfo !== undefined &&
&& pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel
) ) {
{
this.ragfairOfferHelper.processOffersOnProfile(sessionID); this.ragfairOfferHelper.processOffersOnProfile(sessionID);
} }
} }
@ -336,26 +308,22 @@ export class RagfairController
* @param getPriceRequest * @param getPriceRequest
* @returns min/avg/max values for an item based on flea offers available * @returns min/avg/max values for an item based on flea offers available
*/ */
public getItemMinAvgMaxFleaPriceValues(getPriceRequest: IGetMarketPriceRequestData): IGetItemPriceResult public getItemMinAvgMaxFleaPriceValues(getPriceRequest: IGetMarketPriceRequestData): IGetItemPriceResult {
{
// Get all items of tpl // Get all items of tpl
const offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId); const offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
// Offers exist for item, get averages of what's listed // Offers exist for item, get averages of what's listed
if (typeof offers === "object" && offers.length > 0) if (typeof offers === "object" && offers.length > 0) {
{
// These get calculated while iterating through the list below // These get calculated while iterating through the list below
let min = Number.MAX_VALUE; let min = Number.MAX_VALUE;
let max = 0; let max = 0;
// Get the average offer price, excluding barter offers // Get the average offer price, excluding barter offers
let avgOfferCount = 0; let avgOfferCount = 0;
const avg const avg =
= offers.reduce((sum, offer) => offers.reduce((sum, offer) => {
{
// Exclude barter items, they tend to have outrageous equivalent prices // Exclude barter items, they tend to have outrageous equivalent prices
if (offer.requirements.some((req) => !this.paymentHelper.isMoneyTpl(req._tpl))) if (offer.requirements.some((req) => !this.paymentHelper.isMoneyTpl(req._tpl))) {
{
return sum; return sum;
} }
@ -366,12 +334,9 @@ export class RagfairController
const perItemPrice = offer.requirementsCost / offerItemCount; const perItemPrice = offer.requirementsCost / offerItemCount;
// Handle min/max calculations based on the per-item price // Handle min/max calculations based on the per-item price
if (perItemPrice < min) if (perItemPrice < min) {
{
min = perItemPrice; min = perItemPrice;
} } else if (perItemPrice > max) {
else if (perItemPrice > max)
{
max = perItemPrice; max = perItemPrice;
} }
@ -380,8 +345,7 @@ export class RagfairController
}, 0) / Math.max(avgOfferCount, 1); }, 0) / Math.max(avgOfferCount, 1);
// If no items were actually counted, min will still be MAX_VALUE, so set it to 0 // If no items were actually counted, min will still be MAX_VALUE, so set it to 0
if (min === Number.MAX_VALUE) if (min === Number.MAX_VALUE) {
{
min = 0; min = 0;
} }
@ -390,8 +354,7 @@ export class RagfairController
// No offers listed, get price from live ragfair price list prices.json // No offers listed, get price from live ragfair price list prices.json
let tplPrice = this.databaseService.getPrices()[getPriceRequest.templateId]; let tplPrice = this.databaseService.getPrices()[getPriceRequest.templateId];
if (!tplPrice) if (!tplPrice) {
{
// No flea price, get handbook price // No flea price, get handbook price
tplPrice = this.handbookHelper.getTemplatePrice(getPriceRequest.templateId); tplPrice = this.handbookHelper.getTemplatePrice(getPriceRequest.templateId);
} }
@ -410,25 +373,21 @@ export class RagfairController
pmcData: IPmcData, pmcData: IPmcData,
offerRequest: IAddOfferRequestData, offerRequest: IAddOfferRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const fullProfile = this.saveServer.getProfile(sessionID); const fullProfile = this.saveServer.getProfile(sessionID);
const validationMessage = ""; const validationMessage = "";
if (!this.isValidPlayerOfferRequest(offerRequest, validationMessage)) if (!this.isValidPlayerOfferRequest(offerRequest, validationMessage)) {
{
return this.httpResponse.appendErrorToOutput(output, validationMessage); return this.httpResponse.appendErrorToOutput(output, validationMessage);
} }
const typeOfOffer = this.getOfferType(offerRequest); const typeOfOffer = this.getOfferType(offerRequest);
if (typeOfOffer === FleaOfferType.UNKNOWN) if (typeOfOffer === FleaOfferType.UNKNOWN) {
{
return this.httpResponse.appendErrorToOutput(output, "Unknown offer type, cannot list item on flea"); return this.httpResponse.appendErrorToOutput(output, "Unknown offer type, cannot list item on flea");
} }
switch (typeOfOffer) switch (typeOfOffer) {
{
case FleaOfferType.SINGLE: case FleaOfferType.SINGLE:
return this.createSingleOffer(sessionID, offerRequest, fullProfile, output); return this.createSingleOffer(sessionID, offerRequest, fullProfile, output);
case FleaOfferType.MULTI: case FleaOfferType.MULTI:
@ -451,16 +410,15 @@ export class RagfairController
sessionID: string, sessionID: string,
offerRequest: IAddOfferRequestData, offerRequest: IAddOfferRequestData,
fullProfile: ISptProfile, fullProfile: ISptProfile,
output: IItemEventRouterResponse): IItemEventRouterResponse output: IItemEventRouterResponse,
{ ): IItemEventRouterResponse {
const pmcData = fullProfile.characters.pmc; const pmcData = fullProfile.characters.pmc;
const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items
// Find items to be listed on flea from player inventory // Find items to be listed on flea from player inventory
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items); this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) {
{
this.httpResponse.appendErrorToOutput(output, itemsInInventoryError); this.httpResponse.appendErrorToOutput(output, itemsInInventoryError);
} }
@ -485,8 +443,7 @@ export class RagfairController
// Check for and apply item price modifer if it exists in config // Check for and apply item price modifer if it exists in config
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[rootItem._tpl]; const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[rootItem._tpl];
if (itemPriceModifer) if (itemPriceModifer) {
{
averageOfferPriceSingleItem *= itemPriceModifer; averageOfferPriceSingleItem *= itemPriceModifer;
} }
@ -502,8 +459,7 @@ export class RagfairController
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal); offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal);
// Subtract flea market fee from stash // Subtract flea market fee from stash
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees) {
{
const taxFeeChargeFailed = this.chargePlayerTaxFee( const taxFeeChargeFailed = this.chargePlayerTaxFee(
sessionID, sessionID,
rootItem, rootItem,
@ -513,8 +469,7 @@ export class RagfairController
offerRequest, offerRequest,
output, output,
); );
if (taxFeeChargeFailed) if (taxFeeChargeFailed) {
{
return output; return output;
} }
} }
@ -524,8 +479,7 @@ export class RagfairController
output.profileChanges[sessionID].ragFairOffers.push(offer); output.profileChanges[sessionID].ragFairOffers.push(offer);
// Remove items from inventory after creating offer // Remove items from inventory after creating offer
for (const itemToRemove of offerRequest.items) for (const itemToRemove of offerRequest.items) {
{
this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output); this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output);
} }
@ -546,8 +500,8 @@ export class RagfairController
sessionID: string, sessionID: string,
offerRequest: IAddOfferRequestData, offerRequest: IAddOfferRequestData,
fullProfile: ISptProfile, fullProfile: ISptProfile,
output: IItemEventRouterResponse): IItemEventRouterResponse output: IItemEventRouterResponse,
{ ): IItemEventRouterResponse {
const pmcData = fullProfile.characters.pmc; const pmcData = fullProfile.characters.pmc;
const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items
@ -555,13 +509,13 @@ export class RagfairController
// Get first item and its children and use as template // Get first item and its children and use as template
const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems( const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems(
pmcData.Inventory.items, pmcData.Inventory.items,
offerRequest.items[0]); offerRequest.items[0],
);
// Find items to be listed on flea (+ children) from player inventory // Find items to be listed on flea (+ children) from player inventory
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items); this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) {
{
this.httpResponse.appendErrorToOutput(output, itemsInInventoryError); this.httpResponse.appendErrorToOutput(output, itemsInInventoryError);
} }
@ -570,19 +524,13 @@ export class RagfairController
// When listing identical items on flea, condense separate items into one stack with a merged stack count // When listing identical items on flea, condense separate items into one stack with a merged stack count
// e.g. 2 ammo items, stackObjectCount = 3 for each, will result in 1 stack of 6 // e.g. 2 ammo items, stackObjectCount = 3 for each, will result in 1 stack of 6
if (!firstListingAndChidren[0].upd) if (!firstListingAndChidren[0].upd) {
{
firstListingAndChidren[0].upd = {}; firstListingAndChidren[0].upd = {};
} }
firstListingAndChidren[0].upd.StackObjectsCount = stackCountTotal; firstListingAndChidren[0].upd.StackObjectsCount = stackCountTotal;
// Create flea object // Create flea object
const offer = this.createPlayerOffer( const offer = this.createPlayerOffer(sessionID, offerRequest.requirements, firstListingAndChidren, false);
sessionID,
offerRequest.requirements,
firstListingAndChidren,
false,
);
// This is the item that will be listed on flea, has merged stackObjectCount // This is the item that will be listed on flea, has merged stackObjectCount
const newRootOfferItem = offer.items[0]; const newRootOfferItem = offer.items[0];
@ -592,8 +540,7 @@ export class RagfairController
// Check for and apply item price modifer if it exists in config // Check for and apply item price modifer if it exists in config
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl]; const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl];
if (itemPriceModifer) if (itemPriceModifer) {
{
averageOfferPrice *= itemPriceModifer; averageOfferPrice *= itemPriceModifer;
} }
@ -617,8 +564,7 @@ export class RagfairController
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal); offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal);
// Subtract flea market fee from stash // Subtract flea market fee from stash
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees) {
{
const taxFeeChargeFailed = this.chargePlayerTaxFee( const taxFeeChargeFailed = this.chargePlayerTaxFee(
sessionID, sessionID,
newRootOfferItem, newRootOfferItem,
@ -628,8 +574,7 @@ export class RagfairController
offerRequest, offerRequest,
output, output,
); );
if (taxFeeChargeFailed) if (taxFeeChargeFailed) {
{
return output; return output;
} }
} }
@ -639,8 +584,7 @@ export class RagfairController
output.profileChanges[sessionID].ragFairOffers.push(offer); output.profileChanges[sessionID].ragFairOffers.push(offer);
// Remove items from inventory after creating offer // Remove items from inventory after creating offer
for (const itemToRemove of offerRequest.items) for (const itemToRemove of offerRequest.items) {
{
this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output); this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output);
} }
@ -661,8 +605,8 @@ export class RagfairController
sessionID: string, sessionID: string,
offerRequest: IAddOfferRequestData, offerRequest: IAddOfferRequestData,
fullProfile: ISptProfile, fullProfile: ISptProfile,
output: IItemEventRouterResponse): IItemEventRouterResponse output: IItemEventRouterResponse,
{ ): IItemEventRouterResponse {
const pmcData = fullProfile.characters.pmc; const pmcData = fullProfile.characters.pmc;
const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items
@ -670,13 +614,13 @@ export class RagfairController
// Get first item and its children and use as template // Get first item and its children and use as template
const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems( const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems(
pmcData.Inventory.items, pmcData.Inventory.items,
offerRequest.items[0]); offerRequest.items[0],
);
// Find items to be listed on flea (+ children) from player inventory // Find items to be listed on flea (+ children) from player inventory
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items); this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) if (!itemsAndChildrenInInventoryToList || itemsInInventoryError) {
{
this.httpResponse.appendErrorToOutput(output, itemsInInventoryError); this.httpResponse.appendErrorToOutput(output, itemsInInventoryError);
} }
@ -685,19 +629,13 @@ export class RagfairController
// When listing identical items on flea, condense separate items into one stack with a merged stack count // When listing identical items on flea, condense separate items into one stack with a merged stack count
// e.g. 2 ammo items, stackObjectCount = 3 for each, will result in 1 stack of 6 // e.g. 2 ammo items, stackObjectCount = 3 for each, will result in 1 stack of 6
if (!firstListingAndChidren[0].upd) if (!firstListingAndChidren[0].upd) {
{
firstListingAndChidren[0].upd = {}; firstListingAndChidren[0].upd = {};
} }
firstListingAndChidren[0].upd.StackObjectsCount = stackCountTotal; firstListingAndChidren[0].upd.StackObjectsCount = stackCountTotal;
// Create flea object // Create flea object
const offer = this.createPlayerOffer( const offer = this.createPlayerOffer(sessionID, offerRequest.requirements, firstListingAndChidren, true);
sessionID,
offerRequest.requirements,
firstListingAndChidren,
true,
);
// This is the item that will be listed on flea, has merged stackObjectCount // This is the item that will be listed on flea, has merged stackObjectCount
const newRootOfferItem = offer.items[0]; const newRootOfferItem = offer.items[0];
@ -707,8 +645,7 @@ export class RagfairController
// Check for and apply item price modifer if it exists in config // Check for and apply item price modifer if it exists in config
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl]; const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl];
if (itemPriceModifer) if (itemPriceModifer) {
{
singleItemPrice *= itemPriceModifer; singleItemPrice *= itemPriceModifer;
} }
@ -732,8 +669,7 @@ export class RagfairController
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal, true); offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal, true);
// Subtract flea market fee from stash // Subtract flea market fee from stash
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees) {
{
const taxFeeChargeFailed = this.chargePlayerTaxFee( const taxFeeChargeFailed = this.chargePlayerTaxFee(
sessionID, sessionID,
newRootOfferItem, newRootOfferItem,
@ -743,8 +679,7 @@ export class RagfairController
offerRequest, offerRequest,
output, output,
); );
if (taxFeeChargeFailed) if (taxFeeChargeFailed) {
{
return output; return output;
} }
} }
@ -754,8 +689,7 @@ export class RagfairController
output.profileChanges[sessionID].ragFairOffers.push(offer); output.profileChanges[sessionID].ragFairOffers.push(offer);
// Remove items from inventory after creating offer // Remove items from inventory after creating offer
for (const itemToRemove of offerRequest.items) for (const itemToRemove of offerRequest.items) {
{
this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output); this.inventoryHelper.removeItem(pmcData, itemToRemove, sessionID, output);
} }
@ -768,18 +702,12 @@ export class RagfairController
* @param offerRequest Client request * @param offerRequest Client request
* @returns FleaOfferType * @returns FleaOfferType
*/ */
protected getOfferType(offerRequest: IAddOfferRequestData): FleaOfferType protected getOfferType(offerRequest: IAddOfferRequestData): FleaOfferType {
{ if (offerRequest.items.length == 1 && !offerRequest.sellInOnePiece) {
if (offerRequest.items.length == 1 && !offerRequest.sellInOnePiece)
{
return FleaOfferType.SINGLE; return FleaOfferType.SINGLE;
} } else if (offerRequest.items.length > 1 && !offerRequest.sellInOnePiece) {
else if (offerRequest.items.length > 1 && !offerRequest.sellInOnePiece)
{
return FleaOfferType.MULTI; return FleaOfferType.MULTI;
} } else if (offerRequest.sellInOnePiece) {
else if (offerRequest.sellInOnePiece)
{
return FleaOfferType.PACK; return FleaOfferType.PACK;
} }
@ -805,19 +733,18 @@ export class RagfairController
itemStackCount: number, itemStackCount: number,
offerRequest: IAddOfferRequestData, offerRequest: IAddOfferRequestData,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): boolean ): boolean {
{
// Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate) // Get tax from cache hydrated earlier by client, if that's missing fall back to server calculation (inaccurate)
const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]); const storedClientTaxValue = this.ragfairTaxService.getStoredClientOfferTaxValueById(offerRequest.items[0]);
const tax = storedClientTaxValue const tax = storedClientTaxValue
? storedClientTaxValue.fee ? storedClientTaxValue.fee
: this.ragfairTaxService.calculateTax( : this.ragfairTaxService.calculateTax(
rootItem, rootItem,
pmcData, pmcData,
requirementsPriceInRub, requirementsPriceInRub,
itemStackCount, itemStackCount,
offerRequest.sellInOnePiece, offerRequest.sellInOnePiece,
); );
this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${!!storedClientTaxValue}`); this.logger.debug(`Offer tax to charge: ${tax}, pulled from client: ${!!storedClientTaxValue}`);
@ -826,8 +753,7 @@ export class RagfairController
const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax); const buyTradeRequest = this.createBuyTradeRequestObject("RUB", tax);
this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output); this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
this.httpResponse.appendErrorToOutput( this.httpResponse.appendErrorToOutput(
output, output,
this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax), this.localisationService.getText("ragfair-unable_to_pay_commission_fee", tax),
@ -844,17 +770,14 @@ export class RagfairController
* @param errorMessage message to show to player when offer is invalid * @param errorMessage message to show to player when offer is invalid
* @returns Is offer valid * @returns Is offer valid
*/ */
protected isValidPlayerOfferRequest(offerRequest: IAddOfferRequestData, errorMessage: string): boolean protected isValidPlayerOfferRequest(offerRequest: IAddOfferRequestData, errorMessage: string): boolean {
{ if (!offerRequest?.items || offerRequest.items.length === 0) {
if (!offerRequest?.items || offerRequest.items.length === 0)
{
this.logger.error(this.localisationService.getText("ragfair-invalid_player_offer_request")); this.logger.error(this.localisationService.getText("ragfair-invalid_player_offer_request"));
return false; return false;
} }
if (!offerRequest.requirements) if (!offerRequest.requirements) {
{
this.logger.error(this.localisationService.getText("ragfair-unable_to_place_offer_with_no_requirements")); this.logger.error(this.localisationService.getText("ragfair-unable_to_place_offer_with_no_requirements"));
return false; return false;
@ -868,21 +791,16 @@ export class RagfairController
* @param requirements * @param requirements
* @returns Rouble price * @returns Rouble price
*/ */
protected calculateRequirementsPriceInRub(requirements: Requirement[]): number protected calculateRequirementsPriceInRub(requirements: Requirement[]): number {
{
let requirementsPriceInRub = 0; let requirementsPriceInRub = 0;
for (const item of requirements) for (const item of requirements) {
{
const requestedItemTpl = item._tpl; const requestedItemTpl = item._tpl;
if (this.paymentHelper.isMoneyTpl(requestedItemTpl)) if (this.paymentHelper.isMoneyTpl(requestedItemTpl)) {
{
requirementsPriceInRub += this.handbookHelper.inRUB(item.count, requestedItemTpl); requirementsPriceInRub += this.handbookHelper.inRUB(item.count, requestedItemTpl);
} } else {
else requirementsPriceInRub +=
{ this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
requirementsPriceInRub
+= this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
} }
} }
@ -898,17 +816,14 @@ export class RagfairController
protected getItemsToListOnFleaFromInventory( protected getItemsToListOnFleaFromInventory(
pmcData: IPmcData, pmcData: IPmcData,
itemIdsFromFleaOfferRequest: string[], itemIdsFromFleaOfferRequest: string[],
): { items: Item[][] | undefined, errorMessage: string | undefined } ): { items: Item[][] | undefined; errorMessage: string | undefined } {
{
const itemsToReturn: Item[][] = []; const itemsToReturn: Item[][] = [];
let errorMessage: string | undefined = undefined; let errorMessage: string | undefined = undefined;
// Count how many items are being sold and multiply the requested amount accordingly // Count how many items are being sold and multiply the requested amount accordingly
for (const itemId of itemIdsFromFleaOfferRequest) for (const itemId of itemIdsFromFleaOfferRequest) {
{
let item = pmcData.Inventory.items.find((i) => i._id === itemId); let item = pmcData.Inventory.items.find((i) => i._id === itemId);
if (!item) if (!item) {
{
errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", { errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {
id: itemId, id: itemId,
}); });
@ -921,8 +836,7 @@ export class RagfairController
itemsToReturn.push(this.itemHelper.findAndReturnChildrenAsItems(pmcData.Inventory.items, itemId)); itemsToReturn.push(this.itemHelper.findAndReturnChildrenAsItems(pmcData.Inventory.items, itemId));
} }
if (!itemsToReturn?.length) if (!itemsToReturn?.length) {
{
errorMessage = this.localisationService.getText("ragfair-unable_to_find_requested_items_in_inventory"); errorMessage = this.localisationService.getText("ragfair-unable_to_find_requested_items_in_inventory");
this.logger.error(errorMessage); this.logger.error(errorMessage);
@ -937,11 +851,9 @@ export class RagfairController
requirements: Requirement[], requirements: Requirement[],
items: Item[], items: Item[],
sellInOnePiece: boolean, sellInOnePiece: boolean,
): IRagfairOffer ): IRagfairOffer {
{
const loyalLevel = 1; const loyalLevel = 1;
const formattedItems: Item[] = items.map((item) => const formattedItems: Item[] = items.map((item) => {
{
const isChild = items.some((subItem) => subItem._id === item.parentId); const isChild = items.some((subItem) => subItem._id === item.parentId);
return { return {
@ -953,12 +865,12 @@ export class RagfairController
}; };
}); });
const formattedRequirements: IBarterScheme[] = requirements.map((item) => const formattedRequirements: IBarterScheme[] = requirements.map((item) => {
{
return { return {
_tpl: item._tpl, _tpl: item._tpl,
count: item.count, count: item.count,
onlyFunctional: item.onlyFunctional }; onlyFunctional: item.onlyFunctional,
};
}); });
return this.ragfairOfferGenerator.createAndAddFleaOffer( return this.ragfairOfferGenerator.createAndAddFleaOffer(
@ -971,13 +883,11 @@ export class RagfairController
); );
} }
public getAllFleaPrices(): Record<string, number> public getAllFleaPrices(): Record<string, number> {
{
return this.ragfairPriceService.getAllFleaPrices(); return this.ragfairPriceService.getAllFleaPrices();
} }
public getStaticPrices(): Record<string, number> public getStaticPrices(): Record<string, number> {
{
return this.ragfairPriceService.getAllStaticPrices(); return this.ragfairPriceService.getAllStaticPrices();
} }
@ -988,14 +898,12 @@ export class RagfairController
* @param sessionId Players id * @param sessionId Players id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public removeOffer(removeRequest: IRemoveOfferRequestData, sessionId: string): IItemEventRouterResponse public removeOffer(removeRequest: IRemoveOfferRequestData, sessionId: string): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
const pmcData = this.saveServer.getProfile(sessionId).characters.pmc; const pmcData = this.saveServer.getProfile(sessionId).characters.pmc;
const playerProfileOffers = pmcData.RagfairInfo.offers; const playerProfileOffers = pmcData.RagfairInfo.offers;
if (!playerProfileOffers) if (!playerProfileOffers) {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", { this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {
profileId: sessionId, profileId: sessionId,
@ -1007,8 +915,7 @@ export class RagfairController
} }
const playerOfferIndex = playerProfileOffers.findIndex((offer) => offer._id === removeRequest.offerId); const playerOfferIndex = playerProfileOffers.findIndex((offer) => offer._id === removeRequest.offerId);
if (playerOfferIndex === -1) if (playerOfferIndex === -1) {
{
this.logger.error( this.logger.error(
this.localisationService.getText("ragfair-offer_not_found_in_profile", { this.localisationService.getText("ragfair-offer_not_found_in_profile", {
offerId: removeRequest.offerId, offerId: removeRequest.offerId,
@ -1021,8 +928,7 @@ export class RagfairController
} }
const differenceInSeconds = playerProfileOffers[playerOfferIndex].endTime - this.timeUtil.getTimestamp(); const differenceInSeconds = playerProfileOffers[playerOfferIndex].endTime - this.timeUtil.getTimestamp();
if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds) if (differenceInSeconds > this.ragfairConfig.sell.expireSeconds) {
{
// `expireSeconds` Default is 71 seconds // `expireSeconds` Default is 71 seconds
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp(); const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
playerProfileOffers[playerOfferIndex].endTime = Math.round(newEndTime); playerProfileOffers[playerOfferIndex].endTime = Math.round(newEndTime);
@ -1037,8 +943,7 @@ export class RagfairController
* @param sessionId Players id * @param sessionId Players id
* @returns IItemEventRouterResponse * @returns IItemEventRouterResponse
*/ */
public extendOffer(extendRequest: IExtendOfferRequestData, sessionId: string): IItemEventRouterResponse public extendOffer(extendRequest: IExtendOfferRequestData, sessionId: string): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
const pmcData = this.saveServer.getProfile(sessionId).characters.pmc; const pmcData = this.saveServer.getProfile(sessionId).characters.pmc;
@ -1046,8 +951,7 @@ export class RagfairController
const playerOfferIndex = playerOffers.findIndex((offer) => offer._id === extendRequest.offerId); const playerOfferIndex = playerOffers.findIndex((offer) => offer._id === extendRequest.offerId);
const secondsToAdd = extendRequest.renewalTime * TimeUtil.ONE_HOUR_AS_SECONDS; const secondsToAdd = extendRequest.renewalTime * TimeUtil.ONE_HOUR_AS_SECONDS;
if (playerOfferIndex === -1) if (playerOfferIndex === -1) {
{
this.logger.warning( this.logger.warning(
this.localisationService.getText("ragfair-offer_not_found_in_profile", { this.localisationService.getText("ragfair-offer_not_found_in_profile", {
offerId: extendRequest.offerId, offerId: extendRequest.offerId,
@ -1060,14 +964,12 @@ export class RagfairController
} }
// MOD: Pay flea market fee // MOD: Pay flea market fee
if (this.ragfairConfig.sell.fees) if (this.ragfairConfig.sell.fees) {
{
const count = playerOffers[playerOfferIndex].sellInOnePiece const count = playerOffers[playerOfferIndex].sellInOnePiece
? 1 ? 1
: playerOffers[playerOfferIndex].items.reduce((sum, item) => : playerOffers[playerOfferIndex].items.reduce((sum, item) => {
{ return sum + item.upd.StackObjectsCount;
return sum + item.upd.StackObjectsCount; }, 0);
}, 0);
const tax = this.ragfairTaxService.calculateTax( const tax = this.ragfairTaxService.calculateTax(
playerOffers[playerOfferIndex].items[0], playerOffers[playerOfferIndex].items[0],
@ -1079,8 +981,7 @@ export class RagfairController
const request = this.createBuyTradeRequestObject("RUB", tax); const request = this.createBuyTradeRequestObject("RUB", tax);
this.paymentService.payMoney(pmcData, request, sessionId, output); this.paymentService.payMoney(pmcData, request, sessionId, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return this.httpResponse.appendErrorToOutput( return this.httpResponse.appendErrorToOutput(
output, output,
this.localisationService.getText("ragfair-unable_to_pay_commission_fee"), this.localisationService.getText("ragfair-unable_to_pay_commission_fee"),
@ -1100,8 +1001,7 @@ export class RagfairController
* @param value Amount of currency * @param value Amount of currency
* @returns IProcessBuyTradeRequestData * @returns IProcessBuyTradeRequestData
*/ */
protected createBuyTradeRequestObject(currency: string, value: number): IProcessBuyTradeRequestData protected createBuyTradeRequestObject(currency: string, value: number): IProcessBuyTradeRequestData {
{
return { return {
tid: "ragfair", tid: "ragfair",
Action: "TradingConfirm", Action: "TradingConfirm",

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestHelper } from "@spt/helpers/QuestHelper"; import { QuestHelper } from "@spt/helpers/QuestHelper";
import { RepairHelper } from "@spt/helpers/RepairHelper"; import { RepairHelper } from "@spt/helpers/RepairHelper";
@ -13,10 +12,10 @@ import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { PaymentService } from "@spt/services/PaymentService"; import { PaymentService } from "@spt/services/PaymentService";
import { RepairService } from "@spt/services/RepairService"; import { RepairService } from "@spt/services/RepairService";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class RepairController export class RepairController {
{
protected repairConfig: IRepairConfig; protected repairConfig: IRepairConfig;
constructor( constructor(
@ -29,8 +28,7 @@ export class RepairController
@inject("RepairHelper") protected repairHelper: RepairHelper, @inject("RepairHelper") protected repairHelper: RepairHelper,
@inject("RepairService") protected repairService: RepairService, @inject("RepairService") protected repairService: RepairService,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
) ) {}
{}
/** /**
* Handle TraderRepair event * Handle TraderRepair event
@ -44,13 +42,11 @@ export class RepairController
sessionID: string, sessionID: string,
body: ITraderRepairActionDataRequest, body: ITraderRepairActionDataRequest,
pmcData: IPmcData, pmcData: IPmcData,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// find the item to repair // find the item to repair
for (const repairItem of body.repairItems) for (const repairItem of body.repairItems) {
{
const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid); const repairDetails = this.repairService.repairItemByTrader(sessionID, pmcData, repairItem, body.tid);
this.repairService.payForRepair( this.repairService.payForRepair(
@ -62,8 +58,7 @@ export class RepairController
output, output,
); );
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return output; return output;
} }
@ -89,8 +84,7 @@ export class RepairController
sessionID: string, sessionID: string,
body: IRepairActionDataRequest, body: IRepairActionDataRequest,
pmcData: IPmcData, pmcData: IPmcData,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// repair item // repair item

View File

@ -1,13 +1,9 @@
import { inject, injectable } from "tsyringe";
import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator"; import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestHelper } from "@spt/helpers/QuestHelper"; import { QuestHelper } from "@spt/helpers/QuestHelper";
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper"; import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { import { IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt/models/eft/common/tables/IRepeatableQuests";
IPmcDataRepeatableQuest,
IRepeatableQuest,
} from "@spt/models/eft/common/tables/IRepeatableQuests";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { IRepeatableQuestChangeRequest } from "@spt/models/eft/quests/IRepeatableQuestChangeRequest"; import { IRepeatableQuestChangeRequest } from "@spt/models/eft/quests/IRepeatableQuestChangeRequest";
@ -26,15 +22,15 @@ import { DatabaseService } from "@spt/services/DatabaseService";
import { LocalisationService } from "@spt/services/LocalisationService"; import { LocalisationService } from "@spt/services/LocalisationService";
import { PaymentService } from "@spt/services/PaymentService"; import { PaymentService } from "@spt/services/PaymentService";
import { ProfileFixerService } from "@spt/services/ProfileFixerService"; import { ProfileFixerService } from "@spt/services/ProfileFixerService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { ObjectId } from "@spt/utils/ObjectId"; import { ObjectId } from "@spt/utils/ObjectId";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class RepeatableQuestController export class RepeatableQuestController {
{
protected questConfig: IQuestConfig; protected questConfig: IQuestConfig;
constructor( constructor(
@ -54,8 +50,7 @@ export class RepeatableQuestController
@inject("QuestHelper") protected questHelper: QuestHelper, @inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
} }
@ -84,30 +79,26 @@ export class RepeatableQuestController
* *
* @returns {array} Array of "repeatableQuestObjects" as described above * @returns {array} Array of "repeatableQuestObjects" as described above
*/ */
public getClientRepeatableQuests(sessionID: string): IPmcDataRepeatableQuest[] public getClientRepeatableQuests(sessionID: string): IPmcDataRepeatableQuest[] {
{
const returnData: Array<IPmcDataRepeatableQuest> = []; const returnData: Array<IPmcDataRepeatableQuest> = [];
const fullProfile = this.profileHelper.getFullProfile(sessionID)!; const fullProfile = this.profileHelper.getFullProfile(sessionID)!;
const pmcData = fullProfile.characters.pmc; const pmcData = fullProfile.characters.pmc;
const currentTime = this.timeUtil.getTimestamp(); const currentTime = this.timeUtil.getTimestamp();
// Daily / weekly / Daily_Savage // Daily / weekly / Daily_Savage
for (const repeatableConfig of this.questConfig.repeatableQuests) for (const repeatableConfig of this.questConfig.repeatableQuests) {
{
// Get daily/weekly data from profile, add empty object if missing // Get daily/weekly data from profile, add empty object if missing
const generatedRepeatables = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData); const generatedRepeatables = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
const repeatableTypeLower = repeatableConfig.name.toLowerCase(); const repeatableTypeLower = repeatableConfig.name.toLowerCase();
const canAccessRepeatables = this.canProfileAccessRepeatableQuests(repeatableConfig, pmcData); const canAccessRepeatables = this.canProfileAccessRepeatableQuests(repeatableConfig, pmcData);
if (!canAccessRepeatables) if (!canAccessRepeatables) {
{
// Dont send any repeatables, even existing ones // Dont send any repeatables, even existing ones
continue; continue;
} }
// Existing repeatables are still valid, add to return data and move to next sub-type // Existing repeatables are still valid, add to return data and move to next sub-type
if (currentTime < generatedRepeatables.endTime - 1) if (currentTime < generatedRepeatables.endTime - 1) {
{
returnData.push(generatedRepeatables); returnData.push(generatedRepeatables);
this.logger.debug(`[Quest Check] ${repeatableTypeLower} quests are still valid.`); this.logger.debug(`[Quest Check] ${repeatableTypeLower} quests are still valid.`);
@ -132,12 +123,10 @@ export class RepeatableQuestController
const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level); const questTypePool = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
// Add repeatable quests of this loops sub-type (daily/weekly) // Add repeatable quests of this loops sub-type (daily/weekly)
for (let i = 0; i < this.getQuestCount(repeatableConfig, pmcData); i++) for (let i = 0; i < this.getQuestCount(repeatableConfig, pmcData); i++) {
{
let quest: IRepeatableQuest | undefined = undefined; let quest: IRepeatableQuest | undefined = undefined;
let lifeline = 0; let lifeline = 0;
while (!quest && questTypePool.types.length > 0) while (!quest && questTypePool.types.length > 0) {
{
quest = this.repeatableQuestGenerator.generateRepeatableQuest( quest = this.repeatableQuestGenerator.generateRepeatableQuest(
pmcData.Info.Level, pmcData.Info.Level,
pmcData.TradersInfo, pmcData.TradersInfo,
@ -145,8 +134,7 @@ export class RepeatableQuestController
repeatableConfig, repeatableConfig,
); );
lifeline++; lifeline++;
if (lifeline > 10) if (lifeline > 10) {
{
this.logger.debug( this.logger.debug(
"We were stuck in repeatable quest generation. This should never happen. Please report", "We were stuck in repeatable quest generation. This should never happen. Please report",
); );
@ -155,8 +143,7 @@ export class RepeatableQuestController
} }
// check if there are no more quest types available // check if there are no more quest types available
if (questTypePool.types.length === 0) if (questTypePool.types.length === 0) {
{
break; break;
} }
quest.side = repeatableConfig.side; quest.side = repeatableConfig.side;
@ -170,8 +157,7 @@ export class RepeatableQuestController
fullProfile.spt.freeRepeatableRefreshUsedCount[repeatableTypeLower] = 0; fullProfile.spt.freeRepeatableRefreshUsedCount[repeatableTypeLower] = 0;
// Create stupid redundant change requirements from quest data // Create stupid redundant change requirements from quest data
for (const quest of generatedRepeatables.activeQuests) for (const quest of generatedRepeatables.activeQuests) {
{
generatedRepeatables.changeRequirement[quest._id] = { generatedRepeatables.changeRequirement[quest._id] = {
changeCost: quest.changeCost, changeCost: quest.changeCost,
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]), // Randomise standing cost to replace changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]), // Randomise standing cost to replace
@ -202,20 +188,16 @@ export class RepeatableQuestController
* @param generatedRepeatables Repeatables to process (daily/weekly) * @param generatedRepeatables Repeatables to process (daily/weekly)
* @param pmcData Player profile * @param pmcData Player profile
*/ */
protected processExpiredQuests(generatedRepeatables: IPmcDataRepeatableQuest, pmcData: IPmcData): void protected processExpiredQuests(generatedRepeatables: IPmcDataRepeatableQuest, pmcData: IPmcData): void {
{
const questsToKeep = []; const questsToKeep = [];
for (const activeQuest of generatedRepeatables.activeQuests) for (const activeQuest of generatedRepeatables.activeQuests) {
{
const questStatusInProfile = pmcData.Quests.find((quest) => quest.qid === activeQuest._id); const questStatusInProfile = pmcData.Quests.find((quest) => quest.qid === activeQuest._id);
if (!questStatusInProfile) if (!questStatusInProfile) {
{
continue; continue;
} }
// Keep finished quests in list so player can hand in // Keep finished quests in list so player can hand in
if (questStatusInProfile.status === QuestStatus.AvailableForFinish) if (questStatusInProfile.status === QuestStatus.AvailableForFinish) {
{
questsToKeep.push(activeQuest); questsToKeep.push(activeQuest);
this.logger.debug( this.logger.debug(
`Keeping repeatable quest: ${activeQuest._id} in activeQuests since it is available to hand in`, `Keeping repeatable quest: ${activeQuest._id} in activeQuests since it is available to hand in`,
@ -243,17 +225,14 @@ export class RepeatableQuestController
* @param pmcData Player profile * @param pmcData Player profile
* @returns True if profile is allowed to access dailies * @returns True if profile is allowed to access dailies
*/ */
protected canProfileAccessRepeatableQuests(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): boolean protected canProfileAccessRepeatableQuests(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): boolean {
{
// PMC and daily quests not unlocked yet // PMC and daily quests not unlocked yet
if (repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)) if (repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)) {
{
return false; return false;
} }
// Scav and daily quests not unlocked yet // Scav and daily quests not unlocked yet
if (repeatableConfig.side === "Scav" && !this.playerHasDailyScavQuestsUnlocked(pmcData)) if (repeatableConfig.side === "Scav" && !this.playerHasDailyScavQuestsUnlocked(pmcData)) {
{
this.logger.debug("Daily scav quests still locked, Intel center not built"); this.logger.debug("Daily scav quests still locked, Intel center not built");
return false; return false;
@ -267,11 +246,10 @@ export class RepeatableQuestController
* @param pmcData Player profile to check * @param pmcData Player profile to check
* @returns True if unlocked * @returns True if unlocked
*/ */
protected playerHasDailyScavQuestsUnlocked(pmcData: IPmcData): boolean protected playerHasDailyScavQuestsUnlocked(pmcData: IPmcData): boolean {
{ return (
return pmcData?.Hideout?.Areas pmcData?.Hideout?.Areas?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1
?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER) );
?.level >= 1;
} }
/** /**
@ -280,8 +258,7 @@ export class RepeatableQuestController
* @param repeatableConfig Config of daily type to check * @param repeatableConfig Config of daily type to check
* @returns True if unlocked * @returns True if unlocked
*/ */
protected playerHasDailyPmcQuestsUnlocked(pmcData: IPmcData, repeatableConfig: IRepeatableQuestConfig): boolean protected playerHasDailyPmcQuestsUnlocked(pmcData: IPmcData, repeatableConfig: IRepeatableQuestConfig): boolean {
{
return pmcData.Info.Level >= repeatableConfig.minPlayerLevel; return pmcData.Info.Level >= repeatableConfig.minPlayerLevel;
} }
@ -291,17 +268,15 @@ export class RepeatableQuestController
* @param pmcData Player profile * @param pmcData Player profile
* @returns Quest count * @returns Quest count
*/ */
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number {
{
if ( if (
repeatableConfig.name.toLowerCase() === "daily" repeatableConfig.name.toLowerCase() === "daily" &&
&& this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData) this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)
) ) {
{
// Elite charisma skill gives extra daily quest(s) // Elite charisma skill gives extra daily quest(s)
return ( return (
repeatableConfig.numQuests repeatableConfig.numQuests +
+ this.databaseService.getGlobals().config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings this.databaseService.getGlobals().config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
.RepeatableQuestExtraCount .RepeatableQuestExtraCount
); );
} }
@ -318,13 +293,13 @@ export class RepeatableQuestController
protected getRepeatableQuestSubTypeFromProfile( protected getRepeatableQuestSubTypeFromProfile(
repeatableConfig: IRepeatableQuestConfig, repeatableConfig: IRepeatableQuestConfig,
pmcData: IPmcData, pmcData: IPmcData,
): IPmcDataRepeatableQuest ): IPmcDataRepeatableQuest {
{
// Get from profile, add if missing // Get from profile, add if missing
let repeatableQuestDetails = pmcData.RepeatableQuests let repeatableQuestDetails = pmcData.RepeatableQuests.find(
.find((repeatable) => repeatable.name === repeatableConfig.name); (repeatable) => repeatable.name === repeatableConfig.name,
if (!repeatableQuestDetails) // Not in profile, generate );
{ if (!repeatableQuestDetails) {
// Not in profile, generate
const hasAccess = this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(pmcData); const hasAccess = this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(pmcData);
repeatableQuestDetails = { repeatableQuestDetails = {
id: repeatableConfig.id, id: repeatableConfig.id,
@ -347,13 +322,11 @@ export class RepeatableQuestController
/** /**
* Just for debug reasons. Draws dailies a random assort of dailies extracted from dumps * Just for debug reasons. Draws dailies a random assort of dailies extracted from dumps
*/ */
public generateDebugDailies(dailiesPool: any, factory: any, number: number): any public generateDebugDailies(dailiesPool: any, factory: any, number: number): any {
{
let randomQuests = []; let randomQuests = [];
let numberOfQuests = number; let numberOfQuests = number;
if (factory) if (factory) {
{
// First is factory extract always add for debugging // First is factory extract always add for debugging
randomQuests.push(dailiesPool[0]); randomQuests.push(dailiesPool[0]);
numberOfQuests -= 1; numberOfQuests -= 1;
@ -361,14 +334,11 @@ export class RepeatableQuestController
randomQuests = randomQuests.concat(this.randomUtil.drawRandomFromList(dailiesPool, numberOfQuests, false)); randomQuests = randomQuests.concat(this.randomUtil.drawRandomFromList(dailiesPool, numberOfQuests, false));
for (const element of randomQuests) for (const element of randomQuests) {
{
element._id = this.objectId.generate(); element._id = this.objectId.generate();
const conditions = element.conditions.AvailableForFinish; const conditions = element.conditions.AvailableForFinish;
for (const condition of conditions) for (const condition of conditions) {
{ if ("counter" in condition._props) {
if ("counter" in condition._props)
{
condition._props.counter.id = this.objectId.generate(); condition._props.counter.id = this.objectId.generate();
} }
} }
@ -384,18 +354,15 @@ export class RepeatableQuestController
* @param pmcLevel level of pmc generating quest pool * @param pmcLevel level of pmc generating quest pool
* @returns IQuestTypePool * @returns IQuestTypePool
*/ */
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool {
{
const questPool = this.createBaseQuestPool(repeatableConfig); const questPool = this.createBaseQuestPool(repeatableConfig);
// Get the allowed locations based on the PMC's level // Get the allowed locations based on the PMC's level
const locations = this.getAllowedLocationsForPmcLevel(repeatableConfig.locations, pmcLevel); const locations = this.getAllowedLocationsForPmcLevel(repeatableConfig.locations, pmcLevel);
// Populate Exploration and Pickup quest locations // Populate Exploration and Pickup quest locations
for (const location in locations) for (const location in locations) {
{ if (location !== ELocationName.ANY) {
if (location !== ELocationName.ANY)
{
questPool.pool.Exploration.locations[location] = locations[location]; questPool.pool.Exploration.locations[location] = locations[location];
questPool.pool.Pickup.locations[location] = locations[location]; questPool.pool.Pickup.locations[location] = locations[location];
} }
@ -408,21 +375,18 @@ export class RepeatableQuestController
const targetsConfig = this.repeatableQuestHelper.probabilityObjectArray(eliminationConfig.targets); const targetsConfig = this.repeatableQuestHelper.probabilityObjectArray(eliminationConfig.targets);
// Populate Elimination quest targets and their locations // Populate Elimination quest targets and their locations
for (const { data: target, key: targetKey } of targetsConfig) for (const { data: target, key: targetKey } of targetsConfig) {
{
// Target is boss // Target is boss
if (target.isBoss) if (target.isBoss) {
{
questPool.pool.Elimination.targets[targetKey] = { locations: ["any"] }; questPool.pool.Elimination.targets[targetKey] = { locations: ["any"] };
} } else {
else
{
// Non-boss targets // Non-boss targets
const possibleLocations = Object.keys(locations); const possibleLocations = Object.keys(locations);
const allowedLocations = (targetKey === "Savage") const allowedLocations =
? possibleLocations.filter((location) => location !== "laboratory") // Exclude labs for Savage targets. targetKey === "Savage"
: possibleLocations; ? possibleLocations.filter((location) => location !== "laboratory") // Exclude labs for Savage targets.
: possibleLocations;
questPool.pool.Elimination.targets[targetKey] = { locations: allowedLocations }; questPool.pool.Elimination.targets[targetKey] = { locations: allowedLocations };
} }
@ -431,8 +395,7 @@ export class RepeatableQuestController
return questPool; return questPool;
} }
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool {
{
return { return {
types: repeatableConfig.types.slice(), types: repeatableConfig.types.slice(),
pool: { Exploration: { locations: {} }, Elimination: { targets: {} }, Pickup: { locations: {} } }, pool: { Exploration: { locations: {} }, Elimination: { targets: {} }, Pickup: { locations: {} } },
@ -448,23 +411,18 @@ export class RepeatableQuestController
protected getAllowedLocationsForPmcLevel( protected getAllowedLocationsForPmcLevel(
locations: Record<ELocationName, string[]>, locations: Record<ELocationName, string[]>,
pmcLevel: number, pmcLevel: number,
): Partial<Record<ELocationName, string[]>> ): Partial<Record<ELocationName, string[]>> {
{
const allowedLocation: Partial<Record<ELocationName, string[]>> = {}; const allowedLocation: Partial<Record<ELocationName, string[]>> = {};
for (const location in locations) for (const location in locations) {
{
const locationNames = []; const locationNames = [];
for (const locationName of locations[location]) for (const locationName of locations[location]) {
{ if (this.isPmcLevelAllowedOnLocation(locationName, pmcLevel)) {
if (this.isPmcLevelAllowedOnLocation(locationName, pmcLevel))
{
locationNames.push(locationName); locationNames.push(locationName);
} }
} }
if (locationNames.length > 0) if (locationNames.length > 0) {
{
allowedLocation[location] = locationNames; allowedLocation[location] = locationNames;
} }
} }
@ -478,36 +436,29 @@ export class RepeatableQuestController
* @param pmcLevel The level of the pmc * @param pmcLevel The level of the pmc
* @returns True if the given pmc level is allowed to access the given location * @returns True if the given pmc level is allowed to access the given location
*/ */
protected isPmcLevelAllowedOnLocation(location: string, pmcLevel: number): boolean protected isPmcLevelAllowedOnLocation(location: string, pmcLevel: number): boolean {
{
// All PMC levels are allowed for 'any' location requirement // All PMC levels are allowed for 'any' location requirement
if (location === ELocationName.ANY) if (location === ELocationName.ANY) {
{
return true; return true;
} }
const locationBase = this.databaseService.getLocation(location.toLowerCase())?.base; const locationBase = this.databaseService.getLocation(location.toLowerCase())?.base;
if (!locationBase) if (!locationBase) {
{
return true; return true;
} }
return pmcLevel <= locationBase.RequiredPlayerLevelMax && pmcLevel >= locationBase.RequiredPlayerLevelMin; return pmcLevel <= locationBase.RequiredPlayerLevelMax && pmcLevel >= locationBase.RequiredPlayerLevelMin;
} }
public debugLogRepeatableQuestIds(pmcData: IPmcData): void public debugLogRepeatableQuestIds(pmcData: IPmcData): void {
{ for (const repeatable of pmcData.RepeatableQuests) {
for (const repeatable of pmcData.RepeatableQuests)
{
const activeQuestsIds = []; const activeQuestsIds = [];
const inactiveQuestsIds = []; const inactiveQuestsIds = [];
for (const active of repeatable.activeQuests) for (const active of repeatable.activeQuests) {
{
activeQuestsIds.push(active._id); activeQuestsIds.push(active._id);
} }
for (const inactive of repeatable.inactiveQuests) for (const inactive of repeatable.inactiveQuests) {
{
inactiveQuestsIds.push(inactive._id); inactiveQuestsIds.push(inactive._id);
} }
@ -529,15 +480,16 @@ export class RepeatableQuestController
pmcData: IPmcData, pmcData: IPmcData,
changeRequest: IRepeatableQuestChangeRequest, changeRequest: IRepeatableQuestChangeRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
const fullProfile = this.profileHelper.getFullProfile(sessionID); const fullProfile = this.profileHelper.getFullProfile(sessionID);
// Check for existing quest in (daily/weekly/scav arrays) // Check for existing quest in (daily/weekly/scav arrays)
const { quest: questToReplace, repeatableType: repeatablesInProfile } const { quest: questToReplace, repeatableType: repeatablesInProfile } = this.getRepeatableById(
= this.getRepeatableById(changeRequest.qid, pmcData); changeRequest.qid,
pmcData,
);
// Subtype name of quest - daily/weekly/scav // Subtype name of quest - daily/weekly/scav
const repeatableTypeLower = repeatablesInProfile.name.toLowerCase(); const repeatableTypeLower = repeatablesInProfile.name.toLowerCase();
@ -546,8 +498,9 @@ export class RepeatableQuestController
const replacedQuestTraderId = questToReplace.traderId; const replacedQuestTraderId = questToReplace.traderId;
// Update active quests to exclude the quest we're replacing // Update active quests to exclude the quest we're replacing
repeatablesInProfile.activeQuests = repeatablesInProfile.activeQuests repeatablesInProfile.activeQuests = repeatablesInProfile.activeQuests.filter(
.filter((quest) => quest._id !== changeRequest.qid); (quest) => quest._id !== changeRequest.qid,
);
// Save for later cost calculation // Save for later cost calculation
const previousChangeRequirement = this.cloner.clone(repeatablesInProfile.changeRequirement[changeRequest.qid]); const previousChangeRequirement = this.cloner.clone(repeatablesInProfile.changeRequirement[changeRequest.qid]);
@ -556,15 +509,14 @@ export class RepeatableQuestController
delete repeatablesInProfile.changeRequirement[changeRequest.qid]; delete repeatablesInProfile.changeRequirement[changeRequest.qid];
// Get config for this repeatable sub-type (daily/weekly/scav) // Get config for this repeatable sub-type (daily/weekly/scav)
const repeatableConfig = this.questConfig.repeatableQuests const repeatableConfig = this.questConfig.repeatableQuests.find(
.find((config) => config.name === repeatablesInProfile.name, (config) => config.name === repeatablesInProfile.name,
); );
// Generate meta-data for what type/levelrange of quests can be generated for player // Generate meta-data for what type/levelrange of quests can be generated for player
const allowedQuestTypes = this.generateQuestPool(repeatableConfig, pmcData.Info.Level); const allowedQuestTypes = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, allowedQuestTypes, repeatableConfig); const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, allowedQuestTypes, repeatableConfig);
if (!newRepeatableQuest) if (!newRepeatableQuest) {
{
// Unable to find quest being replaced // Unable to find quest being replaced
const message = `Unable to generate repeatable quest of type: ${repeatableTypeLower} to replace trader: ${replacedQuestTraderId} quest ${changeRequest.qid}`; const message = `Unable to generate repeatable quest of type: ${repeatableTypeLower} to replace trader: ${replacedQuestTraderId} quest ${changeRequest.qid}`;
this.logger.error(message); this.logger.error(message);
@ -593,20 +545,17 @@ export class RepeatableQuestController
// Check if we should charge player for replacing quest // Check if we should charge player for replacing quest
const isFreeToReplace = this.useFreeRefreshIfAvailable(fullProfile, repeatablesInProfile, repeatableTypeLower); const isFreeToReplace = this.useFreeRefreshIfAvailable(fullProfile, repeatablesInProfile, repeatableTypeLower);
if (!isFreeToReplace) if (!isFreeToReplace) {
{
// Reduce standing with trader for not doing their quest // Reduce standing with trader for not doing their quest
const traderOfReplacedQuest = pmcData.TradersInfo[replacedQuestTraderId]; const traderOfReplacedQuest = pmcData.TradersInfo[replacedQuestTraderId];
traderOfReplacedQuest.standing -= previousChangeRequirement.changeStandingCost; traderOfReplacedQuest.standing -= previousChangeRequirement.changeStandingCost;
const charismaBonus = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.CHARISMA)?.Progress ?? 0; const charismaBonus = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.CHARISMA)?.Progress ?? 0;
for (const cost of previousChangeRequirement.changeCost) for (const cost of previousChangeRequirement.changeCost) {
{
// Not free, Charge player + appy charisma bonus to cost of replacement // Not free, Charge player + appy charisma bonus to cost of replacement
cost.count = Math.trunc(cost.count * (1 - (Math.trunc((charismaBonus) / 100) * 0.001)) ?? 1); cost.count = Math.trunc(cost.count * (1 - Math.trunc(charismaBonus / 100) * 0.001) ?? 1);
this.paymentService.addPaymentToOutput(pmcData, cost.templateId, cost.count, sessionID, output); this.paymentService.addPaymentToOutput(pmcData, cost.templateId, cost.count, sessionID, output);
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return output; return output;
} }
} }
@ -618,8 +567,7 @@ export class RepeatableQuestController
// Purge inactive repeatables // Purge inactive repeatables
repeatableToChangeClone.inactiveQuests = []; repeatableToChangeClone.inactiveQuests = [];
if (!repeatableToChangeClone) if (!repeatableToChangeClone) {
{
// Unable to find quest being replaced // Unable to find quest being replaced
const message = this.localisationService.getText("quest-unable_to_find_repeatable_to_replace"); const message = this.localisationService.getText("quest-unable_to_find_repeatable_to_replace");
this.logger.error(message); this.logger.error(message);
@ -642,15 +590,11 @@ export class RepeatableQuestController
* @param pmcData Profile that contains quests to look through * @param pmcData Profile that contains quests to look through
* @returns IGetRepeatableByIdResult * @returns IGetRepeatableByIdResult
*/ */
protected getRepeatableById(questId: string, pmcData: IPmcData): IGetRepeatableByIdResult protected getRepeatableById(questId: string, pmcData: IPmcData): IGetRepeatableByIdResult {
{ for (const repeatablesInProfile of pmcData.RepeatableQuests) {
for (const repeatablesInProfile of pmcData.RepeatableQuests)
{
// Check for existing quest in (daily/weekly/scav arrays) // Check for existing quest in (daily/weekly/scav arrays)
const questToReplace = repeatablesInProfile.activeQuests const questToReplace = repeatablesInProfile.activeQuests.find((repeatable) => repeatable._id === questId);
.find((repeatable) => repeatable._id === questId); if (!questToReplace) {
if (!questToReplace)
{
// Not found, skip to next repeatable sub-type // Not found, skip to next repeatable sub-type
continue; continue;
} }
@ -665,14 +609,11 @@ export class RepeatableQuestController
pmcData: IPmcData, pmcData: IPmcData,
questTypePool: IQuestTypePool, questTypePool: IQuestTypePool,
repeatableConfig: IRepeatableQuestConfig, repeatableConfig: IRepeatableQuestConfig,
): IRepeatableQuest ): IRepeatableQuest {
{
const maxAttempts = 10; const maxAttempts = 10;
let newRepeatableQuest: IRepeatableQuest = undefined; let newRepeatableQuest: IRepeatableQuest = undefined;
let attempts = 0; let attempts = 0;
while (attempts < maxAttempts while (attempts < maxAttempts && questTypePool.types.length > 0) {
&& questTypePool.types.length > 0)
{
newRepeatableQuest = this.repeatableQuestGenerator.generateRepeatableQuest( newRepeatableQuest = this.repeatableQuestGenerator.generateRepeatableQuest(
pmcData.Info.Level, pmcData.Info.Level,
pmcData.TradersInfo, pmcData.TradersInfo,
@ -680,8 +621,7 @@ export class RepeatableQuestController
repeatableConfig, repeatableConfig,
); );
if (newRepeatableQuest) if (newRepeatableQuest) {
{
// Successfully generated a quest, exit loop // Successfully generated a quest, exit loop
break; break;
} }
@ -689,11 +629,8 @@ export class RepeatableQuestController
attempts++; attempts++;
} }
if (attempts > maxAttempts) if (attempts > maxAttempts) {
{ this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report");
this.logger.debug(
"We were stuck in repeatable quest generation. This should never happen. Please report",
);
} }
return newRepeatableQuest; return newRepeatableQuest;
@ -710,11 +647,10 @@ export class RepeatableQuestController
protected useFreeRefreshIfAvailable( protected useFreeRefreshIfAvailable(
fullProfile: ISptProfile, fullProfile: ISptProfile,
repeatableSubType: IPmcDataRepeatableQuest, repeatableSubType: IPmcDataRepeatableQuest,
repeatableTypeName: string): boolean repeatableTypeName: string,
{ ): boolean {
// No free refreshes, exit early // No free refreshes, exit early
if (repeatableSubType.freeChangesAvailable <= 0) if (repeatableSubType.freeChangesAvailable <= 0) {
{
// Reset counter to 0 // Reset counter to 0
repeatableSubType.freeChangesAvailable = 0; repeatableSubType.freeChangesAvailable = 0;
@ -722,12 +658,12 @@ export class RepeatableQuestController
} }
// Only certain game versions have access to free refreshes // Only certain game versions have access to free refreshes
const hasAccessToFreeRefreshSystem const hasAccessToFreeRefreshSystem = this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(
= this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(fullProfile.characters.pmc); fullProfile.characters.pmc,
);
// If the player has access and available refreshes: // If the player has access and available refreshes:
if (hasAccessToFreeRefreshSystem) if (hasAccessToFreeRefreshSystem) {
{
// Initialize/retrieve free refresh count for the desired subtype: daily/weekly // Initialize/retrieve free refresh count for the desired subtype: daily/weekly
fullProfile.spt.freeRepeatableRefreshUsedCount ||= {}; fullProfile.spt.freeRepeatableRefreshUsedCount ||= {};
const repeatableRefreshCounts = fullProfile.spt.freeRepeatableRefreshUsedCount; const repeatableRefreshCounts = fullProfile.spt.freeRepeatableRefreshUsedCount;

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { ItemHelper } from "@spt/helpers/ItemHelper"; import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { RagfairOfferHelper } from "@spt/helpers/RagfairOfferHelper"; import { RagfairOfferHelper } from "@spt/helpers/RagfairOfferHelper";
@ -11,10 +10,7 @@ import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRo
import { IRagfairOffer } from "@spt/models/eft/ragfair/IRagfairOffer"; import { IRagfairOffer } from "@spt/models/eft/ragfair/IRagfairOffer";
import { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData"; import { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData";
import { IProcessBuyTradeRequestData } from "@spt/models/eft/trade/IProcessBuyTradeRequestData"; import { IProcessBuyTradeRequestData } from "@spt/models/eft/trade/IProcessBuyTradeRequestData";
import { import { IOfferRequest, IProcessRagfairTradeRequestData } from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
IOfferRequest,
IProcessRagfairTradeRequestData,
} from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
import { IProcessSellTradeRequestData } from "@spt/models/eft/trade/IProcessSellTradeRequestData"; import { IProcessSellTradeRequestData } from "@spt/models/eft/trade/IProcessSellTradeRequestData";
import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData"; import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData";
import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes"; import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes";
@ -37,10 +33,10 @@ import { HashUtil } from "@spt/utils/HashUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class TradeController export class TradeController {
{
protected ragfairConfig: IRagfairConfig; protected ragfairConfig: IRagfairConfig;
protected traderConfig: ITraderConfig; protected traderConfig: ITraderConfig;
@ -62,8 +58,7 @@ export class TradeController
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService, @inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) ) {
{
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
} }
@ -73,13 +68,11 @@ export class TradeController
pmcData: IPmcData, pmcData: IPmcData,
request: IProcessBaseTradeRequestData, request: IProcessBaseTradeRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
// Buying // Buying
if (request.type === "buy_from_trader") if (request.type === "buy_from_trader") {
{
const foundInRaid = this.traderConfig.purchasesAreFoundInRaid; const foundInRaid = this.traderConfig.purchasesAreFoundInRaid;
const buyData = <IProcessBuyTradeRequestData>request; const buyData = <IProcessBuyTradeRequestData>request;
this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output); this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output);
@ -88,8 +81,7 @@ export class TradeController
} }
// Selling // Selling
if (request.type === "sell_to_trader") if (request.type === "sell_to_trader") {
{
const sellData = <IProcessSellTradeRequestData>request; const sellData = <IProcessSellTradeRequestData>request;
this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID, output); this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID, output);
@ -107,15 +99,12 @@ export class TradeController
pmcData: IPmcData, pmcData: IPmcData,
request: IProcessRagfairTradeRequestData, request: IProcessRagfairTradeRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionID); const output = this.eventOutputHolder.getOutput(sessionID);
for (const offer of request.offers) for (const offer of request.offers) {
{
const fleaOffer = this.ragfairServer.getOffer(offer.id); const fleaOffer = this.ragfairServer.getOffer(offer.id);
if (!fleaOffer) if (!fleaOffer) {
{
return this.httpResponse.appendErrorToOutput( return this.httpResponse.appendErrorToOutput(
output, output,
`Offer with ID ${offer.id} not found`, `Offer with ID ${offer.id} not found`,
@ -123,8 +112,7 @@ export class TradeController
); );
} }
if (offer.count === 0) if (offer.count === 0) {
{
const errorMessage = this.localisationService.getText( const errorMessage = this.localisationService.getText(
"ragfair-unable_to_purchase_0_count_item", "ragfair-unable_to_purchase_0_count_item",
this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name, this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name,
@ -133,18 +121,14 @@ export class TradeController
} }
const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER; const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER;
if (sellerIsTrader) if (sellerIsTrader) {
{
this.buyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output); this.buyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
} } else {
else
{
this.buyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output); this.buyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
} }
// Exit loop early if problem found // Exit loop early if problem found
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return output; return output;
} }
} }
@ -166,11 +150,9 @@ export class TradeController
fleaOffer: IRagfairOffer, fleaOffer: IRagfairOffer,
requestOffer: IOfferRequest, requestOffer: IOfferRequest,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
// Skip buying items when player doesn't have needed loyalty // Skip buying items when player doesn't have needed loyalty
if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData)) if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData)) {
{
const errorMessage = `Unable to buy item: ${fleaOffer.items[0]._tpl} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`; const errorMessage = `Unable to buy item: ${fleaOffer.items[0]._tpl} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`;
this.logger.debug(errorMessage); this.logger.debug(errorMessage);
@ -206,8 +188,7 @@ export class TradeController
fleaOffer: IRagfairOffer, fleaOffer: IRagfairOffer,
requestOffer: IOfferRequest, requestOffer: IOfferRequest,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void {
{
const buyData: IProcessBuyTradeRequestData = { const buyData: IProcessBuyTradeRequestData = {
Action: "TradingConfirm", Action: "TradingConfirm",
type: "buy_from_ragfair", type: "buy_from_ragfair",
@ -226,8 +207,7 @@ export class TradeController
this.ragfairConfig.dynamic.purchasesAreFoundInRaid, this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
output, output,
); );
if (output.warnings.length > 0) if (output.warnings.length > 0) {
{
return; return;
} }
@ -236,8 +216,7 @@ export class TradeController
const offerBuyCount = requestOffer.count; const offerBuyCount = requestOffer.count;
const isPlayerOffer = this.isPlayerOffer(fleaOffer._id, fleaOffer.user?.id); const isPlayerOffer = this.isPlayerOffer(fleaOffer._id, fleaOffer.user?.id);
if (isPlayerOffer) if (isPlayerOffer) {
{
// Complete selling the offer now its been purchased // Complete selling the offer now its been purchased
this.ragfairOfferHelper.completeOffer(offerOwnerId, fleaOffer, offerBuyCount); this.ragfairOfferHelper.completeOffer(offerOwnerId, fleaOffer, offerBuyCount);
@ -254,17 +233,14 @@ export class TradeController
* @param offerOwnerId Owner id * @param offerOwnerId Owner id
* @returns true if offer was made by a player * @returns true if offer was made by a player
*/ */
protected isPlayerOffer(offerId: string, offerOwnerId: string): boolean protected isPlayerOffer(offerId: string, offerOwnerId: string): boolean {
{
// No ownerid, not player offer // No ownerid, not player offer
if (!offerOwnerId) if (!offerOwnerId) {
{
return false; return false;
} }
const offerCreatorProfile = this.profileHelper.getPmcProfile(offerOwnerId); const offerCreatorProfile = this.profileHelper.getPmcProfile(offerOwnerId);
if (!offerCreatorProfile || offerCreatorProfile.RagfairInfo.offers?.length === 0) if (!offerCreatorProfile || offerCreatorProfile.RagfairInfo.offers?.length === 0) {
{
// No profile or no offers // No profile or no offers
return false; return false;
} }
@ -280,8 +256,7 @@ export class TradeController
* @param pmcData Player profile * @param pmcData Player profile
* @returns True if player can buy offer * @returns True if player can buy offer
*/ */
protected playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer: IRagfairOffer, pmcData: IPmcData): boolean protected playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer: IRagfairOffer, pmcData: IPmcData): boolean {
{
return fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel; return fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel;
} }
@ -290,8 +265,7 @@ export class TradeController
pmcData: IPmcData, pmcData: IPmcData,
request: ISellScavItemsToFenceRequestData, request: ISellScavItemsToFenceRequestData,
sessionId: string, sessionId: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
const output = this.eventOutputHolder.getOutput(sessionId); const output = this.eventOutputHolder.getOutput(sessionId);
this.mailMoneyToPlayer(sessionId, request.totalValue, Traders.FENCE); this.mailMoneyToPlayer(sessionId, request.totalValue, Traders.FENCE);
@ -305,8 +279,7 @@ export class TradeController
* @param trader Trader to sell items to * @param trader Trader to sell items to
* @param output IItemEventRouterResponse * @param output IItemEventRouterResponse
*/ */
protected mailMoneyToPlayer(sessionId: string, roublesToSend: number, trader: Traders): void protected mailMoneyToPlayer(sessionId: string, roublesToSend: number, trader: Traders): void {
{
this.logger.debug(`Selling scav items to fence for ${roublesToSend} roubles`); this.logger.debug(`Selling scav items to fence for ${roublesToSend} roubles`);
// Create single currency item with all currency on it // Create single currency item with all currency on it
@ -325,7 +298,7 @@ export class TradeController
this.traderHelper.getTraderById(trader), this.traderHelper.getTraderById(trader),
MessageType.MESSAGE_WITH_ITEMS, MessageType.MESSAGE_WITH_ITEMS,
this.randomUtil.getArrayValue(this.databaseService.getTrader(trader).dialogue.soldItems), this.randomUtil.getArrayValue(this.databaseService.getTrader(trader).dialogue.soldItems),
curencyReward.flatMap((x) => x), curencyReward.flat(),
this.timeUtil.getHoursAsSeconds(72), this.timeUtil.getHoursAsSeconds(72),
); );
} }
@ -343,21 +316,18 @@ export class TradeController
items: Item[], items: Item[],
handbookPrices: Record<string, number>, handbookPrices: Record<string, number>,
traderDetails: ITraderBase, traderDetails: ITraderBase,
): number ): number {
{
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId); const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId);
let totalPrice = 0; let totalPrice = 0;
for (const itemToSell of itemWithChildren) for (const itemToSell of itemWithChildren) {
{
const itemDetails = this.itemHelper.getItem(itemToSell._tpl); const itemDetails = this.itemHelper.getItem(itemToSell._tpl);
if ( if (
!( !(
itemDetails[0] itemDetails[0] &&
&& this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category) this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category)
) )
) ) {
{
// Skip if tpl isn't item OR item doesn't fulfil match traders buy categories // Skip if tpl isn't item OR item doesn't fulfil match traders buy categories
continue; continue;
} }

View File

@ -1,4 +1,3 @@
import { inject, injectable } from "tsyringe";
import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator"; import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator";
import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper"; import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper";
@ -13,12 +12,12 @@ import { DatabaseService } from "@spt/services/DatabaseService";
import { FenceService } from "@spt/services/FenceService"; import { FenceService } from "@spt/services/FenceService";
import { TraderAssortService } from "@spt/services/TraderAssortService"; import { TraderAssortService } from "@spt/services/TraderAssortService";
import { TraderPurchasePersisterService } from "@spt/services/TraderPurchasePersisterService"; import { TraderPurchasePersisterService } from "@spt/services/TraderPurchasePersisterService";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class TraderController export class TraderController {
{
protected traderConfig: ITraderConfig; protected traderConfig: ITraderConfig;
constructor( constructor(
@ -35,8 +34,7 @@ export class TraderController
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator, @inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner, @inject("PrimaryCloner") protected cloner: ICloner,
) ) {
{
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
} }
@ -45,21 +43,17 @@ export class TraderController
* Iterate over traders, ensure a pristine copy of their assorts is stored in traderAssortService * Iterate over traders, ensure a pristine copy of their assorts is stored in traderAssortService
* Store timestamp of next assort refresh in nextResupply property of traders .base object * Store timestamp of next assort refresh in nextResupply property of traders .base object
*/ */
public load(): void public load(): void {
{
const nextHourTimestamp = this.timeUtil.getTimestampOfNextHour(); const nextHourTimestamp = this.timeUtil.getTimestampOfNextHour();
const traderResetStartsWithServer = this.traderConfig.tradersResetFromServerStart; const traderResetStartsWithServer = this.traderConfig.tradersResetFromServerStart;
const traders = this.databaseService.getTraders(); const traders = this.databaseService.getTraders();
for (const traderId in traders) for (const traderId in traders) {
{ if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER) {
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER)
{
continue; continue;
} }
if (traderId === Traders.FENCE) if (traderId === Traders.FENCE) {
{
this.fenceBaseAssortGenerator.generateFenceBaseAssorts(); this.fenceBaseAssortGenerator.generateFenceBaseAssorts();
this.fenceService.generateFenceAssorts(); this.fenceService.generateFenceAssorts();
continue; continue;
@ -68,8 +62,7 @@ export class TraderController
const trader = traders[traderId]; const trader = traders[traderId];
// Create dict of trader assorts on server start // Create dict of trader assorts on server start
if (!this.traderAssortService.getPristineTraderAssort(traderId)) if (!this.traderAssortService.getPristineTraderAssort(traderId)) {
{
const assortsClone = this.cloner.clone(trader.assort); const assortsClone = this.cloner.clone(trader.assort);
this.traderAssortService.setPristineTraderAssort(traderId, assortsClone); this.traderAssortService.setPristineTraderAssort(traderId, assortsClone);
} }
@ -90,19 +83,14 @@ export class TraderController
* Fence is handled slightly differently * Fence is handled slightly differently
* @returns has run * @returns has run
*/ */
public update(): boolean public update(): boolean {
{ for (const traderId in this.databaseService.getTables().traders) {
for (const traderId in this.databaseService.getTables().traders) if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER) {
{
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER)
{
continue; continue;
} }
if (traderId === Traders.FENCE) if (traderId === Traders.FENCE) {
{ if (this.fenceService.needsPartialRefresh()) {
if (this.fenceService.needsPartialRefresh())
{
this.fenceService.performPartialRefresh(); this.fenceService.performPartialRefresh();
} }
@ -111,8 +99,7 @@ export class TraderController
// Trader needs to be refreshed // Trader needs to be refreshed
const trader = this.databaseService.getTrader(traderId); const trader = this.databaseService.getTrader(traderId);
if (this.traderAssortHelper.traderAssortsHaveExpired(traderId)) if (this.traderAssortHelper.traderAssortsHaveExpired(traderId)) {
{
this.traderAssortHelper.resetExpiredTrader(trader); this.traderAssortHelper.resetExpiredTrader(trader);
// Reset purchase data per trader as they have independent reset times // Reset purchase data per trader as they have independent reset times
@ -129,21 +116,17 @@ export class TraderController
* @param sessionID Session id * @param sessionID Session id
* @returns array if ITraderBase objects * @returns array if ITraderBase objects
*/ */
public getAllTraders(sessionID: string): ITraderBase[] public getAllTraders(sessionID: string): ITraderBase[] {
{
const traders: ITraderBase[] = []; const traders: ITraderBase[] = [];
const pmcData = this.profileHelper.getPmcProfile(sessionID); const pmcData = this.profileHelper.getPmcProfile(sessionID);
for (const traderID in this.databaseService.getTables().traders) for (const traderID in this.databaseService.getTables().traders) {
{ if (this.databaseService.getTables().traders[traderID].base._id === "ragfair") {
if (this.databaseService.getTables().traders[traderID].base._id === "ragfair")
{
continue; continue;
} }
traders.push(this.traderHelper.getTrader(traderID, sessionID)); traders.push(this.traderHelper.getTrader(traderID, sessionID));
if (pmcData.Info) if (pmcData.Info) {
{
this.traderHelper.lvlUp(traderID, pmcData); this.traderHelper.lvlUp(traderID, pmcData);
} }
} }
@ -157,15 +140,12 @@ export class TraderController
* @param traderB Second trader to compare * @param traderB Second trader to compare
* @returns 1,-1 or 0 * @returns 1,-1 or 0
*/ */
protected sortByTraderId(traderA: ITraderBase, traderB: ITraderBase): number protected sortByTraderId(traderA: ITraderBase, traderB: ITraderBase): number {
{ if (traderA._id > traderB._id) {
if (traderA._id > traderB._id)
{
return 1; return 1;
} }
if (traderA._id < traderB._id) if (traderA._id < traderB._id) {
{
return -1; return -1;
} }
@ -173,14 +153,12 @@ export class TraderController
} }
/** Handle client/trading/api/getTrader */ /** Handle client/trading/api/getTrader */
public getTrader(sessionID: string, traderID: string): ITraderBase public getTrader(sessionID: string, traderID: string): ITraderBase {
{
return this.traderHelper.getTrader(sessionID, traderID); return this.traderHelper.getTrader(sessionID, traderID);
} }
/** Handle client/trading/api/getTraderAssort */ /** Handle client/trading/api/getTraderAssort */
public getAssort(sessionId: string, traderId: string): ITraderAssort public getAssort(sessionId: string, traderId: string): ITraderAssort {
{
return this.traderAssortHelper.getAssort(sessionId, traderId); return this.traderAssortHelper.getAssort(sessionId, traderId);
} }
} }

View File

@ -1,16 +1,15 @@
import { inject, injectable } from "tsyringe";
import { WeatherGenerator } from "@spt/generators/WeatherGenerator"; import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
import { IWeatherData } from "@spt/models/eft/weather/IWeatherData"; import { IWeatherData } from "@spt/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig"; import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData"; import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { SeasonalEventService } from "@spt/services/SeasonalEventService"; import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class WeatherController export class WeatherController {
{
protected weatherConfig: IWeatherConfig; protected weatherConfig: IWeatherConfig;
constructor( constructor(
@ -18,14 +17,12 @@ export class WeatherController
@inject("PrimaryLogger") protected logger: ILogger, @inject("PrimaryLogger") protected logger: ILogger,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService, @inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
) ) {
{
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER); this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
} }
/** Handle client/weather */ /** Handle client/weather */
public generate(): IWeatherData public generate(): IWeatherData {
{
let result: IWeatherData = { acceleration: 0, time: "", date: "", weather: undefined, season: 1 }; // defaults, hydrated below let result: IWeatherData = { acceleration: 0, time: "", date: "", weather: undefined, season: 1 }; // defaults, hydrated below
result = this.weatherGenerator.calculateGameTime(result); result = this.weatherGenerator.calculateGameTime(result);
@ -38,14 +35,15 @@ export class WeatherController
* Get the current in-raid time (MUST HAVE PLAYER LOGGED INTO CLIENT TO WORK) * Get the current in-raid time (MUST HAVE PLAYER LOGGED INTO CLIENT TO WORK)
* @returns Date object * @returns Date object
*/ */
public getCurrentInRaidTime(): Date public getCurrentInRaidTime(): Date {
{
return this.weatherGenerator.getInRaidTime(); return this.weatherGenerator.getInRaidTime();
} }
public generateLocal(sesssionID: string): IGetLocalWeatherResponseData public generateLocal(sesssionID: string): IGetLocalWeatherResponseData {
{ const result: IGetLocalWeatherResponseData = {
let result: IGetLocalWeatherResponseData = { season: this.seasonalEventService.getActiveWeatherSeason(), weather: [] }; season: this.seasonalEventService.getActiveWeatherSeason(),
weather: [],
};
result.weather.push(this.weatherGenerator.generateWeather()); result.weather.push(this.weatherGenerator.generateWeather());

View File

@ -1,22 +1,22 @@
import { inject, injectable } from "tsyringe";
import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest"; import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest";
import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest"; import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest";
import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest"; import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { inject, injectable } from "tsyringe";
@injectable() @injectable()
export class WishlistController export class WishlistController {
{ constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder) {}
constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder)
{}
/** Handle AddToWishList */ /** Handle AddToWishList */
public addToWishList(pmcData: IPmcData, request: IAddToWishlistRequest, sessionID: string): IItemEventRouterResponse public addToWishList(
{ pmcData: IPmcData,
for (const itemId of Object.keys(request.items)) request: IAddToWishlistRequest,
{ sessionID: string,
): IItemEventRouterResponse {
for (const itemId of Object.keys(request.items)) {
pmcData.WishList[itemId] = request.items[itemId]; pmcData.WishList[itemId] = request.items[itemId];
} }
@ -28,10 +28,8 @@ export class WishlistController
pmcData: IPmcData, pmcData: IPmcData,
request: IRemoveFromWishlistRequest, request: IRemoveFromWishlistRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{ for (const itemId of request.items) {
for (const itemId of request.items)
{
delete pmcData.WishList[itemId]; delete pmcData.WishList[itemId];
} }
@ -43,8 +41,7 @@ export class WishlistController
pmcData: IPmcData, pmcData: IPmcData,
request: IChangeWishlistItemCategoryRequest, request: IChangeWishlistItemCategoryRequest,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): IItemEventRouterResponse {
{
pmcData.WishList[request.item] = request.category; pmcData.WishList[request.item] = request.category;
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);

View File

@ -1,4 +1,3 @@
import { DependencyContainer, Lifecycle } from "tsyringe";
import { AchievementCallbacks } from "@spt/callbacks/AchievementCallbacks"; import { AchievementCallbacks } from "@spt/callbacks/AchievementCallbacks";
import { BotCallbacks } from "@spt/callbacks/BotCallbacks"; import { BotCallbacks } from "@spt/callbacks/BotCallbacks";
import { BuildsCallbacks } from "@spt/callbacks/BuildsCallbacks"; import { BuildsCallbacks } from "@spt/callbacks/BuildsCallbacks";
@ -70,18 +69,18 @@ import { BotWeaponGenerator } from "@spt/generators/BotWeaponGenerator";
import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator"; import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator";
import { LocationLootGenerator } from "@spt/generators/LocationLootGenerator"; import { LocationLootGenerator } from "@spt/generators/LocationLootGenerator";
import { LootGenerator } from "@spt/generators/LootGenerator"; import { LootGenerator } from "@spt/generators/LootGenerator";
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { PMCLootGenerator } from "@spt/generators/PMCLootGenerator"; import { PMCLootGenerator } from "@spt/generators/PMCLootGenerator";
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { RagfairAssortGenerator } from "@spt/generators/RagfairAssortGenerator"; import { RagfairAssortGenerator } from "@spt/generators/RagfairAssortGenerator";
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator"; import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator"; import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator";
import { RepeatableQuestRewardGenerator } from "@spt/generators/RepeatableQuestRewardGenerator"; import { RepeatableQuestRewardGenerator } from "@spt/generators/RepeatableQuestRewardGenerator";
import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator"; import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator";
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
import { BarrelInventoryMagGen } from "@spt/generators/weapongen/implementations/BarrelInventoryMagGen"; import { BarrelInventoryMagGen } from "@spt/generators/weapongen/implementations/BarrelInventoryMagGen";
import { ExternalInventoryMagGen } from "@spt/generators/weapongen/implementations/ExternalInventoryMagGen"; import { ExternalInventoryMagGen } from "@spt/generators/weapongen/implementations/ExternalInventoryMagGen";
import { InternalMagazineInventoryMagGen } from "@spt/generators/weapongen/implementations/InternalMagazineInventoryMagGen"; import { InternalMagazineInventoryMagGen } from "@spt/generators/weapongen/implementations/InternalMagazineInventoryMagGen";
import { UbglExternalMagGen } from "@spt/generators/weapongen/implementations/UbglExternalMagGen"; import { UbglExternalMagGen } from "@spt/generators/weapongen/implementations/UbglExternalMagGen";
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
import { AssortHelper } from "@spt/helpers/AssortHelper"; import { AssortHelper } from "@spt/helpers/AssortHelper";
import { BotDifficultyHelper } from "@spt/helpers/BotDifficultyHelper"; import { BotDifficultyHelper } from "@spt/helpers/BotDifficultyHelper";
import { BotGeneratorHelper } from "@spt/helpers/BotGeneratorHelper"; import { BotGeneratorHelper } from "@spt/helpers/BotGeneratorHelper";
@ -133,6 +132,10 @@ import { PostSptModLoader } from "@spt/loaders/PostSptModLoader";
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
import { IAsyncQueue } from "@spt/models/spt/utils/IAsyncQueue"; import { IAsyncQueue } from "@spt/models/spt/utils/IAsyncQueue";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { HttpRouter } from "@spt/routers/HttpRouter";
import { ImageRouter } from "@spt/routers/ImageRouter";
import { ItemEventRouter } from "@spt/routers/ItemEventRouter";
import { BotDynamicRouter } from "@spt/routers/dynamic/BotDynamicRouter"; import { BotDynamicRouter } from "@spt/routers/dynamic/BotDynamicRouter";
import { BundleDynamicRouter } from "@spt/routers/dynamic/BundleDynamicRouter"; import { BundleDynamicRouter } from "@spt/routers/dynamic/BundleDynamicRouter";
import { CustomizationDynamicRouter } from "@spt/routers/dynamic/CustomizationDynamicRouter"; import { CustomizationDynamicRouter } from "@spt/routers/dynamic/CustomizationDynamicRouter";
@ -142,9 +145,6 @@ import { InraidDynamicRouter } from "@spt/routers/dynamic/InraidDynamicRouter";
import { LocationDynamicRouter } from "@spt/routers/dynamic/LocationDynamicRouter"; import { LocationDynamicRouter } from "@spt/routers/dynamic/LocationDynamicRouter";
import { NotifierDynamicRouter } from "@spt/routers/dynamic/NotifierDynamicRouter"; import { NotifierDynamicRouter } from "@spt/routers/dynamic/NotifierDynamicRouter";
import { TraderDynamicRouter } from "@spt/routers/dynamic/TraderDynamicRouter"; import { TraderDynamicRouter } from "@spt/routers/dynamic/TraderDynamicRouter";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { HttpRouter } from "@spt/routers/HttpRouter";
import { ImageRouter } from "@spt/routers/ImageRouter";
import { CustomizationItemEventRouter } from "@spt/routers/item_events/CustomizationItemEventRouter"; import { CustomizationItemEventRouter } from "@spt/routers/item_events/CustomizationItemEventRouter";
import { HealthItemEventRouter } from "@spt/routers/item_events/HealthItemEventRouter"; import { HealthItemEventRouter } from "@spt/routers/item_events/HealthItemEventRouter";
import { HideoutItemEventRouter } from "@spt/routers/item_events/HideoutItemEventRouter"; import { HideoutItemEventRouter } from "@spt/routers/item_events/HideoutItemEventRouter";
@ -156,7 +156,6 @@ import { RagfairItemEventRouter } from "@spt/routers/item_events/RagfairItemEven
import { RepairItemEventRouter } from "@spt/routers/item_events/RepairItemEventRouter"; import { RepairItemEventRouter } from "@spt/routers/item_events/RepairItemEventRouter";
import { TradeItemEventRouter } from "@spt/routers/item_events/TradeItemEventRouter"; import { TradeItemEventRouter } from "@spt/routers/item_events/TradeItemEventRouter";
import { WishlistItemEventRouter } from "@spt/routers/item_events/WishlistItemEventRouter"; import { WishlistItemEventRouter } from "@spt/routers/item_events/WishlistItemEventRouter";
import { ItemEventRouter } from "@spt/routers/ItemEventRouter";
import { HealthSaveLoadRouter } from "@spt/routers/save_load/HealthSaveLoadRouter"; import { HealthSaveLoadRouter } from "@spt/routers/save_load/HealthSaveLoadRouter";
import { InraidSaveLoadRouter } from "@spt/routers/save_load/InraidSaveLoadRouter"; import { InraidSaveLoadRouter } from "@spt/routers/save_load/InraidSaveLoadRouter";
import { InsuranceSaveLoadRouter } from "@spt/routers/save_load/InsuranceSaveLoadRouter"; import { InsuranceSaveLoadRouter } from "@spt/routers/save_load/InsuranceSaveLoadRouter";
@ -188,23 +187,21 @@ import { TraderStaticRouter } from "@spt/routers/static/TraderStaticRouter";
import { WeatherStaticRouter } from "@spt/routers/static/WeatherStaticRouter"; import { WeatherStaticRouter } from "@spt/routers/static/WeatherStaticRouter";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { DatabaseServer } from "@spt/servers/DatabaseServer"; import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { SptHttpListener } from "@spt/servers/http/SptHttpListener";
import { HttpServer } from "@spt/servers/HttpServer"; import { HttpServer } from "@spt/servers/HttpServer";
import { RagfairServer } from "@spt/servers/RagfairServer"; import { RagfairServer } from "@spt/servers/RagfairServer";
import { SaveServer } from "@spt/servers/SaveServer"; import { SaveServer } from "@spt/servers/SaveServer";
import { WebSocketServer } from "@spt/servers/WebSocketServer"; import { WebSocketServer } from "@spt/servers/WebSocketServer";
import { SptHttpListener } from "@spt/servers/http/SptHttpListener";
import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler";
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
import { DefaultSptWebSocketMessageHandler } from "@spt/servers/ws/message/DefaultSptWebSocketMessageHandler"; import { DefaultSptWebSocketMessageHandler } from "@spt/servers/ws/message/DefaultSptWebSocketMessageHandler";
import { ISptWebSocketMessageHandler } from "@spt/servers/ws/message/ISptWebSocketMessageHandler"; import { ISptWebSocketMessageHandler } from "@spt/servers/ws/message/ISptWebSocketMessageHandler";
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
import { AirdropService } from "@spt/services/AirdropService"; import { AirdropService } from "@spt/services/AirdropService";
import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService"; import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService";
import { BotEquipmentModPoolService } from "@spt/services/BotEquipmentModPoolService"; import { BotEquipmentModPoolService } from "@spt/services/BotEquipmentModPoolService";
import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService"; import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService";
import { BotLootCacheService } from "@spt/services/BotLootCacheService"; import { BotLootCacheService } from "@spt/services/BotLootCacheService";
import { BotWeaponModLimitService } from "@spt/services/BotWeaponModLimitService"; import { BotWeaponModLimitService } from "@spt/services/BotWeaponModLimitService";
import { BundleHashCacheService } from "@spt/services/cache/BundleHashCacheService";
import { ModHashCacheService } from "@spt/services/cache/ModHashCacheService";
import { CustomLocationWaveService } from "@spt/services/CustomLocationWaveService"; import { CustomLocationWaveService } from "@spt/services/CustomLocationWaveService";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { FenceService } from "@spt/services/FenceService"; import { FenceService } from "@spt/services/FenceService";
@ -219,13 +216,6 @@ import { MailSendService } from "@spt/services/MailSendService";
import { MapMarkerService } from "@spt/services/MapMarkerService"; import { MapMarkerService } from "@spt/services/MapMarkerService";
import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService"; import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService";
import { MatchLocationService } from "@spt/services/MatchLocationService"; import { MatchLocationService } from "@spt/services/MatchLocationService";
import { CustomItemService } from "@spt/services/mod/CustomItemService";
import { DynamicRouterModService } from "@spt/services/mod/dynamicRouter/DynamicRouterModService";
import { HttpListenerModService } from "@spt/services/mod/httpListener/HttpListenerModService";
import { ImageRouteService } from "@spt/services/mod/image/ImageRouteService";
import { OnLoadModService } from "@spt/services/mod/onLoad/OnLoadModService";
import { OnUpdateModService } from "@spt/services/mod/onUpdate/OnUpdateModService";
import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService";
import { ModCompilerService } from "@spt/services/ModCompilerService"; import { ModCompilerService } from "@spt/services/ModCompilerService";
import { NotificationService } from "@spt/services/NotificationService"; import { NotificationService } from "@spt/services/NotificationService";
import { OpenZoneService } from "@spt/services/OpenZoneService"; import { OpenZoneService } from "@spt/services/OpenZoneService";
@ -246,12 +236,17 @@ import { RepairService } from "@spt/services/RepairService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService"; import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { TraderAssortService } from "@spt/services/TraderAssortService"; import { TraderAssortService } from "@spt/services/TraderAssortService";
import { TraderPurchasePersisterService } from "@spt/services/TraderPurchasePersisterService"; import { TraderPurchasePersisterService } from "@spt/services/TraderPurchasePersisterService";
import { BundleHashCacheService } from "@spt/services/cache/BundleHashCacheService";
import { ModHashCacheService } from "@spt/services/cache/ModHashCacheService";
import { CustomItemService } from "@spt/services/mod/CustomItemService";
import { DynamicRouterModService } from "@spt/services/mod/dynamicRouter/DynamicRouterModService";
import { HttpListenerModService } from "@spt/services/mod/httpListener/HttpListenerModService";
import { ImageRouteService } from "@spt/services/mod/image/ImageRouteService";
import { OnLoadModService } from "@spt/services/mod/onLoad/OnLoadModService";
import { OnUpdateModService } from "@spt/services/mod/onUpdate/OnUpdateModService";
import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService";
import { App } from "@spt/utils/App"; import { App } from "@spt/utils/App";
import { AsyncQueue } from "@spt/utils/AsyncQueue"; import { AsyncQueue } from "@spt/utils/AsyncQueue";
import type { ICloner } from "@spt/utils/cloners/ICloner";
import { JsonCloner } from "@spt/utils/cloners/JsonCloner";
import { RecursiveCloner } from "@spt/utils/cloners/RecursiveCloner";
import { StructuredCloner } from "@spt/utils/cloners/StructuredCloner";
import { CompareUtil } from "@spt/utils/CompareUtil"; import { CompareUtil } from "@spt/utils/CompareUtil";
import { DatabaseImporter } from "@spt/utils/DatabaseImporter"; import { DatabaseImporter } from "@spt/utils/DatabaseImporter";
import { EncodingUtil } from "@spt/utils/EncodingUtil"; import { EncodingUtil } from "@spt/utils/EncodingUtil";
@ -260,28 +255,30 @@ import { HttpFileUtil } from "@spt/utils/HttpFileUtil";
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
import { ImporterUtil } from "@spt/utils/ImporterUtil"; import { ImporterUtil } from "@spt/utils/ImporterUtil";
import { JsonUtil } from "@spt/utils/JsonUtil"; import { JsonUtil } from "@spt/utils/JsonUtil";
import { WinstonMainLogger } from "@spt/utils/logging/WinstonMainLogger";
import { WinstonRequestLogger } from "@spt/utils/logging/WinstonRequestLogger";
import { MathUtil } from "@spt/utils/MathUtil"; import { MathUtil } from "@spt/utils/MathUtil";
import { ObjectId } from "@spt/utils/ObjectId"; import { ObjectId } from "@spt/utils/ObjectId";
import { RandomUtil } from "@spt/utils/RandomUtil"; import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { VFS } from "@spt/utils/VFS"; import { VFS } from "@spt/utils/VFS";
import { Watermark, WatermarkLocale } from "@spt/utils/Watermark"; import { Watermark, WatermarkLocale } from "@spt/utils/Watermark";
import type { ICloner } from "@spt/utils/cloners/ICloner";
import { JsonCloner } from "@spt/utils/cloners/JsonCloner";
import { RecursiveCloner } from "@spt/utils/cloners/RecursiveCloner";
import { StructuredCloner } from "@spt/utils/cloners/StructuredCloner";
import { WinstonMainLogger } from "@spt/utils/logging/WinstonMainLogger";
import { WinstonRequestLogger } from "@spt/utils/logging/WinstonRequestLogger";
import { DependencyContainer, Lifecycle } from "tsyringe";
/** /**
* Handle the registration of classes to be used by the Dependency Injection code * Handle the registration of classes to be used by the Dependency Injection code
*/ */
export class Container export class Container {
{ public static registerPostLoadTypes(container: DependencyContainer, childContainer: DependencyContainer): void {
public static registerPostLoadTypes(container: DependencyContainer, childContainer: DependencyContainer): void
{
container.register<SptHttpListener>("SptHttpListener", SptHttpListener, { lifecycle: Lifecycle.Singleton }); container.register<SptHttpListener>("SptHttpListener", SptHttpListener, { lifecycle: Lifecycle.Singleton });
childContainer.registerType("HttpListener", "SptHttpListener"); childContainer.registerType("HttpListener", "SptHttpListener");
} }
public static registerTypes(depContainer: DependencyContainer): void public static registerTypes(depContainer: DependencyContainer): void {
{
depContainer.register("ApplicationContext", ApplicationContext, { lifecycle: Lifecycle.Singleton }); depContainer.register("ApplicationContext", ApplicationContext, { lifecycle: Lifecycle.Singleton });
Container.registerUtils(depContainer); Container.registerUtils(depContainer);
@ -304,16 +301,20 @@ export class Container
Container.registerPrimaryDependencies(depContainer); Container.registerPrimaryDependencies(depContainer);
} }
public static registerPrimaryDependencies(depContainer: DependencyContainer): void public static registerPrimaryDependencies(depContainer: DependencyContainer): void {
{ depContainer.register<ILogger>(
depContainer.register<ILogger>("PrimaryLogger", { useToken: "WinstonLogger" }, "PrimaryLogger",
{ lifecycle: Lifecycle.Singleton }); { useToken: "WinstonLogger" },
depContainer.register<ICloner>("PrimaryCloner", { useToken: "RecursiveCloner" }, { lifecycle: Lifecycle.Singleton },
{ lifecycle: Lifecycle.Singleton }); );
depContainer.register<ICloner>(
"PrimaryCloner",
{ useToken: "RecursiveCloner" },
{ lifecycle: Lifecycle.Singleton },
);
} }
public static registerListTypes(depContainer: DependencyContainer): void public static registerListTypes(depContainer: DependencyContainer): void {
{
depContainer.register("OnLoadModService", { useValue: new OnLoadModService(depContainer) }); depContainer.register("OnLoadModService", { useValue: new OnLoadModService(depContainer) });
depContainer.register("HttpListenerModService", { useValue: new HttpListenerModService(depContainer) }); depContainer.register("HttpListenerModService", { useValue: new HttpListenerModService(depContainer) });
depContainer.register("OnUpdateModService", { useValue: new OnUpdateModService(depContainer) }); depContainer.register("OnUpdateModService", { useValue: new OnUpdateModService(depContainer) });
@ -409,8 +410,7 @@ export class Container
depContainer.registerType("SptWebSocketMessageHandler", "DefaultSptWebSocketMessageHandler"); depContainer.registerType("SptWebSocketMessageHandler", "DefaultSptWebSocketMessageHandler");
} }
private static registerUtils(depContainer: DependencyContainer): void private static registerUtils(depContainer: DependencyContainer): void {
{
// Utils // Utils
depContainer.register<App>("App", App, { lifecycle: Lifecycle.Singleton }); depContainer.register<App>("App", App, { lifecycle: Lifecycle.Singleton });
depContainer.register<DatabaseImporter>("DatabaseImporter", DatabaseImporter, { depContainer.register<DatabaseImporter>("DatabaseImporter", DatabaseImporter, {
@ -444,8 +444,7 @@ export class Container
depContainer.register<ICloner>("RecursiveCloner", RecursiveCloner, { lifecycle: Lifecycle.Singleton }); depContainer.register<ICloner>("RecursiveCloner", RecursiveCloner, { lifecycle: Lifecycle.Singleton });
} }
private static registerRouters(depContainer: DependencyContainer): void private static registerRouters(depContainer: DependencyContainer): void {
{
// Routers // Routers
depContainer.register<HttpRouter>("HttpRouter", HttpRouter, { lifecycle: Lifecycle.Singleton }); depContainer.register<HttpRouter>("HttpRouter", HttpRouter, { lifecycle: Lifecycle.Singleton });
depContainer.register<ImageRouter>("ImageRouter", ImageRouter); depContainer.register<ImageRouter>("ImageRouter", ImageRouter);
@ -530,8 +529,7 @@ export class Container
depContainer.register<BuildsStaticRouter>("BuildsStaticRouter", { useClass: BuildsStaticRouter }); depContainer.register<BuildsStaticRouter>("BuildsStaticRouter", { useClass: BuildsStaticRouter });
} }
private static registerGenerators(depContainer: DependencyContainer): void private static registerGenerators(depContainer: DependencyContainer): void {
{
// Generators // Generators
depContainer.register<BotGenerator>("BotGenerator", BotGenerator); depContainer.register<BotGenerator>("BotGenerator", BotGenerator);
depContainer.register<BotWeaponGenerator>("BotWeaponGenerator", BotWeaponGenerator); depContainer.register<BotWeaponGenerator>("BotWeaponGenerator", BotWeaponGenerator);
@ -578,8 +576,7 @@ export class Container
depContainer.registerType("InventoryMagGen", "UbglExternalMagGen"); depContainer.registerType("InventoryMagGen", "UbglExternalMagGen");
} }
private static registerHelpers(depContainer: DependencyContainer): void private static registerHelpers(depContainer: DependencyContainer): void {
{
// Helpers // Helpers
depContainer.register<AssortHelper>("AssortHelper", { useClass: AssortHelper }); depContainer.register<AssortHelper>("AssortHelper", { useClass: AssortHelper });
depContainer.register<BotHelper>("BotHelper", { useClass: BotHelper }); depContainer.register<BotHelper>("BotHelper", { useClass: BotHelper });
@ -642,8 +639,7 @@ export class Container
}); });
} }
private static registerLoaders(depContainer: DependencyContainer): void private static registerLoaders(depContainer: DependencyContainer): void {
{
// Loaders // Loaders
depContainer.register<BundleLoader>("BundleLoader", BundleLoader, { lifecycle: Lifecycle.Singleton }); depContainer.register<BundleLoader>("BundleLoader", BundleLoader, { lifecycle: Lifecycle.Singleton });
depContainer.register<PreSptModLoader>("PreSptModLoader", PreSptModLoader, { lifecycle: Lifecycle.Singleton }); depContainer.register<PreSptModLoader>("PreSptModLoader", PreSptModLoader, { lifecycle: Lifecycle.Singleton });
@ -652,8 +648,7 @@ export class Container
}); });
} }
private static registerCallbacks(depContainer: DependencyContainer): void private static registerCallbacks(depContainer: DependencyContainer): void {
{
// Callbacks // Callbacks
depContainer.register<BotCallbacks>("BotCallbacks", { useClass: BotCallbacks }); depContainer.register<BotCallbacks>("BotCallbacks", { useClass: BotCallbacks });
depContainer.register<BundleCallbacks>("BundleCallbacks", { useClass: BundleCallbacks }); depContainer.register<BundleCallbacks>("BundleCallbacks", { useClass: BundleCallbacks });
@ -691,8 +686,7 @@ export class Container
depContainer.register<BuildsCallbacks>("BuildsCallbacks", { useClass: BuildsCallbacks }); depContainer.register<BuildsCallbacks>("BuildsCallbacks", { useClass: BuildsCallbacks });
} }
private static registerServices(depContainer: DependencyContainer): void private static registerServices(depContainer: DependencyContainer): void {
{
// Services // Services
depContainer.register<DatabaseService>("DatabaseService", DatabaseService, { lifecycle: Lifecycle.Singleton }); depContainer.register<DatabaseService>("DatabaseService", DatabaseService, { lifecycle: Lifecycle.Singleton });
depContainer.register<ImageRouteService>("ImageRouteService", ImageRouteService, { depContainer.register<ImageRouteService>("ImageRouteService", ImageRouteService, {
@ -800,21 +794,27 @@ export class Container
}); });
} }
private static registerServers(depContainer: DependencyContainer): void private static registerServers(depContainer: DependencyContainer): void {
{
// Servers // Servers
depContainer.register<DatabaseServer>("DatabaseServer", DatabaseServer, { lifecycle: Lifecycle.Singleton }); depContainer.register<DatabaseServer>("DatabaseServer", DatabaseServer, { lifecycle: Lifecycle.Singleton });
depContainer.register<HttpServer>("HttpServer", HttpServer, { lifecycle: Lifecycle.Singleton }); depContainer.register<HttpServer>("HttpServer", HttpServer, { lifecycle: Lifecycle.Singleton });
depContainer.register<WebSocketServer>("WebSocketServer", WebSocketServer, { lifecycle: Lifecycle.Singleton }); depContainer.register<WebSocketServer>("WebSocketServer", WebSocketServer, { lifecycle: Lifecycle.Singleton });
depContainer.register<IWebSocketConnectionHandler>("SptWebSocketConnectionHandler", SptWebSocketConnectionHandler, { lifecycle: Lifecycle.Singleton }); depContainer.register<IWebSocketConnectionHandler>(
depContainer.register<ISptWebSocketMessageHandler>("DefaultSptWebSocketMessageHandler", DefaultSptWebSocketMessageHandler, { lifecycle: Lifecycle.Singleton }); "SptWebSocketConnectionHandler",
SptWebSocketConnectionHandler,
{ lifecycle: Lifecycle.Singleton },
);
depContainer.register<ISptWebSocketMessageHandler>(
"DefaultSptWebSocketMessageHandler",
DefaultSptWebSocketMessageHandler,
{ lifecycle: Lifecycle.Singleton },
);
depContainer.register<RagfairServer>("RagfairServer", RagfairServer); depContainer.register<RagfairServer>("RagfairServer", RagfairServer);
depContainer.register<SaveServer>("SaveServer", SaveServer, { lifecycle: Lifecycle.Singleton }); depContainer.register<SaveServer>("SaveServer", SaveServer, { lifecycle: Lifecycle.Singleton });
depContainer.register<ConfigServer>("ConfigServer", ConfigServer, { lifecycle: Lifecycle.Singleton }); depContainer.register<ConfigServer>("ConfigServer", ConfigServer, { lifecycle: Lifecycle.Singleton });
} }
private static registerControllers(depContainer: DependencyContainer): void private static registerControllers(depContainer: DependencyContainer): void {
{
// Controllers // Controllers
depContainer.register<BotController>("BotController", { useClass: BotController }); depContainer.register<BotController>("BotController", { useClass: BotController });
depContainer.register<ClientLogController>("ClientLogController", { useClass: ClientLogController }); depContainer.register<ClientLogController>("ClientLogController", { useClass: ClientLogController });

View File

@ -1,5 +1,4 @@
export interface OnLoad export interface OnLoad {
{ onLoad(): Promise<void>;
onLoad(): Promise<void> getRoute(): string;
getRoute(): string
} }

View File

@ -1,5 +1,4 @@
export interface OnUpdate export interface OnUpdate {
{ onUpdate(timeSinceLastRun: number): Promise<boolean>;
onUpdate(timeSinceLastRun: number): Promise<boolean> getRoute(): string;
getRoute(): string
} }

View File

@ -2,33 +2,26 @@ import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
export class Router export class Router {
{
protected handledRoutes: HandledRoute[] = []; protected handledRoutes: HandledRoute[] = [];
public getTopLevelRoute(): string public getTopLevelRoute(): string {
{
return "spt"; return "spt";
} }
protected getHandledRoutes(): HandledRoute[] protected getHandledRoutes(): HandledRoute[] {
{
throw new Error("This method needs to be overrode by the router classes"); throw new Error("This method needs to be overrode by the router classes");
} }
protected getInternalHandledRoutes(): HandledRoute[] protected getInternalHandledRoutes(): HandledRoute[] {
{ if (this.handledRoutes.length === 0) {
if (this.handledRoutes.length === 0)
{
this.handledRoutes = this.getHandledRoutes(); this.handledRoutes = this.getHandledRoutes();
} }
return this.handledRoutes; return this.handledRoutes;
} }
public canHandle(url: string, partialMatch = false): boolean public canHandle(url: string, partialMatch = false): boolean {
{ if (partialMatch) {
if (partialMatch)
{
return this.getInternalHandledRoutes() return this.getInternalHandledRoutes()
.filter((r) => r.dynamic) .filter((r) => r.dynamic)
.some((r) => url.includes(r.route)); .some((r) => url.includes(r.route));
@ -39,80 +32,64 @@ export class Router
} }
} }
export class StaticRouter extends Router export class StaticRouter extends Router {
{ constructor(private routes: RouteAction[]) {
constructor(private routes: RouteAction[])
{
super(); super();
} }
public async handleStatic(url: string, info: any, sessionID: string, output: string): Promise<any> public async handleStatic(url: string, info: any, sessionID: string, output: string): Promise<any> {
{
return this.routes.find((route) => route.url === url).action(url, info, sessionID, output); return this.routes.find((route) => route.url === url).action(url, info, sessionID, output);
} }
public override getHandledRoutes(): HandledRoute[] public override getHandledRoutes(): HandledRoute[] {
{
return this.routes.map((route) => new HandledRoute(route.url, false)); return this.routes.map((route) => new HandledRoute(route.url, false));
} }
} }
export class DynamicRouter extends Router export class DynamicRouter extends Router {
{ constructor(private routes: RouteAction[]) {
constructor(private routes: RouteAction[])
{
super(); super();
} }
public async handleDynamic(url: string, info: any, sessionID: string, output: string): Promise<any> public async handleDynamic(url: string, info: any, sessionID: string, output: string): Promise<any> {
{
return this.routes.find((r) => url.includes(r.url)).action(url, info, sessionID, output); return this.routes.find((r) => url.includes(r.url)).action(url, info, sessionID, output);
} }
public override getHandledRoutes(): HandledRoute[] public override getHandledRoutes(): HandledRoute[] {
{
return this.routes.map((route) => new HandledRoute(route.url, true)); return this.routes.map((route) => new HandledRoute(route.url, true));
} }
} }
// The name of this class should be ItemEventRouter, but that name is taken, // The name of this class should be ItemEventRouter, but that name is taken,
// So instead I added the definition // So instead I added the definition
export class ItemEventRouterDefinition extends Router export class ItemEventRouterDefinition extends Router {
{
public async handleItemEvent( public async handleItemEvent(
url: string, url: string,
pmcData: IPmcData, pmcData: IPmcData,
body: any, body: any,
sessionID: string, sessionID: string,
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): Promise<any> ): Promise<any> {
{
throw new Error("This method needs to be overrode by the router classes"); throw new Error("This method needs to be overrode by the router classes");
} }
} }
export class SaveLoadRouter extends Router export class SaveLoadRouter extends Router {
{ public handleLoad(profile: ISptProfile): ISptProfile {
public handleLoad(profile: ISptProfile): ISptProfile
{
throw new Error("This method needs to be overrode by the router classes"); throw new Error("This method needs to be overrode by the router classes");
} }
} }
export class HandledRoute export class HandledRoute {
{
constructor( constructor(
public route: string, public route: string,
public dynamic: boolean, public dynamic: boolean,
) ) {}
{}
} }
export class RouteAction export class RouteAction {
{
constructor( constructor(
public url: string, public url: string,
public action: (url: string, info: any, sessionID: string, output: string) => Promise<any>, public action: (url: string, info: any, sessionID: string, output: string) => Promise<any>,
) ) {}
{}
} }

View File

@ -1,14 +1,11 @@
import { IncomingMessage, ServerResponse } from "node:http"; import { IncomingMessage, ServerResponse } from "node:http";
export class Serializer export class Serializer {
{ public serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void {
public serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void
{
throw new Error("Should be extended and overrode"); throw new Error("Should be extended and overrode");
} }
public canHandle(something: string): boolean public canHandle(something: string): boolean {
{
throw new Error("Should be extended and overrode"); throw new Error("Should be extended and overrode");
} }
} }

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More