mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-12 13:50:42 -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:
parent
7c76342ee2
commit
5740774a46
@ -15,14 +15,10 @@
|
||||
"keepClassNames": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@spt/*": [
|
||||
"src/*"
|
||||
]
|
||||
"@spt/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules/"
|
||||
],
|
||||
"exclude": ["node_modules/"],
|
||||
"module": {
|
||||
"type": "commonjs",
|
||||
"strict": false,
|
||||
|
@ -104,12 +104,7 @@
|
||||
"max": 50
|
||||
}
|
||||
},
|
||||
"armorLevelWhitelist": [
|
||||
0,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
],
|
||||
"armorLevelWhitelist": [0, 4, 5, 6],
|
||||
"allowBossItems": false
|
||||
},
|
||||
"weaponArmor": {
|
||||
@ -181,13 +176,7 @@
|
||||
"max": 50
|
||||
}
|
||||
},
|
||||
"armorLevelWhitelist": [
|
||||
0,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
],
|
||||
"armorLevelWhitelist": [0, 3, 4, 5, 6],
|
||||
"allowBossItems": false
|
||||
},
|
||||
"foodMedical": {
|
||||
@ -267,9 +256,7 @@
|
||||
"max": 50
|
||||
}
|
||||
},
|
||||
"armorLevelWhitelist": [
|
||||
0
|
||||
],
|
||||
"armorLevelWhitelist": [0],
|
||||
"allowBossItems": false
|
||||
},
|
||||
"barter": {
|
||||
@ -350,10 +337,8 @@
|
||||
"max": 50
|
||||
}
|
||||
},
|
||||
"armorLevelWhitelist": [
|
||||
0
|
||||
],
|
||||
"armorLevelWhitelist": [0],
|
||||
"allowBossItems": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,8 @@
|
||||
"sectactPriestEvent": 10,
|
||||
"pmcUSEC": 15,
|
||||
"pmcBEAR": 15,
|
||||
"peacemaker": 10,
|
||||
"skier": 10
|
||||
"peacemaker": 10,
|
||||
"skier": 10
|
||||
},
|
||||
"bosses": [
|
||||
"bossBully",
|
||||
@ -277,12 +277,9 @@
|
||||
},
|
||||
"chanceAssaultScavHasPlayerScavName": 10,
|
||||
"secureContainerAmmoStackCount": 20,
|
||||
"botRolesWithDogTags": [
|
||||
"pmcbear",
|
||||
"pmcusec"
|
||||
],
|
||||
"botRolesWithDogTags": ["pmcbear", "pmcusec"],
|
||||
"revenge": {
|
||||
"pmcBot": ["pmcBot","gifter"],
|
||||
"pmcBot": ["pmcBot", "gifter"],
|
||||
"arenaFighter": ["pmcBot", "gifter"],
|
||||
"arenaFighterEvent": ["pmcBot", "gifter"],
|
||||
"bossKnight": ["exUsec", "gifter", "bossKnight", "followerBigPipe", "followerBirdEye"],
|
||||
@ -430,8 +427,8 @@
|
||||
"544fb37f4bdc2dee738b4567": 2
|
||||
},
|
||||
"shooterbtr": {},
|
||||
"skier": {},
|
||||
"peacemaker": {}
|
||||
"skier": {},
|
||||
"peacemaker": {}
|
||||
},
|
||||
"equipment": {
|
||||
"assault": {
|
||||
@ -495,9 +492,7 @@
|
||||
"lightIsActiveNightChancePercent": 90,
|
||||
"laserIsActiveChancePercent": 85,
|
||||
"forceStock": true,
|
||||
"weaponSlotIdsToMakeRequired": [
|
||||
"mod_reciever"
|
||||
]
|
||||
"weaponSlotIdsToMakeRequired": ["mod_reciever"]
|
||||
},
|
||||
"bossbully": {
|
||||
"nvgIsActiveChanceDayPercent": 10,
|
||||
@ -776,11 +771,9 @@
|
||||
"scopeLimit": 1,
|
||||
"lightLaserLimit": 1
|
||||
},
|
||||
"weaponSlotIdsToMakeRequired": [
|
||||
"mod_reciever"
|
||||
]
|
||||
"weaponSlotIdsToMakeRequired": ["mod_reciever"]
|
||||
},
|
||||
"peacemaker": {
|
||||
"peacemaker": {
|
||||
"nvgIsActiveChanceDayPercent": 10,
|
||||
"nvgIsActiveChanceNightPercent": 95,
|
||||
"faceShieldIsActiveChancePercent": 100,
|
||||
@ -793,7 +786,7 @@
|
||||
"lightLaserLimit": 1
|
||||
}
|
||||
},
|
||||
"skier": {
|
||||
"skier": {
|
||||
"nvgIsActiveChanceDayPercent": 10,
|
||||
"nvgIsActiveChanceNightPercent": 95,
|
||||
"faceShieldIsActiveChancePercent": 100,
|
||||
@ -926,11 +919,7 @@
|
||||
"laserIsActiveChancePercent": 85,
|
||||
"forceOnlyArmoredRigWhenNoArmor": true,
|
||||
"filterPlatesByLevel": true,
|
||||
"weaponSlotIdsToMakeRequired": [
|
||||
"mod_reciever",
|
||||
"mod_stock",
|
||||
"mod_muzzle"
|
||||
],
|
||||
"weaponSlotIdsToMakeRequired": ["mod_reciever", "mod_stock", "mod_muzzle"],
|
||||
"randomisation": [
|
||||
{
|
||||
"levelRange": {
|
||||
@ -1148,11 +1137,7 @@
|
||||
"Scabbard": 100,
|
||||
"TacticalVest": 90
|
||||
},
|
||||
"randomisedArmorSlots": [
|
||||
"Headwear",
|
||||
"ArmorVest",
|
||||
"TacticalVest"
|
||||
],
|
||||
"randomisedArmorSlots": ["Headwear", "ArmorVest", "TacticalVest"],
|
||||
"randomisedWeaponModSlots": [
|
||||
"mod_scope",
|
||||
"mod_scope_000",
|
||||
@ -1250,11 +1235,7 @@
|
||||
"SecondPrimaryWeapon": 10,
|
||||
"FaceCover": 50
|
||||
},
|
||||
"randomisedArmorSlots": [
|
||||
"Headwear",
|
||||
"TacticalVest",
|
||||
"ArmorVest"
|
||||
],
|
||||
"randomisedArmorSlots": ["Headwear", "TacticalVest", "ArmorVest"],
|
||||
"randomisedWeaponModSlots": [
|
||||
"mod_scope",
|
||||
"mod_scope_000",
|
||||
@ -1321,11 +1302,7 @@
|
||||
"mod_muzzle_000": 95,
|
||||
"mod_muzzle_001": 95
|
||||
},
|
||||
"randomisedArmorSlots": [
|
||||
"Headwear",
|
||||
"TacticalVest",
|
||||
"ArmorVest"
|
||||
],
|
||||
"randomisedArmorSlots": ["Headwear", "TacticalVest", "ArmorVest"],
|
||||
"randomisedWeaponModSlots": [
|
||||
"mod_scope",
|
||||
"mod_scope_000",
|
||||
@ -1397,30 +1374,15 @@
|
||||
"65392f611406374f82152ba5",
|
||||
"653931da5db71d30ab1d6296"
|
||||
],
|
||||
"mod_nvg": [
|
||||
"5c11046cd174af02a012e42b"
|
||||
],
|
||||
"mod_reciever": [
|
||||
"5d4405aaa4b9361e6a4e6bd3"
|
||||
],
|
||||
"mod_stock": [
|
||||
"5cde739cd7f00c0010373bd3"
|
||||
],
|
||||
"mod_rear_sight": [
|
||||
"5a0ed824fcdbcb0176308b0d"
|
||||
],
|
||||
"mod_front_sight": [
|
||||
"5a0f096dfcdbcb0176308b15"
|
||||
]
|
||||
"mod_nvg": ["5c11046cd174af02a012e42b"],
|
||||
"mod_reciever": ["5d4405aaa4b9361e6a4e6bd3"],
|
||||
"mod_stock": ["5cde739cd7f00c0010373bd3"],
|
||||
"mod_rear_sight": ["5a0ed824fcdbcb0176308b0d"],
|
||||
"mod_front_sight": ["5a0f096dfcdbcb0176308b15"]
|
||||
},
|
||||
"cartridge": {
|
||||
"Caliber23x75": [
|
||||
"5e85a9f4add9fe03027d9bf1"
|
||||
],
|
||||
"Caliber127x55": [
|
||||
"5cadf6e5ae921500113bb973",
|
||||
"5cadf6ddae9215051e1c23b2"
|
||||
]
|
||||
"Caliber23x75": ["5e85a9f4add9fe03027d9bf1"],
|
||||
"Caliber127x55": ["5cadf6e5ae921500113bb973", "5cadf6ddae9215051e1c23b2"]
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -2677,10 +2639,7 @@
|
||||
"569668774bdc2da2298b4568": 0,
|
||||
"5696686a4bdc2da3298b456a": 0
|
||||
},
|
||||
"walletTplPool": [
|
||||
"5783c43d2459774bbe137486",
|
||||
"60b0f6c058e0b0481a09ad11"
|
||||
]
|
||||
"walletTplPool": ["5783c43d2459774bbe137486", "60b0f6c058e0b0481a09ad11"]
|
||||
},
|
||||
"currencyStackSize": {
|
||||
"default": {
|
||||
@ -2705,7 +2664,7 @@
|
||||
"1": 8,
|
||||
"2": 4,
|
||||
"5": 4,
|
||||
"10": 1
|
||||
"10": 1
|
||||
}
|
||||
},
|
||||
"assault": {
|
||||
@ -2735,7 +2694,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"lowProfileGasBlockTpls": ["61702f1b67085e45ef140b26", "5dfa3d45dfc58d14537c20b0", "5bb20dcad4351e3bac1212da", "56eabcd4d2720b66698b4574", "6065dc8a132d4d12c81fd8e3", "55d4af3a4bdc2d972f8b456f"],
|
||||
"lowProfileGasBlockTpls": [
|
||||
"61702f1b67085e45ef140b26",
|
||||
"5dfa3d45dfc58d14537c20b0",
|
||||
"5bb20dcad4351e3bac1212da",
|
||||
"56eabcd4d2720b66698b4574",
|
||||
"6065dc8a132d4d12c81fd8e3",
|
||||
"55d4af3a4bdc2d972f8b456f"
|
||||
],
|
||||
"disableLootOnBotTypes": [],
|
||||
"assaultToBossConversion": {
|
||||
"bossConvertEnabled": false,
|
||||
@ -2758,4 +2724,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,11 @@
|
||||
"commandoFeatures": {
|
||||
"giveCommandEnabled": true
|
||||
},
|
||||
"commandUseLimits": {
|
||||
"StashRows": 15
|
||||
}
|
||||
"commandUseLimits": {
|
||||
"StashRows": 15
|
||||
}
|
||||
},
|
||||
"createNewProfileTypesBlacklist": []
|
||||
"createNewProfileTypesBlacklist": []
|
||||
},
|
||||
"customWatermarkLocaleKeys": []
|
||||
"customWatermarkLocaleKeys": []
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,4 +7,4 @@
|
||||
"health": true,
|
||||
"effects": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,5 @@
|
||||
"expCraftAmount": 10,
|
||||
"overrideCraftTimeSeconds": -1,
|
||||
"overrideBuildTimeSeconds": -1,
|
||||
"updateProfileHideoutWhenActiveWithinMinutes": 90
|
||||
}
|
||||
"updateProfileHideoutWhenActiveWithinMinutes": 90
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"port": 6969,
|
||||
"backendIp": "127.0.0.1",
|
||||
"backendPort": 6969,
|
||||
"backendIp": "127.0.0.1",
|
||||
"backendPort": 6969,
|
||||
"webSocketPingDelayMs": 90000,
|
||||
"logRequests": true,
|
||||
"serverImagePathOverride": {}
|
||||
}
|
||||
}
|
||||
|
@ -13,15 +13,7 @@
|
||||
"save": {
|
||||
"loot": true
|
||||
},
|
||||
"carExtracts": [
|
||||
"Dorms V-Ex",
|
||||
"PP Exfil",
|
||||
"V-Ex_light",
|
||||
"South V-Ex",
|
||||
"E7_car",
|
||||
"Sandbox_VExit",
|
||||
"Shorl_V-Ex"
|
||||
],
|
||||
"carExtracts": ["Dorms V-Ex", "PP Exfil", "V-Ex_light", "South V-Ex", "E7_car", "Sandbox_VExit", "Shorl_V-Ex"],
|
||||
"coopExtracts": [
|
||||
"Interchange Cooperation",
|
||||
"tunnel_shared",
|
||||
@ -37,4 +29,4 @@
|
||||
"pmcKillProbabilityForScavGain": 0.2,
|
||||
"keepFiRSecureContainerOnDeath": false,
|
||||
"playerScavHostileChancePercent": 15
|
||||
}
|
||||
}
|
||||
|
@ -3,17 +3,10 @@
|
||||
"54cb50c76803fa8b248b4571": 75,
|
||||
"54cb57776803fa99248b456e": 85
|
||||
},
|
||||
"blacklistedEquipment": [
|
||||
"SpecialSlot1",
|
||||
"SpecialSlot2",
|
||||
"SpecialSlot3"
|
||||
],
|
||||
"slotIdsToAlwaysRemove": [
|
||||
"cartridges",
|
||||
"patron_in_weapon"
|
||||
],
|
||||
"blacklistedEquipment": ["SpecialSlot1", "SpecialSlot2", "SpecialSlot3"],
|
||||
"slotIdsToAlwaysRemove": ["cartridges", "patron_in_weapon"],
|
||||
"returnTimeOverrideSeconds": 0,
|
||||
"runIntervalSeconds": 600,
|
||||
"minAttachmentRoublePriceToBeTaken": 2000,
|
||||
"chanceNoAttachmentsTakenPercent": 10
|
||||
}
|
||||
}
|
||||
|
@ -49,10 +49,7 @@
|
||||
"6241c2c2117ad530666a5108",
|
||||
"5580239d4bdc2de7118b4583"
|
||||
],
|
||||
"lootableItemBlacklist": [
|
||||
"660bbc47c38b837877075e47",
|
||||
"660bc341c38b837877075e4c"
|
||||
],
|
||||
"lootableItemBlacklist": ["660bbc47c38b837877075e47", "660bc341c38b837877075e4c"],
|
||||
"rewardItemBlacklist": [
|
||||
"58ac60eb86f77401897560ff",
|
||||
"5e997f0b86f7741ac73993e2",
|
||||
@ -101,7 +98,7 @@
|
||||
"6662e9f37fa79a6d83730fa0",
|
||||
"6662ea05f6259762c56f3189",
|
||||
"6638a5474e92f038531e210e",
|
||||
"65ddcc9cfa85b9f17d0dfb07"
|
||||
"65ddcc9cfa85b9f17d0dfb07"
|
||||
],
|
||||
"bossItems": [
|
||||
"6275303a9f372d6ea97f9ec7",
|
||||
@ -148,4 +145,4 @@
|
||||
"63a898a328e385334e0640a5": 5000,
|
||||
"63a897c6b1ff6e29734fcc95": 10000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,4 +35,4 @@
|
||||
"zh-*": "zh-cn",
|
||||
"es-*": "es-es"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
"tarkovstreets": 3,
|
||||
"terminal": 1,
|
||||
"sandbox": 2.8,
|
||||
"sandbox_high": 2.8,
|
||||
"sandbox_high": 2.8,
|
||||
"town": 0
|
||||
},
|
||||
"staticLootMultiplier": {
|
||||
@ -36,7 +36,7 @@
|
||||
"tarkovstreets": 1,
|
||||
"terminal": 1,
|
||||
"sandbox": 1,
|
||||
"sandbox_high": 1,
|
||||
"sandbox_high": 1,
|
||||
"town": 1
|
||||
},
|
||||
"customWaves": {
|
||||
@ -56,9 +56,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -79,9 +77,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -104,9 +100,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -127,9 +121,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -152,9 +144,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -175,9 +165,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -198,9 +186,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -221,9 +207,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -244,9 +228,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -267,9 +249,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -292,9 +272,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -315,9 +293,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -338,9 +314,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -361,9 +335,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -384,9 +356,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -407,9 +377,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -432,9 +400,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -455,9 +421,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -478,9 +442,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "0"
|
||||
}
|
||||
],
|
||||
@ -501,9 +463,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -524,9 +484,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -547,9 +505,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "0"
|
||||
}
|
||||
],
|
||||
@ -572,9 +528,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -595,9 +549,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -620,9 +572,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -643,9 +593,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -668,9 +616,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -691,9 +637,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -714,9 +658,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -737,9 +679,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "1"
|
||||
}
|
||||
],
|
||||
@ -762,9 +702,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -785,9 +723,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -795,7 +731,7 @@
|
||||
"ChanceGroup": 0
|
||||
}
|
||||
],
|
||||
"sandbox_high": [
|
||||
"sandbox_high": [
|
||||
{
|
||||
"sptId": "pmcBEARSandboxNormalSpawn",
|
||||
"BossName": "pmcBEAR",
|
||||
@ -810,9 +746,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcBEAR",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -833,9 +767,7 @@
|
||||
"Supports": [
|
||||
{
|
||||
"BossEscortType": "pmcUSEC",
|
||||
"BossEscortDifficult": [
|
||||
"normal"
|
||||
],
|
||||
"BossEscortDifficult": ["normal"],
|
||||
"BossEscortAmount": "2"
|
||||
}
|
||||
],
|
||||
@ -1047,36 +979,29 @@
|
||||
}
|
||||
},
|
||||
"openZones": {
|
||||
"bigmap": [
|
||||
"ZoneScavBase"
|
||||
]
|
||||
"bigmap": ["ZoneScavBase"]
|
||||
},
|
||||
"forcedLootSingleSpawnById": {
|
||||
"bigmap": [
|
||||
"5ac620eb86f7743a8e6e0da0",
|
||||
"5939e5a786f77461f11c0098",
|
||||
"64e74a3d4d49d23b2c39d319",
|
||||
"6614230055afee107f05e998"
|
||||
],
|
||||
"interchange": [
|
||||
"64e74a5ac2b4f829615ec336"
|
||||
"6614230055afee107f05e998"
|
||||
],
|
||||
"interchange": ["64e74a5ac2b4f829615ec336"],
|
||||
"lighthouse": [
|
||||
"6331bb0d1aa9f42b804997a6",
|
||||
"6398a0861c712b1e1d4dadf1",
|
||||
"6399f54b0a36db13c823ad21",
|
||||
"64e74a64aac4cd0a7264ecdf",
|
||||
"661666458c2aa9cb1602503b"
|
||||
],
|
||||
"rezervbase": [
|
||||
"64e74a4baac4cd0a7264ecdd",
|
||||
"6398a072e301557ae24cec92"
|
||||
"661666458c2aa9cb1602503b"
|
||||
],
|
||||
"rezervbase": ["64e74a4baac4cd0a7264ecdd", "6398a072e301557ae24cec92"],
|
||||
"shoreline": [
|
||||
"64e74a534d49d23b2c39d31b",
|
||||
"661421c7c1f2f548c50ee649",
|
||||
"6614217b6d9d5abcad0ff098",
|
||||
"661423200d240a5f5d0f679b"
|
||||
"661421c7c1f2f548c50ee649",
|
||||
"6614217b6d9d5abcad0ff098",
|
||||
"661423200d240a5f5d0f679b"
|
||||
],
|
||||
"tarkovstreets": [
|
||||
"638df4cc7b560b03794a18d2",
|
||||
@ -1101,14 +1026,8 @@
|
||||
"657acb2ac900be5902191ac9",
|
||||
"6582dbf0b8d7830efc45016f"
|
||||
],
|
||||
"laboratory": [
|
||||
"6398a4cfb5992f573c6562b3",
|
||||
"64e74a44c2b4f829615ec334"
|
||||
],
|
||||
"sandbox": [
|
||||
"6575a6ca8778e96ded05a802",
|
||||
"6582bd252b50c61c565828e2"
|
||||
]
|
||||
"laboratory": ["6398a4cfb5992f573c6562b3", "64e74a44c2b4f829615ec334"],
|
||||
"sandbox": ["6575a6ca8778e96ded05a802", "6582bd252b50c61c565828e2"]
|
||||
},
|
||||
"splitWaveIntoSingleSpawnsSettings": {
|
||||
"enabled": true,
|
||||
@ -1131,15 +1050,7 @@
|
||||
},
|
||||
"fixEmptyBotWavesSettings": {
|
||||
"enabled": true,
|
||||
"ignoreMaps": [
|
||||
"base",
|
||||
"develop",
|
||||
"hideout",
|
||||
"privatearea",
|
||||
"suburbs",
|
||||
"terminal",
|
||||
"town"
|
||||
]
|
||||
"ignoreMaps": ["base", "develop", "hideout", "privatearea", "suburbs", "terminal", "town"]
|
||||
},
|
||||
"fitLootIntoContainerAttempts": 3,
|
||||
"addOpenZonesToAllMaps": true,
|
||||
@ -1201,7 +1112,7 @@
|
||||
"minFillStaticMagazinePercent": 50,
|
||||
"allowDuplicateItemsInStaticContainers": true,
|
||||
"magazineLootHasAmmoChancePercent": 50,
|
||||
"staticMagazineLootHasAmmoChancePercent": 0,
|
||||
"staticMagazineLootHasAmmoChancePercent": 0,
|
||||
"looseLootBlacklist": {},
|
||||
"scavRaidTimeSettings": {
|
||||
"settings": {
|
||||
@ -1424,4 +1335,4 @@
|
||||
"mod_equipment": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,4 @@
|
||||
"rezervbase": {},
|
||||
"tarkovstreets": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,4 +18,4 @@
|
||||
},
|
||||
"questItems": true,
|
||||
"specialSlotItems": false
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,4 @@
|
||||
"laboratory": false,
|
||||
"rezervbase": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -856,19 +856,10 @@
|
||||
"botTypeForLoot": "assault",
|
||||
"equipmentBlacklist": {
|
||||
"Headwear": [],
|
||||
"ArmorVest": [
|
||||
"5df8a2ca86f7740bfe6df777"
|
||||
],
|
||||
"TacticalVest": [
|
||||
"572b7adb24597762ae139821"
|
||||
],
|
||||
"Backpack": [
|
||||
"56e33634d2720bd8058b456b",
|
||||
"5ab8ee7786f7742d8f33f0b9"
|
||||
],
|
||||
"FirstPrimaryWeapon": [
|
||||
"5a38e6bac4a2826c6e06d79b"
|
||||
],
|
||||
"ArmorVest": ["5df8a2ca86f7740bfe6df777"],
|
||||
"TacticalVest": ["572b7adb24597762ae139821"],
|
||||
"Backpack": ["56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
|
||||
"FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
|
||||
"Holster": [],
|
||||
"Scabbard": []
|
||||
},
|
||||
@ -955,22 +946,10 @@
|
||||
"botTypeForLoot": "assault",
|
||||
"equipmentBlacklist": {
|
||||
"Headwear": [],
|
||||
"ArmorVest": [
|
||||
"59e7635f86f7742cbf2c1095",
|
||||
"5df8a2ca86f7740bfe6df777"
|
||||
],
|
||||
"TacticalVest": [
|
||||
"5fd4c5477a8d854fa0105061",
|
||||
"572b7adb24597762ae139821"
|
||||
],
|
||||
"Backpack": [
|
||||
"56e33680d2720be2748b4576",
|
||||
"56e33634d2720bd8058b456b",
|
||||
"5ab8ee7786f7742d8f33f0b9"
|
||||
],
|
||||
"FirstPrimaryWeapon": [
|
||||
"5a38e6bac4a2826c6e06d79b"
|
||||
],
|
||||
"ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
|
||||
"TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
|
||||
"Backpack": ["56e33680d2720be2748b4576", "56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
|
||||
"FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
|
||||
"Holster": [],
|
||||
"Scabbard": []
|
||||
},
|
||||
@ -1056,22 +1035,10 @@
|
||||
"botTypeForLoot": "assault",
|
||||
"equipmentBlacklist": {
|
||||
"Headwear": [],
|
||||
"ArmorVest": [
|
||||
"59e7635f86f7742cbf2c1095",
|
||||
"5df8a2ca86f7740bfe6df777"
|
||||
],
|
||||
"TacticalVest": [
|
||||
"5fd4c5477a8d854fa0105061",
|
||||
"572b7adb24597762ae139821"
|
||||
],
|
||||
"Backpack": [
|
||||
"56e33680d2720be2748b4576",
|
||||
"56e33634d2720bd8058b456b",
|
||||
"5ab8ee7786f7742d8f33f0b9"
|
||||
],
|
||||
"FirstPrimaryWeapon": [
|
||||
"5a38e6bac4a2826c6e06d79b"
|
||||
],
|
||||
"ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
|
||||
"TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
|
||||
"Backpack": ["56e33680d2720be2748b4576", "56e33634d2720bd8058b456b", "5ab8ee7786f7742d8f33f0b9"],
|
||||
"FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
|
||||
"Holster": [],
|
||||
"Scabbard": []
|
||||
},
|
||||
@ -1168,14 +1135,8 @@
|
||||
"5f60e7788adaa7100c3adb49",
|
||||
"5aa2a7e8e5b5b00016327c16"
|
||||
],
|
||||
"ArmorVest": [
|
||||
"59e7635f86f7742cbf2c1095",
|
||||
"5df8a2ca86f7740bfe6df777"
|
||||
],
|
||||
"TacticalVest": [
|
||||
"5fd4c5477a8d854fa0105061",
|
||||
"572b7adb24597762ae139821"
|
||||
],
|
||||
"ArmorVest": ["59e7635f86f7742cbf2c1095", "5df8a2ca86f7740bfe6df777"],
|
||||
"TacticalVest": ["5fd4c5477a8d854fa0105061", "572b7adb24597762ae139821"],
|
||||
"Backpack": [
|
||||
"56e33680d2720be2748b4576",
|
||||
"56e33634d2720bd8058b456b",
|
||||
@ -1183,14 +1144,8 @@
|
||||
"5ab8f04f86f774585f4237d8",
|
||||
"5f5e45cc5021ce62144be7aa"
|
||||
],
|
||||
"FirstPrimaryWeapon": [
|
||||
"5a38e6bac4a2826c6e06d79b"
|
||||
],
|
||||
"Holster": [
|
||||
"5448bd6b4bdc2dfc2f8b4569",
|
||||
"579204f224597773d619e051",
|
||||
"571a12c42459771f627b58a0"
|
||||
],
|
||||
"FirstPrimaryWeapon": ["5a38e6bac4a2826c6e06d79b"],
|
||||
"Holster": ["5448bd6b4bdc2dfc2f8b4569", "579204f224597773d619e051", "571a12c42459771f627b58a0"],
|
||||
"Scabbard": []
|
||||
},
|
||||
"modifiers": {
|
||||
@ -1271,4 +1226,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@
|
||||
"6391fcf5744e45201147080f",
|
||||
"614451b71e5874611e2c7ae5",
|
||||
"6540d2162ae6d96b540afcaf",
|
||||
"65ca457b4aafb5d7fc0dcb5d"
|
||||
"65ca457b4aafb5d7fc0dcb5d"
|
||||
]
|
||||
},
|
||||
"pocketLoot": {
|
||||
@ -120,8 +120,8 @@
|
||||
"57864c8c245977548867e7f1",
|
||||
"6087e570b998180e9f76dc24",
|
||||
"6391fcf5744e45201147080f",
|
||||
"65ca457b4aafb5d7fc0dcb5d",
|
||||
"660bbc98c38b837877075e4a"
|
||||
"65ca457b4aafb5d7fc0dcb5d",
|
||||
"660bbc98c38b837877075e4a"
|
||||
]
|
||||
},
|
||||
"backpackLoot": {
|
||||
@ -179,8 +179,8 @@
|
||||
"6391fcf5744e45201147080f",
|
||||
"614451b71e5874611e2c7ae5",
|
||||
"6540d2162ae6d96b540afcaf",
|
||||
"65ca457b4aafb5d7fc0dcb5d",
|
||||
"660bbc98c38b837877075e4a"
|
||||
"65ca457b4aafb5d7fc0dcb5d",
|
||||
"660bbc98c38b837877075e4a"
|
||||
]
|
||||
},
|
||||
"useDifficultyOverride": false,
|
||||
@ -212,7 +212,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"factory4_night": {
|
||||
"bossKilla": 2,
|
||||
@ -233,7 +233,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"bigmap": {
|
||||
"bossKilla": 2,
|
||||
@ -254,7 +254,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"laboratory": {
|
||||
"bossKilla": 2,
|
||||
@ -275,7 +275,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"woods": {
|
||||
"bossKilla": 2,
|
||||
@ -297,7 +297,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"interchange": {
|
||||
"bossKilla": 2,
|
||||
@ -318,7 +318,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"lighthouse": {
|
||||
"bossKilla": 2,
|
||||
@ -339,7 +339,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"rezervbase": {
|
||||
"bossKilla": 2,
|
||||
@ -360,7 +360,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"shoreline": {
|
||||
"bossKilla": 2,
|
||||
@ -381,7 +381,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"tarkovstreets": {
|
||||
"bossKilla": 2,
|
||||
@ -402,7 +402,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"sandbox": {
|
||||
"bossKilla": 2,
|
||||
@ -425,7 +425,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 1,
|
||||
"pmcBot": 7,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
},
|
||||
"sandbox_high": {
|
||||
"bossKilla": 2,
|
||||
@ -448,7 +448,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 1,
|
||||
"pmcBot": 7,
|
||||
"pmcBEAR": 2
|
||||
"pmcBEAR": 2
|
||||
}
|
||||
},
|
||||
"pmcusec": {
|
||||
@ -471,7 +471,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"factory4_night": {
|
||||
"bossKilla": 2,
|
||||
@ -492,7 +492,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"bigmap": {
|
||||
"bossKilla": 0,
|
||||
@ -513,7 +513,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 2,
|
||||
"pmcBot": 6,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"laboratory": {
|
||||
"bossKilla": 2,
|
||||
@ -534,7 +534,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"woods": {
|
||||
"bossKilla": 2,
|
||||
@ -556,7 +556,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"interchange": {
|
||||
"bossKilla": 2,
|
||||
@ -577,7 +577,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"lighthouse": {
|
||||
"bossKilla": 2,
|
||||
@ -598,7 +598,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"rezervbase": {
|
||||
"bossKilla": 2,
|
||||
@ -619,7 +619,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"shoreline": {
|
||||
"bossKilla": 2,
|
||||
@ -640,7 +640,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"tarkovstreets": {
|
||||
"bossKilla": 2,
|
||||
@ -661,7 +661,7 @@
|
||||
"arenaFighterEvent": 0,
|
||||
"crazyAssaultEvent": 3,
|
||||
"pmcBot": 5,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"sandbox": {
|
||||
"bossKilla": 2,
|
||||
@ -684,7 +684,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 1,
|
||||
"pmcBot": 7,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
},
|
||||
"sandbox_high": {
|
||||
"bossKilla": 2,
|
||||
@ -707,7 +707,7 @@
|
||||
"arenaFighterEvent": 1,
|
||||
"crazyAssaultEvent": 1,
|
||||
"pmcBot": 7,
|
||||
"pmcUSEC": 2
|
||||
"pmcUSEC": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -276,10 +276,7 @@
|
||||
},
|
||||
"armor": {
|
||||
"removeRemovablePlateChance": 40,
|
||||
"plateSlotIdToRemovePool": [
|
||||
"front_plate",
|
||||
"back_plate"
|
||||
]
|
||||
"plateSlotIdToRemovePool": ["front_plate", "back_plate"]
|
||||
},
|
||||
"itemPriceMultiplier": {
|
||||
"5737292724597765e5728562": 6,
|
||||
@ -303,7 +300,7 @@
|
||||
"62e910aaf957f2915e0a5e36",
|
||||
"5447e1d04bdc2dff2f8b4567",
|
||||
"57bef4c42459772e8d35a53b",
|
||||
"55802f4a4bdc2ddb688b4569"
|
||||
"55802f4a4bdc2ddb688b4569"
|
||||
],
|
||||
"removeSeasonalItemsWhenNotInEvent": true,
|
||||
"blacklist": {
|
||||
@ -314,10 +311,7 @@
|
||||
"traderItems": false,
|
||||
"armorPlate": {
|
||||
"maxProtectionLevel": 4,
|
||||
"ignoreSlots": [
|
||||
"right_side_plate",
|
||||
"left_side_plate"
|
||||
]
|
||||
"ignoreSlots": ["right_side_plate", "left_side_plate"]
|
||||
},
|
||||
"enableCustomItemCategoryList": false,
|
||||
"customItemCategoryList": []
|
||||
@ -337,4 +331,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
"trader": 0.6
|
||||
},
|
||||
"weaponTreatment": {
|
||||
"critSuccessChance": 0.10,
|
||||
"critSuccessChance": 0.1,
|
||||
"critSuccessAmount": 4,
|
||||
"critFailureChance": 0.10,
|
||||
"critFailureChance": 0.1,
|
||||
"critFailureAmount": 4,
|
||||
"pointGainMultiplier": 0.6
|
||||
},
|
||||
@ -163,7 +163,7 @@
|
||||
"MalfunctionProtections": {
|
||||
"valuesMinMax": {
|
||||
"min": 0.75,
|
||||
"max": 0.90
|
||||
"max": 0.9
|
||||
},
|
||||
"activeDurabilityPercentMinMax": {
|
||||
"min": 75,
|
||||
@ -183,4 +183,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,9 +86,7 @@
|
||||
"ammoRewardChancePercent": 10,
|
||||
"minStackSize": 10,
|
||||
"ammoRewardBlacklist": {
|
||||
"common": [
|
||||
"59e690b686f7746c9f75e848"
|
||||
],
|
||||
"common": ["59e690b686f7746c9f75e848"],
|
||||
"rare": [],
|
||||
"superrare": []
|
||||
},
|
||||
@ -110,4 +108,4 @@
|
||||
"allowMultipleMoneyRewardsPerRarity": false,
|
||||
"allowMultipleAmmoRewardsPerRarity": true,
|
||||
"allowBossItemsAsRewards": false
|
||||
}
|
||||
}
|
||||
|
@ -721,4 +721,4 @@
|
||||
"endMonth": "2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -202,11 +202,11 @@
|
||||
}
|
||||
},
|
||||
"chancePlateExistsInArmorPercent": {
|
||||
"3": 95,
|
||||
"4": 75,
|
||||
"5": 25,
|
||||
"6": 10
|
||||
},
|
||||
"3": 95,
|
||||
"4": 75,
|
||||
"5": 25,
|
||||
"6": 10
|
||||
},
|
||||
"armorMaxDurabilityPercentMinMax": {
|
||||
"current": {
|
||||
"min": 50,
|
||||
|
@ -2,94 +2,32 @@
|
||||
"acceleration": 7,
|
||||
"weather": {
|
||||
"clouds": {
|
||||
"values": [
|
||||
-1.5,
|
||||
-1,
|
||||
0,
|
||||
0.5,
|
||||
1,
|
||||
1.5
|
||||
],
|
||||
"weights": [
|
||||
60,
|
||||
50,
|
||||
15,
|
||||
5,
|
||||
4,
|
||||
3
|
||||
]
|
||||
"values": [-1.5, -1, 0, 0.5, 1, 1.5],
|
||||
"weights": [60, 50, 15, 5, 4, 3]
|
||||
},
|
||||
"windSpeed": {
|
||||
"values": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"weights": [
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1
|
||||
]
|
||||
"values": [0, 1, 2, 3],
|
||||
"weights": [4, 3, 2, 1]
|
||||
},
|
||||
"windDirection": {
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8
|
||||
],
|
||||
"weights": [
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
"values": [1, 2, 3, 4, 5, 6, 7, 8],
|
||||
"weights": [1, 1, 1, 1, 1, 1, 1, 1]
|
||||
},
|
||||
"windGustiness": {
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"rain": {
|
||||
"values": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"weights": [
|
||||
25,
|
||||
1,
|
||||
1
|
||||
]
|
||||
"values": [1, 2, 3],
|
||||
"weights": [25, 1, 1]
|
||||
},
|
||||
"rainIntensity": {
|
||||
"min": 0,
|
||||
"max": 1
|
||||
},
|
||||
"fog": {
|
||||
"values": [
|
||||
0.002,
|
||||
0.004,
|
||||
0.008,
|
||||
0.012,
|
||||
0.087
|
||||
],
|
||||
"weights": [
|
||||
20,
|
||||
8,
|
||||
5,
|
||||
5,
|
||||
1
|
||||
]
|
||||
"values": [0.002, 0.004, 0.008, 0.012, 0.087],
|
||||
"weights": [20, 8, 5, 5, 1]
|
||||
},
|
||||
"temp": {
|
||||
"min": 0,
|
||||
@ -100,55 +38,55 @@
|
||||
"max": 764
|
||||
}
|
||||
},
|
||||
"seasonDates": [
|
||||
{
|
||||
"seasonType": 0,
|
||||
"name": "SUMMER",
|
||||
"startDay": "2",
|
||||
"seasonDates": [
|
||||
{
|
||||
"seasonType": 0,
|
||||
"name": "SUMMER",
|
||||
"startDay": "2",
|
||||
"startMonth": "6",
|
||||
"endDay": "1",
|
||||
"endMonth": "8"
|
||||
},
|
||||
{
|
||||
"seasonType": 1,
|
||||
"name": "AUTUMN",
|
||||
"startDay": "2",
|
||||
},
|
||||
{
|
||||
"seasonType": 1,
|
||||
"name": "AUTUMN",
|
||||
"startDay": "2",
|
||||
"startMonth": "8",
|
||||
"endDay": "1",
|
||||
"endMonth": "11"
|
||||
},
|
||||
{
|
||||
"seasonType": 2,
|
||||
"name": "WINTER_END",
|
||||
"startDay": "2",
|
||||
},
|
||||
{
|
||||
"seasonType": 2,
|
||||
"name": "WINTER_END",
|
||||
"startDay": "2",
|
||||
"startMonth": "11",
|
||||
"endDay": "31",
|
||||
"endMonth": "12"
|
||||
},
|
||||
{
|
||||
"seasonType": 2,
|
||||
"name": "WINTER_START",
|
||||
"startDay": "1",
|
||||
},
|
||||
{
|
||||
"seasonType": 2,
|
||||
"name": "WINTER_START",
|
||||
"startDay": "1",
|
||||
"startMonth": "1",
|
||||
"endDay": "24",
|
||||
"endMonth": "3"
|
||||
},
|
||||
{
|
||||
"seasonType": 3,
|
||||
"name": "SPRING",
|
||||
"startDay": "25",
|
||||
},
|
||||
{
|
||||
"seasonType": 3,
|
||||
"name": "SPRING",
|
||||
"startDay": "25",
|
||||
"startMonth": "3",
|
||||
"endDay": "1",
|
||||
"endMonth": "6"
|
||||
},
|
||||
{
|
||||
"seasonType": 4,
|
||||
"name": "STORM",
|
||||
"startDay": "24",
|
||||
},
|
||||
{
|
||||
"seasonType": 4,
|
||||
"name": "STORM",
|
||||
"startDay": "24",
|
||||
"startMonth": "10",
|
||||
"endDay": "4",
|
||||
"endMonth": "11"
|
||||
}
|
||||
],
|
||||
"overrideSeason": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"overrideSeason": null
|
||||
}
|
||||
|
@ -34,16 +34,16 @@
|
||||
"enabled": true,
|
||||
"formatWithErrors": false,
|
||||
"ignore": [
|
||||
"**/.git",
|
||||
"**/.pkg-cache",
|
||||
"**/.vscode",
|
||||
"**/build",
|
||||
"**/node_modules",
|
||||
"**/types",
|
||||
"**/tests/__cache__",
|
||||
"**/tests/__coverage__",
|
||||
"**/.editorconfig"
|
||||
],
|
||||
"**/.git",
|
||||
"**/.pkg-cache",
|
||||
"**/.vscode",
|
||||
"**/build",
|
||||
"**/node_modules",
|
||||
"**/types",
|
||||
"**/tests/__cache__",
|
||||
"**/tests/__coverage__",
|
||||
"**/.editorconfig"
|
||||
],
|
||||
"attributePosition": "auto",
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 4,
|
||||
|
@ -37,8 +37,7 @@ const licenseFile = "../LICENSE.md";
|
||||
/**
|
||||
* Transpile src files into Javascript with SWC
|
||||
*/
|
||||
const compile = async () =>
|
||||
{
|
||||
const compile = async () => {
|
||||
// Compile TypeScript files using SWC
|
||||
await exec("npx swc src -d obj", { stdio: "inherit" });
|
||||
|
||||
@ -46,29 +45,23 @@ const compile = async () =>
|
||||
const srcDir = path.join("obj", "src");
|
||||
const destDir = path.join("obj");
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
const entities = await fs.readdir(srcDir);
|
||||
for (const entity of entities)
|
||||
{
|
||||
for (const entity of entities) {
|
||||
const srcPath = path.join(srcDir, entity);
|
||||
const destPath = path.join(destDir, entity);
|
||||
await fs.move(srcPath, destPath, { overwrite: true });
|
||||
}
|
||||
// After moving all contents, remove the now-empty /obj/src directory
|
||||
await fs.remove(srcDir);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
} catch (error) {
|
||||
console.error("An error occurred during the merge operation:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Packaging
|
||||
const fetchPackageImage = async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const fetchPackageImage = async () => {
|
||||
try {
|
||||
const output = "./.pkg-cache/v3.5";
|
||||
const fetchedPkg = await pkgfetch.need({
|
||||
arch: targetArch,
|
||||
@ -79,18 +72,14 @@ const fetchPackageImage = async () =>
|
||||
console.log(`fetched node binary at ${fetchedPkg}`);
|
||||
const builtPkg = fetchedPkg.replace("node", "built");
|
||||
await fs.copyFile(fetchedPkg, builtPkg);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
} catch (e) {
|
||||
console.error(`Error while fetching and patching package image: ${e.message}`);
|
||||
console.error(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
const updateBuildProperties = async () =>
|
||||
{
|
||||
if (targetPlatform !== "win32")
|
||||
{
|
||||
const updateBuildProperties = async () => {
|
||||
if (targetPlatform !== "win32") {
|
||||
// can't modify executable's resource on non-windows build
|
||||
return;
|
||||
}
|
||||
@ -110,12 +99,15 @@ const updateBuildProperties = async () =>
|
||||
|
||||
const vi = ResEdit.Resource.VersionInfo.fromEntries(res.entries)[0];
|
||||
|
||||
vi.setStringValues({ lang: 1033, codepage: 1200 }, {
|
||||
ProductName: manifest.author,
|
||||
FileDescription: manifest.description,
|
||||
CompanyName: manifest.name,
|
||||
LegalCopyright: manifest.license,
|
||||
});
|
||||
vi.setStringValues(
|
||||
{ lang: 1033, codepage: 1200 },
|
||||
{
|
||||
ProductName: manifest.author,
|
||||
FileDescription: manifest.description,
|
||||
CompanyName: manifest.name,
|
||||
LegalCopyright: manifest.license,
|
||||
},
|
||||
);
|
||||
vi.removeStringValue({ lang: 1033, codepage: 1200 }, "OriginalFilename");
|
||||
vi.removeStringValue({ lang: 1033, codepage: 1200 }, "InternalName");
|
||||
vi.setFileVersion(...manifest.version.split(".").map(Number));
|
||||
@ -129,15 +121,14 @@ const updateBuildProperties = async () =>
|
||||
* Copy various asset files to the destination directory
|
||||
*/
|
||||
const copyAssets = () =>
|
||||
gulp.src(["assets/**/*.json", "assets/**/*.json5", "assets/**/*.png", "assets/**/*.jpg", "assets/**/*.ico"]).pipe(
|
||||
gulp.dest(dataDir),
|
||||
);
|
||||
gulp
|
||||
.src(["assets/**/*.json", "assets/**/*.json5", "assets/**/*.png", "assets/**/*.jpg", "assets/**/*.ico"])
|
||||
.pipe(gulp.dest(dataDir));
|
||||
|
||||
/**
|
||||
* 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
|
||||
// following task will download *all* versions that are compatible with the semver range specified.
|
||||
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 pnpmLink = npmResult.stdout.trim();
|
||||
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.
|
||||
*/
|
||||
const writeBuildDataToJSON = async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const writeBuildDataToJSON = async () => {
|
||||
try {
|
||||
// Fetch the latest Git commit hash
|
||||
const gitResult = await exec("git rev-parse HEAD", { stdout: "pipe" });
|
||||
|
||||
@ -180,9 +171,7 @@ const writeBuildDataToJSON = async () =>
|
||||
buildInfo.buildTime = coreParsed.buildTime;
|
||||
buildInfo.sptVersion = coreParsed.sptVersion;
|
||||
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}`);
|
||||
}
|
||||
};
|
||||
@ -190,8 +179,7 @@ const writeBuildDataToJSON = async () =>
|
||||
/**
|
||||
* Create a hash file for asset checks
|
||||
*/
|
||||
const createHashFile = async () =>
|
||||
{
|
||||
const createHashFile = async () => {
|
||||
const hashFileDir = path.resolve(dataDir, "checks.dat");
|
||||
const assetData = await loadRecursiveAsync("assets/");
|
||||
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
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
const getJSONFiles = async (dir, files = []) =>
|
||||
{
|
||||
const getJSONFiles = async (dir, files = []) => {
|
||||
const fileList = await fs.readdir(dir);
|
||||
for (const file of fileList)
|
||||
{
|
||||
for (const file of fileList) {
|
||||
const name = path.resolve(dir, file);
|
||||
if ((await fs.stat(name)).isDirectory())
|
||||
{
|
||||
if ((await fs.stat(name)).isDirectory()) {
|
||||
getJSONFiles(name, files);
|
||||
}
|
||||
else if (name.slice(-5) === ".json")
|
||||
{
|
||||
} else if (name.slice(-5) === ".json") {
|
||||
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.
|
||||
*/
|
||||
const validateJSONs = async () =>
|
||||
{
|
||||
const validateJSONs = async () => {
|
||||
const assetsPath = path.resolve("assets");
|
||||
const jsonFileList = await getJSONFiles(assetsPath);
|
||||
let jsonFileInProcess = "";
|
||||
try
|
||||
{
|
||||
for (const jsonFile of jsonFileList)
|
||||
{
|
||||
try {
|
||||
for (const jsonFile of jsonFileList) {
|
||||
jsonFileInProcess = jsonFile;
|
||||
JSON.parse(await fs.readFile(jsonFile));
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
} catch (error) {
|
||||
throw new Error(`${error.message} | ${jsonFileInProcess}`);
|
||||
}
|
||||
};
|
||||
@ -264,8 +242,7 @@ const validateJSONs = async () =>
|
||||
* @param {crypto.BinaryLike} data
|
||||
* @returns {string}
|
||||
*/
|
||||
const generateHashForData = (data) =>
|
||||
{
|
||||
const generateHashForData = (data) => {
|
||||
const hashSum = crypto.createHash("sha1");
|
||||
hashSum.update(data);
|
||||
return hashSum.digest("hex");
|
||||
@ -277,21 +254,16 @@ const generateHashForData = (data) =>
|
||||
* @param {fs.PathLike} filepath
|
||||
* @returns {}
|
||||
*/
|
||||
const loadRecursiveAsync = async (filepath) =>
|
||||
{
|
||||
const loadRecursiveAsync = async (filepath) => {
|
||||
const result = {};
|
||||
|
||||
const filesList = await fs.readdir(filepath);
|
||||
|
||||
for (const file of filesList)
|
||||
{
|
||||
for (const file of filesList) {
|
||||
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}/`);
|
||||
}
|
||||
else if (curPath.ext === ".json")
|
||||
{
|
||||
} else if (curPath.ext === ".json") {
|
||||
result[curPath.name] = generateHashForData(await fs.readFile(`${filepath}${file}`));
|
||||
}
|
||||
}
|
||||
@ -299,8 +271,7 @@ const loadRecursiveAsync = async (filepath) =>
|
||||
// set all loadRecursive to be executed asynchronously
|
||||
const resEntries = Object.entries(result);
|
||||
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];
|
||||
}
|
||||
|
||||
@ -309,8 +280,7 @@ const loadRecursiveAsync = async (filepath) =>
|
||||
};
|
||||
|
||||
// Main Tasks Generation
|
||||
const build = (packagingType) =>
|
||||
{
|
||||
const build = (packagingType) => {
|
||||
const anonPackaging = () => packaging(entries[packagingType]);
|
||||
anonPackaging.displayName = `packaging-${packagingType}`;
|
||||
const tasks = [
|
||||
@ -327,11 +297,9 @@ const build = (packagingType) =>
|
||||
};
|
||||
|
||||
// Packaging Arguments
|
||||
const packaging = async (entry) =>
|
||||
{
|
||||
const packaging = async (entry) => {
|
||||
const target = `${nodeVersion}-${targetPlatform}-${targetArch}`;
|
||||
try
|
||||
{
|
||||
try {
|
||||
await pkg.exec([
|
||||
entry,
|
||||
"--compress",
|
||||
@ -344,9 +312,7 @@ const packaging = async (entry) =>
|
||||
pkgConfig,
|
||||
"--public",
|
||||
]);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
} catch (error) {
|
||||
console.error(`Error occurred during packaging: ${error}`);
|
||||
}
|
||||
};
|
||||
@ -361,8 +327,7 @@ gulp.task(
|
||||
"run:debug",
|
||||
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 compile();
|
||||
await exec("node --prof --inspect --trace-warnings obj/ide/TestEntry.js", { stdio });
|
||||
|
@ -1,11 +1,6 @@
|
||||
{
|
||||
"pkg": {
|
||||
"scripts": [
|
||||
"obj/**/*.js"
|
||||
],
|
||||
"assets": [
|
||||
"obj/**/*.js.map",
|
||||
"package.json"
|
||||
]
|
||||
"scripts": ["obj/**/*.js"],
|
||||
"assets": ["obj/**/*.js.map", "package.json"]
|
||||
}
|
||||
}
|
||||
|
@ -3,23 +3,19 @@ import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { AsyncQueue } from "@spt/utils/AsyncQueue";
|
||||
import { WinstonMainLogger } from "@spt/utils/logging/WinstonMainLogger";
|
||||
|
||||
export class ErrorHandler
|
||||
{
|
||||
export class ErrorHandler {
|
||||
private logger: ILogger;
|
||||
private readLine: readline.Interface;
|
||||
|
||||
constructor()
|
||||
{
|
||||
constructor() {
|
||||
this.logger = new WinstonMainLogger(new AsyncQueue());
|
||||
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(`Exception produced: ${err.name}`);
|
||||
if (err.stack)
|
||||
{
|
||||
if (err.stack) {
|
||||
this.logger.error(`\nStacktrace:\n${err.stack}`);
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,21 @@
|
||||
import { container } from "tsyringe";
|
||||
import { Container } from "@spt/di/Container";
|
||||
import { ErrorHandler } from "@spt/ErrorHandler";
|
||||
import { Container } from "@spt/di/Container";
|
||||
import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
|
||||
import { App } from "@spt/utils/App";
|
||||
import { Watermark } from "@spt/utils/Watermark";
|
||||
import { container } from "tsyringe";
|
||||
|
||||
export class Program
|
||||
{
|
||||
export class Program {
|
||||
private errorHandler: ErrorHandler;
|
||||
constructor()
|
||||
{
|
||||
constructor() {
|
||||
// set window properties
|
||||
process.stdout.setEncoding("utf8");
|
||||
process.title = "SPT Server";
|
||||
this.errorHandler = new ErrorHandler();
|
||||
}
|
||||
|
||||
public async start(): Promise<void>
|
||||
{
|
||||
try
|
||||
{
|
||||
public async start(): Promise<void> {
|
||||
try {
|
||||
Container.registerTypes(container);
|
||||
const childContainer = container.createChildContainer();
|
||||
const watermark = childContainer.resolve<Watermark>("Watermark");
|
||||
@ -31,9 +27,7 @@ export class Program
|
||||
|
||||
Container.registerPostLoadTypes(container, childContainer);
|
||||
childContainer.resolve<App>("App").load();
|
||||
}
|
||||
catch (err: any)
|
||||
{
|
||||
} catch (err: any) {
|
||||
this.errorHandler.handleCriticalError(err instanceof Error ? err : new Error(err));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { AchievementController } from "@spt/controllers/AchievementController";
|
||||
import { ProfileController } from "@spt/controllers/ProfileController";
|
||||
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 { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class AchievementCallbacks
|
||||
{
|
||||
export class AchievementCallbacks {
|
||||
constructor(
|
||||
@inject("AchievementController") protected achievementController: AchievementController,
|
||||
@inject("ProfileController") protected profileController: ProfileController,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/achievement/list
|
||||
@ -24,8 +22,7 @@ export class AchievementCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetAchievementsResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGetAchievementsResponse> {
|
||||
return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID));
|
||||
}
|
||||
|
||||
@ -36,8 +33,7 @@ export class AchievementCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICompletedAchievementsResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ICompletedAchievementsResponse> {
|
||||
return this.httpResponse.getBody(this.achievementController.getAchievementStatistics(sessionID));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { BotController } from "@spt/controllers/BotController";
|
||||
import { IGenerateBotsRequestData } from "@spt/models/eft/bot/IGenerateBotsRequestData";
|
||||
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 { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class BotCallbacks
|
||||
{
|
||||
export class BotCallbacks {
|
||||
constructor(
|
||||
@inject("BotController") protected botController: BotController,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle singleplayer/settings/bot/limit
|
||||
* Is called by client to define each bot roles wave limit
|
||||
* @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 type = splittedUrl[splittedUrl.length - 1];
|
||||
return this.httpResponse.noBody(this.botController.getBotPresetGenerationLimit(type));
|
||||
@ -32,13 +29,11 @@ export class BotCallbacks
|
||||
* Handle singleplayer/settings/bot/difficulty
|
||||
* @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 type = splittedUrl[splittedUrl.length - 2].toLowerCase();
|
||||
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.getBotDifficulty(type, difficulty));
|
||||
@ -52,8 +47,7 @@ export class BotCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): Record<string, Difficulties>
|
||||
{
|
||||
): Record<string, Difficulties> {
|
||||
return this.httpResponse.noBody(this.botController.getAllBotDifficulties());
|
||||
}
|
||||
|
||||
@ -65,8 +59,7 @@ export class BotCallbacks
|
||||
url: string,
|
||||
info: IGenerateBotsRequestData,
|
||||
sessionID: string,
|
||||
): Promise<IGetBodyResponseData<IBotBase[]>>
|
||||
{
|
||||
): Promise<IGetBodyResponseData<IBotBase[]>> {
|
||||
return this.httpResponse.getBody(await this.botController.generate(sessionID, info));
|
||||
}
|
||||
|
||||
@ -74,8 +67,7 @@ export class BotCallbacks
|
||||
* Handle singleplayer/settings/bot/maxCap
|
||||
* @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 location = splitUrl[splitUrl.length - 1];
|
||||
return this.httpResponse.noBody(this.botController.getBotCap(location));
|
||||
@ -85,8 +77,7 @@ export class BotCallbacks
|
||||
* Handle singleplayer/settings/bot/getBotBehaviours
|
||||
* @returns string
|
||||
*/
|
||||
public getBotBehaviours(): string
|
||||
{
|
||||
public getBotBehaviours(): string {
|
||||
return this.httpResponse.noBody(this.botController.getAiBotBrainTypes());
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { BuildController } from "@spt/controllers/BuildController";
|
||||
import { ISetMagazineRequest } from "@spt/models/eft/builds/ISetMagazineRequest";
|
||||
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 { IUserBuilds } from "@spt/models/eft/profile/ISptProfile";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class BuildsCallbacks
|
||||
{
|
||||
export class BuildsCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("BuildController") protected buildController: BuildController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return this.httpResponse.nullResponse();
|
||||
@ -39,8 +35,7 @@ export class BuildsCallbacks
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return this.httpResponse.nullResponse();
|
||||
@ -49,8 +44,7 @@ export class BuildsCallbacks
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return this.httpResponse.nullResponse();
|
||||
@ -59,8 +53,7 @@ export class BuildsCallbacks
|
||||
/**
|
||||
* 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);
|
||||
|
||||
return this.httpResponse.nullResponse();
|
||||
|
@ -1,34 +1,30 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { BundleLoader } from "@spt/loaders/BundleLoader";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class BundleCallbacks
|
||||
{
|
||||
export class BundleCallbacks {
|
||||
protected httpConfig: IHttpConfig;
|
||||
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("BundleLoader") protected bundleLoader: BundleLoader,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
public getBundle(url: string, info: any, sessionID: string): string
|
||||
{
|
||||
public getBundle(url: string, info: any, sessionID: string): string {
|
||||
return "BUNDLE";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ClientLogController } from "@spt/controllers/ClientLogController";
|
||||
import { ModLoadOrder } from "@spt/loaders/ModLoadOrder";
|
||||
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 { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/** Handle client logging related events */
|
||||
@injectable()
|
||||
export class ClientLogCallbacks
|
||||
{
|
||||
export class ClientLogCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("ClientLogController") protected clientLogController: ClientLogController,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ModLoadOrder") protected modLoadOrder: ModLoadOrder,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -34,8 +31,7 @@ export class ClientLogCallbacks
|
||||
/**
|
||||
* Handle /singleplayer/release
|
||||
*/
|
||||
public releaseNotes(): string
|
||||
{
|
||||
public releaseNotes(): string {
|
||||
const data: IRelease = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).release;
|
||||
|
||||
data.betaDisclaimerText = globalThis.G_MODS_ENABLED
|
||||
@ -62,8 +58,7 @@ export class ClientLogCallbacks
|
||||
* Handle /singleplayer/enableBSGlogging
|
||||
*/
|
||||
|
||||
public bsgLogging(): string
|
||||
{
|
||||
public bsgLogging(): string {
|
||||
const data: IBsgLogging = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).bsgLogging;
|
||||
return this.httpResponse.noBody(data);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { CustomizationController } from "@spt/controllers/CustomizationController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
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 { SaveServer } from "@spt/servers/SaveServer";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class CustomizationCallbacks
|
||||
{
|
||||
export class CustomizationCallbacks {
|
||||
constructor(
|
||||
@inject("CustomizationController") protected customizationController: CustomizationController,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/trading/customization/storage
|
||||
* @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 };
|
||||
return this.httpResponse.getBody(result);
|
||||
}
|
||||
@ -35,8 +32,7 @@ export class CustomizationCallbacks
|
||||
* Handle client/trading/customization
|
||||
* @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 traderID = splittedUrl[splittedUrl.length - 2];
|
||||
|
||||
@ -50,16 +46,14 @@ export class CustomizationCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IWearClothingRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.customizationController.wearClothing(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HideoutController } from "@spt/controllers/HideoutController";
|
||||
import { RagfairController } from "@spt/controllers/RagfairController";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Handle client requests
|
||||
*/
|
||||
@injectable()
|
||||
export class DataCallbacks
|
||||
{
|
||||
export class DataCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@ -31,15 +30,13 @@ export class DataCallbacks
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
@inject("RagfairController") protected ragfairController: RagfairController,
|
||||
@inject("HideoutController") protected hideoutController: HideoutController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/settings
|
||||
* @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());
|
||||
}
|
||||
|
||||
@ -47,8 +44,7 @@ export class DataCallbacks
|
||||
* Handle client/globals
|
||||
* @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();
|
||||
globals.time = Date.now() / 1000;
|
||||
|
||||
@ -59,8 +55,7 @@ export class DataCallbacks
|
||||
* Handle client/items
|
||||
* @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());
|
||||
}
|
||||
|
||||
@ -72,8 +67,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IHandbookBase>
|
||||
{
|
||||
): IGetBodyResponseData<IHandbookBase> {
|
||||
return this.httpResponse.getBody(this.databaseService.getHandbook());
|
||||
}
|
||||
|
||||
@ -85,8 +79,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<Record<string, ICustomizationItem>>
|
||||
{
|
||||
): IGetBodyResponseData<Record<string, ICustomizationItem>> {
|
||||
return this.httpResponse.getBody(this.databaseService.getTemplates().customization);
|
||||
}
|
||||
|
||||
@ -98,8 +91,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<string[]>
|
||||
{
|
||||
): IGetBodyResponseData<string[]> {
|
||||
return this.httpResponse.getBody(this.databaseService.getTemplates().character);
|
||||
}
|
||||
|
||||
@ -111,8 +103,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IHideoutSettingsBase>
|
||||
{
|
||||
): IGetBodyResponseData<IHideoutSettingsBase> {
|
||||
return this.httpResponse.getBody(this.databaseService.getHideout().settings);
|
||||
}
|
||||
|
||||
@ -120,8 +111,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IHideoutArea[]>
|
||||
{
|
||||
): IGetBodyResponseData<IHideoutArea[]> {
|
||||
return this.httpResponse.getBody(this.databaseService.getHideout().areas);
|
||||
}
|
||||
|
||||
@ -129,8 +119,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IHideoutProduction[]>
|
||||
{
|
||||
): IGetBodyResponseData<IHideoutProduction[]> {
|
||||
return this.httpResponse.getBody(this.databaseService.getHideout().production);
|
||||
}
|
||||
|
||||
@ -138,8 +127,7 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IHideoutScavCase[]>
|
||||
{
|
||||
): IGetBodyResponseData<IHideoutScavCase[]> {
|
||||
return this.httpResponse.getBody(this.databaseService.getHideout().scavcase);
|
||||
}
|
||||
|
||||
@ -150,27 +138,23 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<Record<string, string>>
|
||||
{
|
||||
): IGetBodyResponseData<Record<string, string>> {
|
||||
return this.httpResponse.getBody(this.databaseService.getLocales().languages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 locales = this.databaseService.getLocales();
|
||||
let result = locales.menu[localeId];
|
||||
|
||||
if (result === undefined)
|
||||
{
|
||||
if (result === undefined) {
|
||||
result = locales.menu.en;
|
||||
}
|
||||
|
||||
if (result === undefined)
|
||||
throw new Error(`Unable to determine locale for request with '${localeId}'`);
|
||||
if (result === undefined) throw new Error(`Unable to determine locale for request with '${localeId}'`);
|
||||
|
||||
return this.httpResponse.getBody(result);
|
||||
}
|
||||
@ -178,14 +162,12 @@ export class DataCallbacks
|
||||
/**
|
||||
* 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 locales = this.databaseService.getLocales();
|
||||
let result = locales.global[localeId];
|
||||
|
||||
if (result === undefined)
|
||||
{
|
||||
if (result === undefined) {
|
||||
result = locales.global["en"];
|
||||
}
|
||||
|
||||
@ -195,8 +177,7 @@ export class DataCallbacks
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
@ -209,16 +190,14 @@ export class DataCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetItemPricesResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGetItemPricesResponse> {
|
||||
const traderId = url.replace("/client/items/prices/", "");
|
||||
|
||||
// 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
|
||||
const handbookPrices = this.ragfairController.getStaticPrices();
|
||||
|
||||
const response: IGetItemPricesResponse
|
||||
= {
|
||||
const response: IGetItemPricesResponse = {
|
||||
supplyNextTime: this.traderHelper.getNextUpdateTimestamp(traderId),
|
||||
prices: handbookPrices,
|
||||
currencyCourses: {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { DialogueController } from "@spt/controllers/DialogueController";
|
||||
import { OnUpdate } from "@spt/di/OnUpdate";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class DialogueCallbacks implements OnUpdate
|
||||
{
|
||||
export class DialogueCallbacks implements OnUpdate {
|
||||
constructor(
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("DialogueController") protected dialogueController: DialogueController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/friend/list
|
||||
@ -56,8 +54,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetFriendListDataResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGetFriendListDataResponse> {
|
||||
return this.httpResponse.getBody(this.dialogueController.getFriendList(sessionID));
|
||||
}
|
||||
|
||||
@ -69,8 +66,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetChatServerListRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IChatServer[]>
|
||||
{
|
||||
): IGetBodyResponseData<IChatServer[]> {
|
||||
const chatServer: IChatServer = {
|
||||
_id: this.hashUtil.generate(),
|
||||
RegistrationId: 20,
|
||||
@ -91,8 +87,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetMailDialogListRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<DialogueInfo[]>
|
||||
{
|
||||
): IGetBodyResponseData<DialogueInfo[]> {
|
||||
return this.httpResponse.getBody(this.dialogueController.generateDialogueList(sessionID), 0, undefined, false);
|
||||
}
|
||||
|
||||
@ -101,8 +96,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetMailDialogViewRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetMailDialogViewResponseData>
|
||||
{
|
||||
): IGetBodyResponseData<IGetMailDialogViewResponseData> {
|
||||
return this.httpResponse.getBody(
|
||||
this.dialogueController.generateDialogueView(info, sessionID),
|
||||
0,
|
||||
@ -116,35 +110,30 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetMailDialogInfoRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<DialogueInfo>
|
||||
{
|
||||
): IGetBodyResponseData<DialogueInfo> {
|
||||
return this.httpResponse.getBody(this.dialogueController.getDialogueInfo(info.dialogId, sessionID));
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return this.httpResponse.emptyArrayResponse();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return this.httpResponse.emptyArrayResponse();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return this.httpResponse.emptyArrayResponse();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return this.httpResponse.emptyArrayResponse();
|
||||
}
|
||||
@ -157,28 +146,24 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetAllAttachmentsRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetAllAttachmentsResponse | undefined>
|
||||
{
|
||||
): IGetBodyResponseData<IGetAllAttachmentsResponse | undefined> {
|
||||
return this.httpResponse.getBody(this.dialogueController.getAllAttachments(info.dialogId, sessionID));
|
||||
}
|
||||
|
||||
/** 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));
|
||||
}
|
||||
|
||||
/** 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([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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([]);
|
||||
}
|
||||
|
||||
@ -189,16 +174,14 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
request: IFriendRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IFriendRequestSendResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IFriendRequestSendResponse> {
|
||||
return this.httpResponse.getBody(this.dialogueController.sendFriendRequest(sessionID, request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@ -209,8 +192,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
request: IAcceptFriendRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
@ -221,8 +203,7 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
request: IDeclineFriendRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
@ -233,41 +214,34 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
request: ICancelFriendRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
public clearMail(url: string, request: IClearMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]>
|
||||
{
|
||||
public clearMail(url: string, request: IClearMailMessageRequest, sessionID: string): IGetBodyResponseData<any[]> {
|
||||
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();
|
||||
}
|
||||
|
||||
public createGroupMail(url: string, info: ICreateGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]>
|
||||
{
|
||||
public createGroupMail(url: string, info: ICreateGroupMailRequest, sessionID: string): IGetBodyResponseData<any[]> {
|
||||
return this.httpResponse.emptyArrayResponse();
|
||||
}
|
||||
|
||||
@ -275,13 +249,11 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IChangeGroupMailOwnerRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<any[]>
|
||||
{
|
||||
): IGetBodyResponseData<any[]> {
|
||||
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.");
|
||||
}
|
||||
|
||||
@ -289,19 +261,16 @@ export class DialogueCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IRemoveUserGroupMailRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<any[]>
|
||||
{
|
||||
): IGetBodyResponseData<any[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean>
|
||||
{
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
|
||||
this.dialogueController.update();
|
||||
return true;
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-dialogue";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { GameController } from "@spt/controllers/GameController";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { Watermark } from "@spt/utils/Watermark";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class GameCallbacks implements OnLoad
|
||||
{
|
||||
export class GameCallbacks implements OnLoad {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("Watermark") protected watermark: Watermark,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("GameController") protected gameController: GameController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.gameController.load();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-game";
|
||||
}
|
||||
|
||||
@ -47,8 +43,7 @@ export class GameCallbacks implements OnLoad
|
||||
* Handle client/game/version/validate
|
||||
* @returns INullResponseData
|
||||
*/
|
||||
public versionValidate(url: string, info: IVersionValidateRequestData, sessionID: string): INullResponseData
|
||||
{
|
||||
public versionValidate(url: string, info: IVersionValidateRequestData, sessionID: string): INullResponseData {
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
|
||||
@ -60,8 +55,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGameStartResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGameStartResponse> {
|
||||
const today = new Date().toUTCString();
|
||||
const startTimeStampMS = Date.parse(today);
|
||||
this.gameController.gameStart(url, info, sessionID, startTimeStampMS);
|
||||
@ -77,8 +71,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGameLogoutResponseData>
|
||||
{
|
||||
): IGetBodyResponseData<IGameLogoutResponseData> {
|
||||
this.saveServer.save();
|
||||
return this.httpResponse.getBody({ status: "ok" });
|
||||
}
|
||||
@ -91,8 +84,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IGameEmptyCrcRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGameConfigResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGameConfigResponse> {
|
||||
return this.httpResponse.getBody(this.gameController.getGameConfig(sessionID));
|
||||
}
|
||||
|
||||
@ -104,16 +96,14 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IGameModeRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGameModeResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGameModeResponse> {
|
||||
return this.httpResponse.getBody(this.gameController.getGameMode(sessionID, info));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
@ -124,8 +114,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICurrentGroupResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ICurrentGroupResponse> {
|
||||
return this.httpResponse.getBody(this.gameController.getCurrentGroup(sessionID));
|
||||
}
|
||||
|
||||
@ -136,8 +125,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICheckVersionResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ICheckVersionResponse> {
|
||||
return this.httpResponse.getBody(this.gameController.getValidGameVersion(sessionID));
|
||||
}
|
||||
|
||||
@ -149,8 +137,7 @@ export class GameCallbacks implements OnLoad
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGameKeepAliveResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGameKeepAliveResponse> {
|
||||
return this.httpResponse.getBody(this.gameController.getKeepAlive(sessionID));
|
||||
}
|
||||
|
||||
@ -158,8 +145,7 @@ export class GameCallbacks implements OnLoad
|
||||
* Handle singleplayer/settings/version
|
||||
* @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() });
|
||||
}
|
||||
|
||||
@ -167,8 +153,7 @@ export class GameCallbacks implements OnLoad
|
||||
* Handle /client/report/send & /client/reports/lobby/send
|
||||
* @returns INullResponseData
|
||||
*/
|
||||
public reportNickname(url: string, info: IUIDRequestData, sessionID: string): INullResponseData
|
||||
{
|
||||
public reportNickname(url: string, info: IUIDRequestData, sessionID: string): INullResponseData {
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
|
||||
@ -176,8 +161,7 @@ export class GameCallbacks implements OnLoad
|
||||
* Handle singleplayer/settings/getRaidTime
|
||||
* @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));
|
||||
}
|
||||
|
||||
@ -185,8 +169,7 @@ export class GameCallbacks implements OnLoad
|
||||
* Handle /client/survey
|
||||
* @returns INullResponseData
|
||||
*/
|
||||
public getSurvey(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData
|
||||
{
|
||||
public getSurvey(url: string, request: IEmptyRequestData, sessionID: string): INullResponseData {
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,16 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HandbookController } from "@spt/controllers/HandbookController";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HandbookCallbacks implements OnLoad
|
||||
{
|
||||
constructor(@inject("HandbookController") protected handbookController: HandbookController)
|
||||
{}
|
||||
export class HandbookCallbacks implements OnLoad {
|
||||
constructor(@inject("HandbookController") protected handbookController: HandbookController) {}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.handbookController.load();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-handbook";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HealthController } from "@spt/controllers/HealthController";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
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 { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HealthCallbacks
|
||||
{
|
||||
export class HealthCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
@inject("HealthController") protected healthController: HealthController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Custom spt server request found in modules/HealthSynchronizer.cs
|
||||
@ -28,8 +26,7 @@ export class HealthCallbacks
|
||||
* @param sessionID session id
|
||||
* @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);
|
||||
return this.httpResponse.emptyResponse();
|
||||
}
|
||||
@ -41,8 +38,7 @@ export class HealthCallbacks
|
||||
* @param sessionID session id
|
||||
* @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);
|
||||
return this.httpResponse.emptyResponse();
|
||||
}
|
||||
@ -51,8 +47,7 @@ export class HealthCallbacks
|
||||
* Handle Eat
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -60,8 +55,7 @@ export class HealthCallbacks
|
||||
* Handle Heal
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -73,8 +67,7 @@ export class HealthCallbacks
|
||||
pmcData: IPmcData,
|
||||
info: IHealthTreatmentRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.healthController.healthTreatment(pmcData, info, sessionID);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HideoutController } from "@spt/controllers/HideoutController";
|
||||
import { OnUpdate } from "@spt/di/OnUpdate";
|
||||
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 { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HideoutCallbacks implements OnUpdate
|
||||
{
|
||||
export class HideoutCallbacks implements OnUpdate {
|
||||
protected hideoutConfig: IHideoutConfig;
|
||||
|
||||
constructor(
|
||||
@inject("HideoutController") protected hideoutController: HideoutController, // TODO: delay needed
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
||||
}
|
||||
|
||||
@ -41,8 +39,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
body: IHideoutUpgradeRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.hideoutController.startUpgrade(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -56,8 +53,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
body: IHideoutUpgradeCompleteRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.hideoutController.upgradeComplete(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -70,8 +66,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutPutItemInRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.putItemsInAreaSlots(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -82,8 +77,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutTakeItemOutRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.takeItemsFromAreaSlots(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -94,8 +88,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutToggleAreaRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.toggleArea(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -106,8 +99,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutSingleProductionStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.singleProductionStart(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -118,8 +110,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutScavCaseStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.scavCaseProductionStart(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -130,8 +121,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutContinuousProductionStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.continuousProductionStart(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -142,8 +132,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutTakeProductionRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.takeProduction(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -155,8 +144,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
request: IHandleQTEEventRequestData,
|
||||
sessionId: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.hideoutController.handleQTEEventOutcome(sessionId, pmcData, request, output);
|
||||
|
||||
return output;
|
||||
@ -170,8 +158,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
request: IRecordShootingRangePoints,
|
||||
sessionId: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.hideoutController.recordShootingRangePoints(sessionId, pmcData, request);
|
||||
|
||||
return output;
|
||||
@ -184,8 +171,7 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutImproveAreaRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.improveArea(sessionId, pmcData, request);
|
||||
}
|
||||
|
||||
@ -196,23 +182,19 @@ export class HideoutCallbacks implements OnUpdate
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutCancelProductionRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.hideoutController.cancelProduction(sessionId, pmcData, request);
|
||||
}
|
||||
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean>
|
||||
{
|
||||
if (timeSinceLastRun > this.hideoutConfig.runIntervalSeconds)
|
||||
{
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
|
||||
if (timeSinceLastRun > this.hideoutConfig.runIntervalSeconds) {
|
||||
this.hideoutController.update();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-hideout";
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,20 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { HttpServer } from "@spt/servers/HttpServer";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HttpCallbacks implements OnLoad
|
||||
{
|
||||
constructor(@inject("HttpServer") protected httpServer: HttpServer)
|
||||
{}
|
||||
export class HttpCallbacks implements OnLoad {
|
||||
constructor(@inject("HttpServer") protected httpServer: HttpServer) {}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.httpServer.load();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-http";
|
||||
}
|
||||
|
||||
public getImage(): string
|
||||
{
|
||||
public getImage(): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,20 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { InraidController } from "@spt/controllers/InraidController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData";
|
||||
import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData";
|
||||
import { IScavSaveRequestData } from "@spt/models/eft/inRaid/IScavSaveRequestData";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Handle client requests
|
||||
*/
|
||||
@injectable()
|
||||
export class InraidCallbacks
|
||||
{
|
||||
export class InraidCallbacks {
|
||||
constructor(
|
||||
@inject("InraidController") protected inraidController: InraidController,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/location/getLocalloot
|
||||
@ -26,8 +24,7 @@ export class InraidCallbacks
|
||||
* @param sessionID Session id
|
||||
* @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);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -39,8 +36,7 @@ export class InraidCallbacks
|
||||
* @param sessionID Session id
|
||||
* @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);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -50,8 +46,7 @@ export class InraidCallbacks
|
||||
* Handle singleplayer/settings/raid/endstate
|
||||
* @returns
|
||||
*/
|
||||
public getRaidEndState(): string
|
||||
{
|
||||
public getRaidEndState(): string {
|
||||
return this.httpResponse.noBody(this.inraidController.getInraidConfig().MIAOnRaidEnd);
|
||||
}
|
||||
|
||||
@ -59,18 +54,15 @@ export class InraidCallbacks
|
||||
* Handle singleplayer/settings/raid/menu
|
||||
* @returns JSON as string
|
||||
*/
|
||||
public getRaidMenuSettings(): string
|
||||
{
|
||||
public getRaidMenuSettings(): string {
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { InsuranceController } from "@spt/controllers/InsuranceController";
|
||||
import { OnUpdate } from "@spt/di/OnUpdate";
|
||||
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 { InsuranceService } from "@spt/services/InsuranceService";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class InsuranceCallbacks implements OnUpdate
|
||||
{
|
||||
export class InsuranceCallbacks implements OnUpdate {
|
||||
protected insuranceConfig: IInsuranceConfig;
|
||||
constructor(
|
||||
@inject("InsuranceController") protected insuranceController: InsuranceController,
|
||||
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
||||
}
|
||||
|
||||
@ -35,8 +33,7 @@ export class InsuranceCallbacks implements OnUpdate
|
||||
url: string,
|
||||
info: IGetInsuranceCostRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetInsuranceCostResponseData>
|
||||
{
|
||||
): IGetBodyResponseData<IGetInsuranceCostResponseData> {
|
||||
return this.httpResponse.getBody(this.insuranceController.cost(info, sessionID));
|
||||
}
|
||||
|
||||
@ -44,24 +41,20 @@ export class InsuranceCallbacks implements OnUpdate
|
||||
* Handle Insure event
|
||||
* @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);
|
||||
}
|
||||
|
||||
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
|
||||
if (secondsSinceLastRun > Math.max(this.insuranceConfig.runIntervalSeconds, 1))
|
||||
{
|
||||
if (secondsSinceLastRun > Math.max(this.insuranceConfig.runIntervalSeconds, 1)) {
|
||||
this.insuranceController.processReturn();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-insurance";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { InventoryController } from "@spt/controllers/InventoryController";
|
||||
import { QuestController } from "@spt/controllers/QuestController";
|
||||
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 { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IFailQuestRequestData } from "@spt/models/eft/quests/IFailQuestRequestData";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class InventoryCallbacks
|
||||
{
|
||||
export class InventoryCallbacks {
|
||||
constructor(
|
||||
@inject("InventoryController") protected inventoryController: InventoryController,
|
||||
@inject("QuestController") protected questController: QuestController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/** Handle client/game/profile/items/moving Move event */
|
||||
public moveItem(
|
||||
@ -39,8 +37,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryMoveRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.moveItem(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -52,8 +49,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryRemoveRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.discardItem(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -65,8 +61,7 @@ export class InventoryCallbacks
|
||||
body: IInventorySplitRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.inventoryController.splitItem(pmcData, body, sessionID, output);
|
||||
}
|
||||
|
||||
@ -75,8 +70,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryMergeRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.inventoryController.mergeItem(pmcData, body, sessionID, output);
|
||||
}
|
||||
|
||||
@ -85,20 +79,17 @@ export class InventoryCallbacks
|
||||
request: IInventoryTransferRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.inventoryController.transferItem(pmcData, request, sessionID, output);
|
||||
}
|
||||
|
||||
/** Handle Swap */
|
||||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -106,13 +97,11 @@ export class InventoryCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IInventoryToggleRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -121,8 +110,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryBindRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.bindItem(pmcData, body, sessionID);
|
||||
|
||||
return output;
|
||||
@ -133,8 +121,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryBindRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.unbindItem(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -145,8 +132,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryExamineRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.inventoryController.examineItem(pmcData, body, sessionID, output);
|
||||
}
|
||||
|
||||
@ -155,8 +141,7 @@ export class InventoryCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IInventoryReadEncyclopediaRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.inventoryController.readEncyclopedia(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -166,8 +151,7 @@ export class InventoryCallbacks
|
||||
body: IInventorySortRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.sortInventory(pmcData, body, sessionID);
|
||||
|
||||
return output;
|
||||
@ -178,8 +162,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryCreateMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.createMapMarker(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -190,8 +173,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryDeleteMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.deleteMapMarker(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -202,8 +184,7 @@ export class InventoryCallbacks
|
||||
body: IInventoryEditMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.editMapMarker(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -215,8 +196,7 @@ export class InventoryCallbacks
|
||||
body: IOpenRandomLootContainerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.openRandomLootContainer(pmcData, body, sessionID, output);
|
||||
|
||||
return output;
|
||||
@ -227,8 +207,7 @@ export class InventoryCallbacks
|
||||
body: IRedeemProfileRequestData,
|
||||
sessionId: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.redeemProfileReward(pmcData, body, sessionId);
|
||||
|
||||
return output;
|
||||
@ -239,8 +218,7 @@ export class InventoryCallbacks
|
||||
body: ISetFavoriteItems,
|
||||
sessionId: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.inventoryController.setFavoriteItem(pmcData, body, sessionId);
|
||||
|
||||
return output;
|
||||
@ -255,8 +233,7 @@ export class InventoryCallbacks
|
||||
request: IFailQuestRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.questController.failQuest(pmcData, request, sessionID, output);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||
import { Warning } from "@spt/models/eft/itemEvent/IItemEventRouterBase";
|
||||
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 { ItemEventRouter } from "@spt/routers/ItemEventRouter";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class ItemEventCallbacks
|
||||
{
|
||||
export class ItemEventCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("ItemEventRouter") protected itemEventRouter: ItemEventRouter,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public async handleEvents(
|
||||
url: string,
|
||||
info: IItemEventRouterRequest,
|
||||
sessionID: string,
|
||||
): Promise<IGetBodyResponseData<IItemEventRouterResponse>>
|
||||
{
|
||||
): Promise<IGetBodyResponseData<IItemEventRouterResponse>> {
|
||||
const eventResponse = await this.itemEventRouter.handleEvents(info, sessionID);
|
||||
const result = this.isCriticalError(eventResponse.warnings)
|
||||
? this.httpResponse.getBody(
|
||||
eventResponse,
|
||||
this.getErrorCode(eventResponse.warnings),
|
||||
eventResponse.warnings[0].errmsg,
|
||||
)
|
||||
eventResponse,
|
||||
this.getErrorCode(eventResponse.warnings),
|
||||
eventResponse.warnings[0].errmsg,
|
||||
)
|
||||
: this.httpResponse.getBody(eventResponse);
|
||||
|
||||
return result;
|
||||
@ -39,15 +36,12 @@ export class ItemEventCallbacks
|
||||
* @param warnings The list of warnings to check for critical errors
|
||||
* @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
|
||||
const nonCriticalErrorCodes: BackendErrorCodes[] = [BackendErrorCodes.NOTENOUGHSPACE];
|
||||
|
||||
for (const warning of warnings)
|
||||
{
|
||||
if (!nonCriticalErrorCodes.includes(+(warning?.code ?? "0")))
|
||||
{
|
||||
for (const warning of warnings) {
|
||||
if (!nonCriticalErrorCodes.includes(+(warning?.code ?? "0"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -55,10 +49,8 @@ export class ItemEventCallbacks
|
||||
return false;
|
||||
}
|
||||
|
||||
protected getErrorCode(warnings: Warning[]): number
|
||||
{
|
||||
if (warnings[0]?.code)
|
||||
{
|
||||
protected getErrorCode(warnings: Warning[]): number {
|
||||
if (warnings[0]?.code) {
|
||||
return Number(warnings[0].code);
|
||||
}
|
||||
return BackendErrorCodes.UNKNOWN_ERROR;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { LauncherController } from "@spt/controllers/LauncherController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { Watermark } from "@spt/utils/Watermark";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class LauncherCallbacks
|
||||
{
|
||||
export class LauncherCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("LauncherController") protected launcherController: LauncherController,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("Watermark") protected watermark: Watermark,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public connect(): string
|
||||
{
|
||||
public connect(): string {
|
||||
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);
|
||||
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);
|
||||
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));
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
return !output ? "FAILED" : "OK";
|
||||
}
|
||||
|
||||
public getServerVersion(): string
|
||||
{
|
||||
public getServerVersion(): string {
|
||||
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!");
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public getCompatibleTarkovVersion(): string
|
||||
{
|
||||
public getCompatibleTarkovVersion(): string {
|
||||
return this.httpResponse.noBody(this.launcherController.getCompatibleTarkovVersion());
|
||||
}
|
||||
|
||||
public getLoadedServerMods(): string
|
||||
{
|
||||
public getLoadedServerMods(): string {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,24 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { LocationController } from "@spt/controllers/LocationController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
||||
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class LocationCallbacks
|
||||
{
|
||||
export class LocationCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("LocationController") protected locationController: LocationController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/** Handle client/locations */
|
||||
public getLocationData(
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ILocationsGenerateAllResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ILocationsGenerateAllResponse> {
|
||||
return this.httpResponse.getBody(this.locationController.generateAll(sessionID));
|
||||
}
|
||||
|
||||
@ -30,8 +27,7 @@ export class LocationCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetAirdropLootResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGetAirdropLootResponse> {
|
||||
return this.httpResponse.getBody(this.locationController.getAirdropLoot());
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { MatchController } from "@spt/controllers/MatchController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { JsonUtil } from "@spt/utils/JsonUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class MatchCallbacks
|
||||
{
|
||||
export class MatchCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("MatchController") protected matchController: MatchController,
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
@ -56,18 +51,15 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IMatchGroupCurrentResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IMatchGroupCurrentResponse> {
|
||||
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();
|
||||
}
|
||||
|
||||
public stopGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData
|
||||
{
|
||||
public stopGroupSearch(url: string, info: IEmptyRequestData, sessionID: string): INullResponseData {
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
|
||||
@ -76,8 +68,7 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IMatchGroupInviteSendRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<string>
|
||||
{
|
||||
): IGetBodyResponseData<string> {
|
||||
return this.httpResponse.getBody("2427943f23698ay9f2863735");
|
||||
}
|
||||
|
||||
@ -86,8 +77,7 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IRequestIdRequest,
|
||||
sessionId: string,
|
||||
): IGetBodyResponseData<IGroupCharacter[]>
|
||||
{
|
||||
): IGetBodyResponseData<IGroupCharacter[]> {
|
||||
const result = [];
|
||||
result.push({});
|
||||
|
||||
@ -95,14 +85,12 @@ export class MatchCallbacks
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
@ -111,8 +99,7 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IMatchGroupTransferRequest,
|
||||
sessionId: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
@ -121,19 +108,16 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionId: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
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();
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
return this.httpResponse.getBody(output);
|
||||
@ -144,14 +128,12 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IMatchGroupStartGameRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IProfileStatusResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IProfileStatusResponse> {
|
||||
return this.httpResponse.getBody(this.matchController.joinMatch(info, sessionID));
|
||||
}
|
||||
|
||||
/** 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));
|
||||
}
|
||||
|
||||
@ -164,21 +146,18 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IMatchGroupStatusRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IMatchGroupStatusResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IMatchGroupStatusResponse> {
|
||||
return this.httpResponse.getBody(this.matchController.getGroupStatus(info));
|
||||
}
|
||||
|
||||
/** 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);
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -187,8 +166,7 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IMatchGroupPlayerRemoveRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(true);
|
||||
}
|
||||
|
||||
@ -197,18 +175,12 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IStartLocalRaidRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IStartLocalRaidResponseData>
|
||||
{
|
||||
): IGetBodyResponseData<IStartLocalRaidResponseData> {
|
||||
return this.httpResponse.getBody(this.matchController.startLocalRaid(sessionID, info));
|
||||
}
|
||||
|
||||
/** Handle client/match/local/end */
|
||||
public endLocalRaid(
|
||||
url: string,
|
||||
info: IEndLocalRaidRequestData,
|
||||
sessionID: string,
|
||||
): INullResponseData
|
||||
{
|
||||
public endLocalRaid(url: string, info: IEndLocalRaidRequestData, sessionID: string): INullResponseData {
|
||||
this.matchController.endLocalRaid(sessionID, info);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -218,8 +190,7 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IGetRaidConfigurationRequestData,
|
||||
sessionID: string,
|
||||
): INullResponseData
|
||||
{
|
||||
): INullResponseData {
|
||||
this.matchController.configureOfflineRaid(info, sessionID);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -229,20 +200,17 @@ export class MatchCallbacks
|
||||
url: string,
|
||||
info: IGetRaidConfigurationRequestData,
|
||||
sessionID: string,
|
||||
): INullResponseData
|
||||
{
|
||||
): INullResponseData {
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { PostSptModLoader } from "@spt/loaders/PostSptModLoader";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
@ -8,10 +7,10 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { HttpFileUtil } from "@spt/utils/HttpFileUtil";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class ModCallbacks implements OnLoad
|
||||
{
|
||||
export class ModCallbacks implements OnLoad {
|
||||
protected httpConfig: IHttpConfig;
|
||||
|
||||
constructor(
|
||||
@ -21,21 +20,17 @@ export class ModCallbacks implements OnLoad
|
||||
@inject("PostSptModLoader") protected postSptModLoader: PostSptModLoader,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
||||
}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
if (globalThis.G_MODS_ENABLED)
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
if (globalThis.G_MODS_ENABLED) {
|
||||
await this.postSptModLoader.load();
|
||||
}
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-mods";
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,25 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { NoteController } from "@spt/controllers/NoteController";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { INoteActionData } from "@spt/models/eft/notes/INoteActionData";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class NoteCallbacks
|
||||
{
|
||||
constructor(@inject("NoteController") protected noteController: NoteController)
|
||||
{}
|
||||
export class NoteCallbacks {
|
||||
constructor(@inject("NoteController") protected noteController: NoteController) {}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { NotifierController } from "@spt/controllers/NotifierController";
|
||||
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { JsonUtil } from "@spt/utils/JsonUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class NotifierCallbacks
|
||||
{
|
||||
export class NotifierCallbacks {
|
||||
constructor(
|
||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("NotifierController") protected notifierController: NotifierController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 tmpSessionID = splittedUrl[splittedUrl.length - 1].split("?last_id")[0];
|
||||
|
||||
@ -44,8 +41,7 @@ export class NotifierCallbacks
|
||||
/** Handle push/notifier/get */
|
||||
/** Handle push/notifier/getwebsocket */
|
||||
// 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();
|
||||
}
|
||||
|
||||
@ -54,8 +50,7 @@ export class NotifierCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<INotifierChannel>
|
||||
{
|
||||
): IGetBodyResponseData<INotifierChannel> {
|
||||
return this.httpResponse.getBody(this.notifierController.getChannel(sessionID));
|
||||
}
|
||||
|
||||
@ -67,13 +62,11 @@ export class NotifierCallbacks
|
||||
url: string,
|
||||
info: IUIDRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ISelectProfileResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ISelectProfileResponse> {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,16 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { PresetController } from "@spt/controllers/PresetController";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class PresetCallbacks implements OnLoad
|
||||
{
|
||||
constructor(@inject("PresetController") protected presetController: PresetController)
|
||||
{}
|
||||
export class PresetCallbacks implements OnLoad {
|
||||
constructor(@inject("PresetController") protected presetController: PresetController) {}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.presetController.initialize();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-presets";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ProfileController } from "@spt/controllers/ProfileController";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
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 { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/** Handle profile related client events */
|
||||
@injectable()
|
||||
export class ProfileCallbacks
|
||||
{
|
||||
export class ProfileCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("ProfileController") protected profileController: ProfileController,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/game/profile/create
|
||||
@ -39,8 +37,7 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: IProfileCreateRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICreateProfileResponse>
|
||||
{
|
||||
): IGetBodyResponseData<ICreateProfileResponse> {
|
||||
const id = this.profileController.createProfile(info, sessionID);
|
||||
return this.httpResponse.getBody({ uid: id });
|
||||
}
|
||||
@ -49,8 +46,7 @@ export class ProfileCallbacks
|
||||
* Handle client/game/profile/list
|
||||
* 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));
|
||||
}
|
||||
|
||||
@ -63,16 +59,14 @@ export class ProfileCallbacks
|
||||
* @param sessionID Session id
|
||||
* @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)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -85,17 +79,14 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: IProfileChangeNicknameRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<any>
|
||||
{
|
||||
): IGetBodyResponseData<any> {
|
||||
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");
|
||||
}
|
||||
|
||||
if (output === "tooshort")
|
||||
{
|
||||
if (output === "tooshort") {
|
||||
return this.httpResponse.getBody(undefined, 1, "The nickname is too short");
|
||||
}
|
||||
|
||||
@ -109,17 +100,14 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: IValidateNicknameRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<any>
|
||||
{
|
||||
): IGetBodyResponseData<any> {
|
||||
const output = this.profileController.validateNickname(info, sessionID);
|
||||
|
||||
if (output === "taken")
|
||||
{
|
||||
if (output === "taken") {
|
||||
return this.httpResponse.getBody(undefined, 255, "225 - ");
|
||||
}
|
||||
|
||||
if (output === "tooshort")
|
||||
{
|
||||
if (output === "tooshort") {
|
||||
return this.httpResponse.getBody(undefined, 256, "256 - ");
|
||||
}
|
||||
|
||||
@ -129,8 +117,7 @@ export class ProfileCallbacks
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
@ -142,8 +129,7 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<GetProfileStatusResponseData>
|
||||
{
|
||||
): IGetBodyResponseData<GetProfileStatusResponseData> {
|
||||
return this.httpResponse.getBody(this.profileController.getProfileStatus(sessionID));
|
||||
}
|
||||
|
||||
@ -155,8 +141,7 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
request: IGetOtherProfileRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetOtherProfileResponse>
|
||||
{
|
||||
): IGetBodyResponseData<IGetOtherProfileResponse> {
|
||||
return this.httpResponse.getBody(this.profileController.getOtherProfile(sessionID, request));
|
||||
}
|
||||
|
||||
@ -167,8 +152,7 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: IGetProfileSettingsRequest,
|
||||
sessionId: string,
|
||||
): IGetBodyResponseData<boolean>
|
||||
{
|
||||
): IGetBodyResponseData<boolean> {
|
||||
return this.httpResponse.getBody(this.profileController.setChosenProfileIcon(sessionId, info));
|
||||
}
|
||||
|
||||
@ -179,24 +163,21 @@ export class ProfileCallbacks
|
||||
url: string,
|
||||
info: ISearchFriendRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ISearchFriendResponse[]>
|
||||
{
|
||||
): IGetBodyResponseData<ISearchFriendResponse[]> {
|
||||
return this.httpResponse.getBody(this.profileController.getFriends(info, sessionID));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { QuestController } from "@spt/controllers/QuestController";
|
||||
import { RepeatableQuestController } from "@spt/controllers/RepeatableQuestController";
|
||||
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 { IRepeatableQuestChangeRequest } from "@spt/models/eft/quests/IRepeatableQuestChangeRequest";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class QuestCallbacks
|
||||
{
|
||||
export class QuestCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("QuestController") protected questController: QuestController,
|
||||
@inject("RepeatableQuestController") protected repeatableQuestController: RepeatableQuestController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle RepeatableQuestChange event
|
||||
@ -31,18 +29,15 @@ export class QuestCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IRepeatableQuestChangeRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.repeatableQuestController.changeRepeatableQuest(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle QuestAccept event
|
||||
*/
|
||||
public acceptQuest(pmcData: IPmcData, body: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse
|
||||
{
|
||||
if (body.type === "repeatable")
|
||||
{
|
||||
public acceptQuest(pmcData: IPmcData, body: IAcceptQuestRequestData, sessionID: string): IItemEventRouterResponse {
|
||||
if (body.type === "repeatable") {
|
||||
return this.questController.acceptRepeatableQuest(pmcData, body, sessionID);
|
||||
}
|
||||
return this.questController.acceptQuest(pmcData, body, sessionID);
|
||||
@ -55,8 +50,7 @@ export class QuestCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: ICompleteQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.questController.completeQuest(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -67,16 +61,14 @@ export class QuestCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IHandoverQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.questController.handoverQuest(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
@ -87,8 +79,7 @@ export class QuestCallbacks
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IPmcDataRepeatableQuest[]>
|
||||
{
|
||||
): IGetBodyResponseData<IPmcDataRepeatableQuest[]> {
|
||||
return this.httpResponse.getBody(this.repeatableQuestController.getClientRepeatableQuests(sessionID));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { RagfairController } from "@spt/controllers/RagfairController";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { OnUpdate } from "@spt/di/OnUpdate";
|
||||
@ -24,13 +23,13 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { RagfairServer } from "@spt/servers/RagfairServer";
|
||||
import { RagfairTaxService } from "@spt/services/RagfairTaxService";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Handle ragfair related callback events
|
||||
*/
|
||||
@injectable()
|
||||
export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
{
|
||||
export class RagfairCallbacks implements OnLoad, OnUpdate {
|
||||
protected ragfairConfig: IRagfairConfig;
|
||||
|
||||
constructor(
|
||||
@ -39,25 +38,20 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
@inject("RagfairController") protected ragfairController: RagfairController,
|
||||
@inject("RagfairTaxService") protected ragfairTaxService: RagfairTaxService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
await this.ragfairServer.load();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-ragfair";
|
||||
}
|
||||
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean>
|
||||
{
|
||||
if (timeSinceLastRun > this.ragfairConfig.runIntervalSeconds)
|
||||
{
|
||||
public async onUpdate(timeSinceLastRun: number): Promise<boolean> {
|
||||
if (timeSinceLastRun > this.ragfairConfig.runIntervalSeconds) {
|
||||
// There is a flag inside this class that only makes it run once.
|
||||
this.ragfairServer.addPlayerOffers();
|
||||
|
||||
@ -76,8 +70,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
* Handle client/ragfair/search
|
||||
* 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));
|
||||
}
|
||||
|
||||
@ -86,26 +79,22 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
url: string,
|
||||
info: IGetMarketPriceRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetItemPriceResult>
|
||||
{
|
||||
): IGetBodyResponseData<IGetItemPriceResult> {
|
||||
return this.httpResponse.getBody(this.ragfairController.getItemMinAvgMaxFleaPriceValues(info));
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
@ -117,14 +106,12 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
url: string,
|
||||
request: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<Record<string, number>>
|
||||
{
|
||||
): IGetBodyResponseData<Record<string, number>> {
|
||||
return this.httpResponse.getBody(this.ragfairController.getAllFleaPrices());
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
|
||||
@ -132,8 +119,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
url: string,
|
||||
request: IStorePlayerOfferTaxAmountRequestData,
|
||||
sessionId: string,
|
||||
): INullResponseData
|
||||
{
|
||||
): INullResponseData {
|
||||
this.ragfairTaxService.storeClientOfferTaxValue(sessionId, request);
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
@ -143,8 +129,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
url: string,
|
||||
request: IGetRagfairOfferByIdRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IRagfairOffer>
|
||||
{
|
||||
): IGetBodyResponseData<IRagfairOffer> {
|
||||
return this.httpResponse.getBody(this.ragfairController.getOfferById(sessionID, request));
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { RepairController } from "@spt/controllers/RepairController";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IRepairActionDataRequest } from "@spt/models/eft/repair/IRepairActionDataRequest";
|
||||
import { ITraderRepairActionDataRequest } from "@spt/models/eft/repair/ITraderRepairActionDataRequest";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class RepairCallbacks
|
||||
{
|
||||
constructor(@inject("RepairController") protected repairController: RepairController)
|
||||
{}
|
||||
export class RepairCallbacks {
|
||||
constructor(@inject("RepairController") protected repairController: RepairController) {}
|
||||
|
||||
/**
|
||||
* Handle TraderRepair event
|
||||
@ -23,8 +21,7 @@ export class RepairCallbacks
|
||||
pmcData: IPmcData,
|
||||
traderRepairRequest: ITraderRepairActionDataRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.repairController.traderRepair(sessionID, traderRepairRequest, pmcData);
|
||||
}
|
||||
|
||||
@ -40,8 +37,7 @@ export class RepairCallbacks
|
||||
pmcData: IPmcData,
|
||||
repairRequest: IRepairActionDataRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.repairController.repairWithKit(sessionID, repairRequest, pmcData);
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,33 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
import { OnUpdate } from "@spt/di/OnUpdate";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { SaveServer } from "@spt/servers/SaveServer";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class SaveCallbacks implements OnLoad, OnUpdate
|
||||
{
|
||||
export class SaveCallbacks implements OnLoad, OnUpdate {
|
||||
protected coreConfig: ICoreConfig;
|
||||
|
||||
constructor(
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||
}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.saveServer.load();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-save";
|
||||
}
|
||||
|
||||
public async onUpdate(secondsSinceLastRun: number): Promise<boolean>
|
||||
{
|
||||
public async onUpdate(secondsSinceLastRun: number): Promise<boolean> {
|
||||
// run every 15 seconds
|
||||
if (secondsSinceLastRun > this.coreConfig.profileSaveIntervalSeconds)
|
||||
{
|
||||
if (secondsSinceLastRun > this.coreConfig.profileSaveIntervalSeconds) {
|
||||
this.saveServer.save();
|
||||
return true;
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { TradeController } from "@spt/controllers/TradeController";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData";
|
||||
import { IProcessRagfairTradeRequestData } from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
|
||||
import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class TradeCallbacks
|
||||
{
|
||||
constructor(@inject("TradeController") protected tradeController: TradeController)
|
||||
{}
|
||||
export class TradeCallbacks {
|
||||
constructor(@inject("TradeController") protected tradeController: TradeController) {}
|
||||
|
||||
/**
|
||||
* Handle client/game/profile/items/moving TradingConfirm event
|
||||
@ -19,8 +17,7 @@ export class TradeCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IProcessBaseTradeRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// body can be IProcessBuyTradeRequestData or IProcessSellTradeRequestData
|
||||
return this.tradeController.confirmTrading(pmcData, body, sessionID);
|
||||
}
|
||||
@ -30,8 +27,7 @@ export class TradeCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: IProcessRagfairTradeRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.tradeController.confirmRagfairTrading(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
@ -40,8 +36,7 @@ export class TradeCallbacks
|
||||
pmcData: IPmcData,
|
||||
body: ISellScavItemsToFenceRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.tradeController.sellScavItemsToFence(pmcData, body, sessionID);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { TraderController } from "@spt/controllers/TraderController";
|
||||
import { OnLoad } from "@spt/di/OnLoad";
|
||||
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 { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class TraderCallbacks implements OnLoad, OnUpdate
|
||||
{
|
||||
export class TraderCallbacks implements OnLoad, OnUpdate {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, // TODO: delay required
|
||||
@inject("TraderController") protected traderController: TraderController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public async onLoad(): Promise<void>
|
||||
{
|
||||
public async onLoad(): Promise<void> {
|
||||
this.traderController.load();
|
||||
}
|
||||
|
||||
public async onUpdate(): Promise<boolean>
|
||||
{
|
||||
public async onUpdate(): Promise<boolean> {
|
||||
return this.traderController.update();
|
||||
}
|
||||
|
||||
public getRoute(): string
|
||||
{
|
||||
public getRoute(): string {
|
||||
return "spt-traders";
|
||||
}
|
||||
|
||||
@ -36,21 +31,18 @@ export class TraderCallbacks implements OnLoad, OnUpdate
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ITraderBase[]>
|
||||
{
|
||||
): IGetBodyResponseData<ITraderBase[]> {
|
||||
return this.httpResponse.getBody(this.traderController.getAllTraders(sessionID));
|
||||
}
|
||||
|
||||
/** 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/", "");
|
||||
return this.httpResponse.getBody(this.traderController.getTrader(sessionID, traderID));
|
||||
}
|
||||
|
||||
/** 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/", "");
|
||||
return this.httpResponse.getBody(this.traderController.getAssort(sessionID, traderID));
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { WeatherController } from "@spt/controllers/WeatherController";
|
||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
||||
import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData";
|
||||
import { IWeatherData } from "@spt/models/eft/weather/IWeatherData";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class WeatherCallbacks
|
||||
{
|
||||
export class WeatherCallbacks {
|
||||
constructor(
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("WeatherController") protected weatherController: WeatherController,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle client/weather
|
||||
* @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());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,21 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { WishlistController } from "@spt/controllers/WishlistController";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest";
|
||||
import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest";
|
||||
import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class WishlistCallbacks
|
||||
{
|
||||
constructor(@inject("WishlistController") protected wishlistController: WishlistController)
|
||||
{}
|
||||
export class WishlistCallbacks {
|
||||
constructor(@inject("WishlistController") protected wishlistController: WishlistController) {}
|
||||
|
||||
/** Handle AddToWishList event */
|
||||
public addToWishlist(
|
||||
pmcData: IPmcData,
|
||||
request: IAddToWishlistRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.wishlistController.addToWishList(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
@ -27,8 +24,7 @@ export class WishlistCallbacks
|
||||
pmcData: IPmcData,
|
||||
request: IRemoveFromWishlistRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.wishlistController.removeFromWishList(pmcData, request, sessionID);
|
||||
}
|
||||
|
||||
@ -37,8 +33,7 @@ export class WishlistCallbacks
|
||||
pmcData: IPmcData,
|
||||
request: IChangeWishlistItemCategoryRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
return this.wishlistController.changeWishlistItemCategory(pmcData, request, sessionID);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { injectable } from "tsyringe";
|
||||
import { ContextVariable } from "@spt/context/ContextVariable";
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
import { LinkedList } from "@spt/utils/collections/lists/LinkedList";
|
||||
import { injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class ApplicationContext
|
||||
{
|
||||
export class ApplicationContext {
|
||||
private variables = new Map<ContextVariableType, LinkedList<ContextVariable>>();
|
||||
private static holderMaxSize = 10;
|
||||
|
||||
@ -19,23 +18,18 @@ export class ApplicationContext
|
||||
* const matchInfo = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>();
|
||||
* ```
|
||||
*/
|
||||
public getLatestValue(type: ContextVariableType): ContextVariable | undefined
|
||||
{
|
||||
if (this.variables.has(type))
|
||||
{
|
||||
public getLatestValue(type: ContextVariableType): ContextVariable | undefined {
|
||||
if (this.variables.has(type)) {
|
||||
return this.variables.get(type)?.getTail();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getValues(type: ContextVariableType): ContextVariable[] | undefined
|
||||
{
|
||||
if (this.variables.has(type))
|
||||
{
|
||||
public getValues(type: ContextVariableType): ContextVariable[] | undefined {
|
||||
if (this.variables.has(type)) {
|
||||
const res: ContextVariable[] = [];
|
||||
|
||||
for (const value of this.variables.get(type)!.values())
|
||||
{
|
||||
for (const value of this.variables.get(type)!.values()) {
|
||||
res.push(value);
|
||||
}
|
||||
|
||||
@ -44,20 +38,15 @@ export class ApplicationContext
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public addValue(type: ContextVariableType, value: any): void
|
||||
{
|
||||
public addValue(type: ContextVariableType, value: any): void {
|
||||
let list: LinkedList<ContextVariable>;
|
||||
if (this.variables.has(type))
|
||||
{
|
||||
if (this.variables.has(type)) {
|
||||
list = this.variables.get(type)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
list = new LinkedList<ContextVariable>();
|
||||
}
|
||||
|
||||
if (list.length >= ApplicationContext.holderMaxSize)
|
||||
{
|
||||
if (list.length >= ApplicationContext.holderMaxSize) {
|
||||
list.shift();
|
||||
}
|
||||
|
||||
@ -65,10 +54,8 @@ export class ApplicationContext
|
||||
this.variables.set(type, list);
|
||||
}
|
||||
|
||||
public clearValues(type: ContextVariableType): void
|
||||
{
|
||||
if (this.variables.has(type))
|
||||
{
|
||||
public clearValues(type: ContextVariableType): void {
|
||||
if (this.variables.has(type)) {
|
||||
this.variables.delete(type);
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,25 @@
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
|
||||
export class ContextVariable
|
||||
{
|
||||
export class ContextVariable {
|
||||
private value: any;
|
||||
private timestamp: Date;
|
||||
private type: ContextVariableType;
|
||||
|
||||
constructor(value: any, type: ContextVariableType)
|
||||
{
|
||||
constructor(value: any, type: ContextVariableType) {
|
||||
this.value = value;
|
||||
this.timestamp = new Date();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public getValue<T>(): T
|
||||
{
|
||||
public getValue<T>(): T {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public getTimestamp(): Date
|
||||
{
|
||||
public getTimestamp(): Date {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public getType(): ContextVariableType
|
||||
{
|
||||
public getType(): ContextVariableType {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
export enum ContextVariableType
|
||||
{
|
||||
export enum ContextVariableType {
|
||||
/** Logged in users session id */
|
||||
SESSION_ID = 0,
|
||||
/** Currently acive raid information */
|
||||
|
@ -1,27 +1,24 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ICompletedAchievementsResponse } from "@spt/models/eft/profile/ICompletedAchievementsResponse";
|
||||
import { IGetAchievementsResponse } from "@spt/models/eft/profile/IGetAchievementsResponse";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Logic for handling In Raid callbacks
|
||||
*/
|
||||
@injectable()
|
||||
export class AchievementController
|
||||
{
|
||||
export class AchievementController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get base achievements
|
||||
* @param sessionID Session id
|
||||
*/
|
||||
public getAchievements(sessionID: string): IGetAchievementsResponse
|
||||
{
|
||||
public getAchievements(sessionID: string): IGetAchievementsResponse {
|
||||
return { elements: this.databaseService.getAchievements() };
|
||||
}
|
||||
|
||||
@ -30,13 +27,11 @@ export class AchievementController
|
||||
* @param sessionId Session id
|
||||
* @returns ICompletedAchievementsResponse
|
||||
*/
|
||||
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse
|
||||
{
|
||||
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse {
|
||||
const achievements = this.databaseService.getAchievements();
|
||||
const stats = {};
|
||||
|
||||
for (const achievement of achievements)
|
||||
{
|
||||
for (const achievement of achievements) {
|
||||
stats[achievement.id] = 0;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
import { BotGenerator } from "@spt/generators/BotGenerator";
|
||||
@ -25,12 +24,12 @@ import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class BotController
|
||||
{
|
||||
export class BotController {
|
||||
protected botConfig: IBotConfig;
|
||||
protected pmcConfig: IPmcConfig;
|
||||
|
||||
@ -50,8 +49,7 @@ export class BotController
|
||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
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
|
||||
* @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];
|
||||
|
||||
if (!value)
|
||||
{
|
||||
if (!value) {
|
||||
this.logger.warning(this.localisationService.getText("bot-bot_preset_count_value_missing", type));
|
||||
|
||||
return 30;
|
||||
@ -80,8 +76,7 @@ export class BotController
|
||||
* Get the core.json difficulty settings from database/bots
|
||||
* @returns IBotCore
|
||||
*/
|
||||
public getBotCoreDifficulty(): IBotCore
|
||||
{
|
||||
public getBotCoreDifficulty(): IBotCore {
|
||||
return this.databaseService.getBots().core;
|
||||
}
|
||||
|
||||
@ -93,15 +88,13 @@ export class BotController
|
||||
* @param ignoreRaidSettings should raid settings chosen pre-raid be ignored
|
||||
* @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();
|
||||
|
||||
const raidConfig = this.applicationContext
|
||||
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
||||
?.getValue<IGetRaidConfigurationRequestData>();
|
||||
if (!(raidConfig || ignoreRaidSettings))
|
||||
{
|
||||
if (!(raidConfig || ignoreRaidSettings)) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("bot-missing_application_context", "RAID_CONFIGURATION"),
|
||||
);
|
||||
@ -110,16 +103,14 @@ export class BotController
|
||||
// Check value chosen in pre-raid difficulty dropdown
|
||||
// If value is not 'asonline', change requested difficulty to be what was chosen in dropdown
|
||||
const botDifficultyDropDownValue = raidConfig?.wavesSettings.botDifficulty.toLowerCase() ?? "asonline";
|
||||
if (botDifficultyDropDownValue !== "asonline")
|
||||
{
|
||||
difficulty
|
||||
= this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
|
||||
if (botDifficultyDropDownValue !== "asonline") {
|
||||
difficulty =
|
||||
this.botDifficultyHelper.convertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
|
||||
}
|
||||
|
||||
let difficultySettings: Difficulty;
|
||||
const lowercasedBotType = type.toLowerCase();
|
||||
switch (lowercasedBotType)
|
||||
{
|
||||
switch (lowercasedBotType) {
|
||||
case this.pmcConfig.bearType.toLowerCase():
|
||||
difficultySettings = this.botDifficultyHelper.getPmcDifficultySettings(
|
||||
"bear",
|
||||
@ -144,14 +135,12 @@ export class BotController
|
||||
return difficultySettings;
|
||||
}
|
||||
|
||||
public getAllBotDifficulties(): Record<string, any>
|
||||
{
|
||||
public getAllBotDifficulties(): Record<string, any> {
|
||||
const result = {};
|
||||
|
||||
const botTypesDb = this.databaseService.getBots().types;
|
||||
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();
|
||||
// pmcBEAR/pmcUSEC need to be converted into `usec`/`bear` so we can read difficulty settings from bots/types
|
||||
botType = this.botHelper.isBotPmc(botType)
|
||||
@ -159,15 +148,13 @@ export class BotController
|
||||
: botType.toLowerCase();
|
||||
|
||||
const botDetails = botTypesDb[botType];
|
||||
if (!botDetails?.difficulty)
|
||||
{
|
||||
if (!botDetails?.difficulty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const botDifficulties = Object.keys(botDetails.difficulty);
|
||||
result[enumType] = {};
|
||||
for (const difficulty of botDifficulties)
|
||||
{
|
||||
for (const difficulty of botDifficulties) {
|
||||
result[enumType][difficulty] = this.getBotDifficulty(enumType, difficulty, true);
|
||||
}
|
||||
}
|
||||
@ -181,14 +168,12 @@ export class BotController
|
||||
* @param info bot generation request info
|
||||
* @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);
|
||||
|
||||
// Use this opportunity to create and cache bots for later retreval
|
||||
const multipleBotTypesRequested = info.conditions.length > 1;
|
||||
if (multipleBotTypesRequested)
|
||||
{
|
||||
if (multipleBotTypesRequested) {
|
||||
return this.generateMultipleBotsAndCache(info, pmcProfile, sessionId);
|
||||
}
|
||||
|
||||
@ -206,26 +191,23 @@ export class BotController
|
||||
request: IGenerateBotsRequestData,
|
||||
pmcProfile: IPmcData,
|
||||
sessionId: string,
|
||||
): Promise<IBotBase[]>
|
||||
{
|
||||
): Promise<IBotBase[]> {
|
||||
const raidSettings = this.applicationContext
|
||||
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
||||
?.getValue<IGetRaidConfigurationRequestData>();
|
||||
|
||||
if (raidSettings === undefined)
|
||||
{
|
||||
if (raidSettings === undefined) {
|
||||
// throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext"));
|
||||
}
|
||||
const pmcLevelRangeForMap
|
||||
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings?.location.toLowerCase()];
|
||||
const pmcLevelRangeForMap =
|
||||
this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings?.location.toLowerCase()];
|
||||
|
||||
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(
|
||||
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
|
||||
);
|
||||
|
||||
const conditionPromises: Promise<void>[] = [];
|
||||
for (const condition of request.conditions)
|
||||
{
|
||||
for (const condition of request.conditions) {
|
||||
const botGenerationDetails = this.getBotGenerationDetailsForWave(
|
||||
condition,
|
||||
pmcProfile,
|
||||
@ -260,8 +242,7 @@ export class BotController
|
||||
pmcLevelRangeForMap: MinMax,
|
||||
botCountToGenerate: number,
|
||||
generateAsPmc: boolean,
|
||||
): BotGenerationDetails
|
||||
{
|
||||
): BotGenerationDetails {
|
||||
return {
|
||||
isPmc: generateAsPmc,
|
||||
side: "Savage",
|
||||
@ -283,8 +264,7 @@ export class BotController
|
||||
* @param pmcProfile Profile to get level from
|
||||
* @returns Level as number
|
||||
*/
|
||||
protected getPlayerLevelFromProfile(pmcProfile: IPmcData): number
|
||||
{
|
||||
protected getPlayerLevelFromProfile(pmcProfile: IPmcData): number {
|
||||
return pmcProfile.Info.Level;
|
||||
}
|
||||
|
||||
@ -299,11 +279,9 @@ export class BotController
|
||||
condition: Condition,
|
||||
botGenerationDetails: BotGenerationDetails,
|
||||
sessionId: string,
|
||||
): Promise<void>
|
||||
{
|
||||
): Promise<void> {
|
||||
const isEventBot = condition.Role.toLowerCase().includes("event");
|
||||
if (isEventBot)
|
||||
{
|
||||
if (isEventBot) {
|
||||
// Add eventRole data + reassign role property to be base type
|
||||
botGenerationDetails.eventRole = condition.Role;
|
||||
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
|
||||
@ -313,8 +291,7 @@ export class BotController
|
||||
|
||||
// Custom map waves can have spt roles in them
|
||||
// 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.side = this.botHelper.getPmcSideByRole(condition.Role);
|
||||
}
|
||||
@ -328,20 +305,17 @@ export class BotController
|
||||
// Get number of bots we have in cache
|
||||
const botCacheCount = this.botGenerationCacheService.getCachedBotCount(cacheKey);
|
||||
const botPromises: Promise<void>[] = [];
|
||||
if (botCacheCount > botGenerationDetails.botCountToGenerate)
|
||||
{
|
||||
if (botCacheCount > botGenerationDetails.botCountToGenerate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey));
|
||||
}
|
||||
|
||||
return Promise.all(botPromises).then(() =>
|
||||
{
|
||||
return Promise.all(botPromises).then(() => {
|
||||
this.logger.debug(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? ""
|
||||
@ -361,8 +335,7 @@ export class BotController
|
||||
botGenerationDetails: BotGenerationDetails,
|
||||
sessionId: string,
|
||||
cacheKey: string,
|
||||
): Promise<void>
|
||||
{
|
||||
): Promise<void> {
|
||||
const botToCache = this.botGenerator.prepareAndGenerateBot(sessionId, botGenerationDetails);
|
||||
this.botGenerationCacheService.storeBots(cacheKey, [botToCache]);
|
||||
|
||||
@ -379,8 +352,7 @@ export class BotController
|
||||
protected async returnSingleBotFromCache(
|
||||
sessionId: string,
|
||||
request: IGenerateBotsRequestData,
|
||||
): Promise<IBotBase[]>
|
||||
{
|
||||
): Promise<IBotBase[]> {
|
||||
const pmcProfile = this.profileHelper.getPmcProfile(sessionId);
|
||||
const requestedBot = request.conditions[0];
|
||||
|
||||
@ -388,12 +360,11 @@ export class BotController
|
||||
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
||||
?.getValue<IGetRaidConfigurationRequestData>();
|
||||
|
||||
if (raidSettings === undefined)
|
||||
{
|
||||
if (raidSettings === undefined) {
|
||||
throw new Error(this.localisationService.getText("bot-unable_to_load_raid_settings_from_appcontext"));
|
||||
}
|
||||
const pmcLevelRangeForMap
|
||||
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
|
||||
const pmcLevelRangeForMap =
|
||||
this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
|
||||
|
||||
// Create gen request for when cache is empty
|
||||
const condition: Condition = {
|
||||
@ -412,8 +383,7 @@ export class BotController
|
||||
|
||||
// Event bots need special actions to occur, set data up for them
|
||||
const isEventBot = requestedBot.Role.toLowerCase().includes("event");
|
||||
if (isEventBot)
|
||||
{
|
||||
if (isEventBot) {
|
||||
// Add eventRole data + reassign role property
|
||||
botGenerationDetails.eventRole = requestedBot.Role;
|
||||
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.side = this.botHelper.getPmcSideByRole(requestedBot.Role);
|
||||
}
|
||||
|
||||
// Roll chance to be pmc if type is allowed to be one
|
||||
const botConvertRateMinMax = this.pmcConfig.convertIntoPmcChance[requestedBot.Role.toLowerCase()];
|
||||
if (botConvertRateMinMax)
|
||||
{
|
||||
if (botConvertRateMinMax) {
|
||||
// Should bot become PMC
|
||||
const convertToPmc = this.botHelper.rollChanceToBePmc(requestedBot.Role, botConvertRateMinMax);
|
||||
if (convertToPmc)
|
||||
{
|
||||
if (convertToPmc) {
|
||||
botGenerationDetails.isPmc = true;
|
||||
botGenerationDetails.role = this.botHelper.getRandomizedPmcRole();
|
||||
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
|
||||
const {
|
||||
bossConvertEnabled,
|
||||
bossConvertMinMax,
|
||||
bossesToConvertToWeights } = this.botConfig.assaultToBossConversion;
|
||||
if (bossConvertEnabled && !botGenerationDetails.isPmc)
|
||||
{
|
||||
const { bossConvertEnabled, bossConvertMinMax, bossesToConvertToWeights } =
|
||||
this.botConfig.assaultToBossConversion;
|
||||
if (bossConvertEnabled && !botGenerationDetails.isPmc) {
|
||||
const bossConvertPercent = bossConvertMinMax[requestedBot.Role.toLowerCase()];
|
||||
if (bossConvertPercent)
|
||||
{
|
||||
if (bossConvertPercent) {
|
||||
// Roll a percentage check if we should convert scav to boss
|
||||
if (this.randomUtil.getChance100(
|
||||
this.randomUtil.getInt(bossConvertPercent.min, bossConvertPercent.max)))
|
||||
{
|
||||
this.updateBotGenerationDetailsToRandomBoss(
|
||||
botGenerationDetails,
|
||||
bossesToConvertToWeights);
|
||||
if (
|
||||
this.randomUtil.getChance100(this.randomUtil.getInt(bossConvertPercent.min, bossConvertPercent.max))
|
||||
) {
|
||||
this.updateBotGenerationDetailsToRandomBoss(botGenerationDetails, bossesToConvertToWeights);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -466,20 +427,18 @@ export class BotController
|
||||
// Create a compound key to store bots in cache against
|
||||
const cacheKey = this.botGenerationCacheService.createCacheKey(
|
||||
botGenerationDetails.eventRole ?? botGenerationDetails.role,
|
||||
botGenerationDetails.botDifficulty);
|
||||
botGenerationDetails.botDifficulty,
|
||||
);
|
||||
|
||||
// Check cache for bot using above key
|
||||
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey))
|
||||
{
|
||||
if (!this.botGenerationCacheService.cacheHasBotOfRole(cacheKey)) {
|
||||
const botPromises: Promise<void>[] = [];
|
||||
// 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));
|
||||
}
|
||||
|
||||
await Promise.all(botPromises).then(() =>
|
||||
{
|
||||
await Promise.all(botPromises).then(() => {
|
||||
this.logger.debug(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${botGenerationDetails.role} (${
|
||||
botGenerationDetails.eventRole ?? ""
|
||||
@ -496,11 +455,10 @@ export class BotController
|
||||
|
||||
protected updateBotGenerationDetailsToRandomBoss(
|
||||
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
|
||||
botGenerationDetails.role
|
||||
= this.weightedRandomHelper.getWeightedValue(possibleBossTypeWeights);
|
||||
botGenerationDetails.role = this.weightedRandomHelper.getWeightedValue(possibleBossTypeWeights);
|
||||
|
||||
// Bosses are only ever 'normal'
|
||||
botGenerationDetails.botDifficulty = "normal";
|
||||
@ -512,16 +470,13 @@ export class BotController
|
||||
* @param requestedDifficulty
|
||||
* @returns
|
||||
*/
|
||||
public getPMCDifficulty(requestedDifficulty: string): string
|
||||
{
|
||||
public getPMCDifficulty(requestedDifficulty: string): string {
|
||||
// Maybe return a random difficulty...
|
||||
if (this.pmcConfig.difficulty.toLowerCase() === "asonline")
|
||||
{
|
||||
if (this.pmcConfig.difficulty.toLowerCase() === "asonline") {
|
||||
return requestedDifficulty;
|
||||
}
|
||||
|
||||
if (this.pmcConfig.difficulty.toLowerCase() === "random")
|
||||
{
|
||||
if (this.pmcConfig.difficulty.toLowerCase() === "random") {
|
||||
return this.botDifficultyHelper.chooseRandomDifficulty();
|
||||
}
|
||||
|
||||
@ -534,24 +489,18 @@ export class BotController
|
||||
* @param location The map location cap was requested for
|
||||
* @returns cap number
|
||||
*/
|
||||
public getBotCap(location: string): number
|
||||
{
|
||||
public getBotCap(location: string): number {
|
||||
const botCap = this.botConfig.maxBotCap[location.toLowerCase()];
|
||||
if (location === "default")
|
||||
{
|
||||
if (location === "default") {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText(
|
||||
"bot-no_bot_cap_found_for_location",
|
||||
location.toLowerCase(),
|
||||
),
|
||||
this.localisationService.getText("bot-no_bot_cap_found_for_location", location.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
return botCap;
|
||||
}
|
||||
|
||||
public getAiBotBrainTypes(): any
|
||||
{
|
||||
public getAiBotBrainTypes(): any {
|
||||
return {
|
||||
pmc: this.pmcConfig.pmcType,
|
||||
assault: this.botConfig.assaultBrainType,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
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 { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class BuildController
|
||||
{
|
||||
export class BuildController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@ -27,16 +26,13 @@ export class BuildController
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/** Handle client/handbook/builds/my/list */
|
||||
public getUserBuilds(sessionID: string): IUserBuilds
|
||||
{
|
||||
public getUserBuilds(sessionID: string): IUserBuilds {
|
||||
const secureContainerSlotId = "SecuredContainer";
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
if (!profile.userbuilds)
|
||||
{
|
||||
if (!profile.userbuilds) {
|
||||
profile.userbuilds = { equipmentBuilds: [], weaponBuilds: [], magazineBuilds: [] };
|
||||
}
|
||||
|
||||
@ -50,15 +46,12 @@ export class BuildController
|
||||
const firstDefaultItemsSecureContainer = defaultEquipmentPresetsClone[0]?.Items?.find(
|
||||
(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
|
||||
for (const defaultPreset of defaultEquipmentPresetsClone)
|
||||
{
|
||||
for (const defaultPreset of defaultEquipmentPresetsClone) {
|
||||
// Find presets secure container
|
||||
const secureContainer = defaultPreset.Items.find((item) => item.slotId === secureContainerSlotId);
|
||||
if (secureContainer)
|
||||
{
|
||||
if (secureContainer) {
|
||||
secureContainer._tpl = playerSecureContainer._tpl;
|
||||
}
|
||||
}
|
||||
@ -72,8 +65,7 @@ export class BuildController
|
||||
}
|
||||
|
||||
/** 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);
|
||||
|
||||
// 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 existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
|
||||
if (existingBuild)
|
||||
{
|
||||
if (existingBuild) {
|
||||
// exists, replace
|
||||
this.saveServer
|
||||
.getProfile(sessionId)
|
||||
.userbuilds.weaponBuilds.splice(savedWeaponBuilds.indexOf(existingBuild), 1, newBuild);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Add fresh
|
||||
this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds.push(newBuild);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle client/builds/equipment/save event */
|
||||
public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void
|
||||
{
|
||||
public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void {
|
||||
const buildType = "equipmentBuilds";
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
|
||||
const existingSavedEquipmentBuilds: IEquipmentBuild[]
|
||||
= this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
||||
const existingSavedEquipmentBuilds: IEquipmentBuild[] =
|
||||
this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
||||
|
||||
// Replace duplicate ID's. The first item is the base item.
|
||||
// Root ID and the base item ID need to match.
|
||||
@ -124,28 +112,23 @@ export class BuildController
|
||||
const existingBuild = existingSavedEquipmentBuilds.find(
|
||||
(build) => build.Name === request.Name || build.Id === request.Id,
|
||||
);
|
||||
if (existingBuild)
|
||||
{
|
||||
if (existingBuild) {
|
||||
// Already exists, replace
|
||||
this.saveServer
|
||||
.getProfile(sessionID)
|
||||
.userbuilds[buildType].splice(existingSavedEquipmentBuilds.indexOf(existingBuild), 1, newBuild);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Fresh, add new
|
||||
this.saveServer.getProfile(sessionID).userbuilds[buildType].push(newBuild);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle client/builds/delete */
|
||||
public removeBuild(sessionID: string, request: IRemoveBuildRequestData): void
|
||||
{
|
||||
public removeBuild(sessionID: string, request: IRemoveBuildRequestData): void {
|
||||
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 weaponBuilds = profile.userbuilds.weaponBuilds;
|
||||
const equipmentBuilds = profile.userbuilds.equipmentBuilds;
|
||||
@ -153,8 +136,7 @@ export class BuildController
|
||||
|
||||
// Check for id in weapon array first
|
||||
const matchingWeaponBuild = weaponBuilds.find((weaponBuild) => weaponBuild.Id === idToRemove);
|
||||
if (matchingWeaponBuild)
|
||||
{
|
||||
if (matchingWeaponBuild) {
|
||||
weaponBuilds.splice(weaponBuilds.indexOf(matchingWeaponBuild), 1);
|
||||
|
||||
return;
|
||||
@ -162,8 +144,7 @@ export class BuildController
|
||||
|
||||
// Id not found in weapons, try equipment
|
||||
const matchingEquipmentBuild = equipmentBuilds.find((equipmentBuild) => equipmentBuild.Id === idToRemove);
|
||||
if (matchingEquipmentBuild)
|
||||
{
|
||||
if (matchingEquipmentBuild) {
|
||||
equipmentBuilds.splice(equipmentBuilds.indexOf(matchingEquipmentBuild), 1);
|
||||
|
||||
return;
|
||||
@ -171,8 +152,7 @@ export class BuildController
|
||||
|
||||
// Id not found in weapons/equipment, try mags
|
||||
const matchingMagazineBuild = magazineBuilds.find((magBuild) => magBuild.Id === idToRemove);
|
||||
if (matchingMagazineBuild)
|
||||
{
|
||||
if (matchingMagazineBuild) {
|
||||
magazineBuilds.splice(magazineBuilds.indexOf(matchingMagazineBuild), 1);
|
||||
|
||||
return;
|
||||
@ -185,8 +165,7 @@ export class BuildController
|
||||
/**
|
||||
* Handle client/builds/magazine/save
|
||||
*/
|
||||
public createMagazineTemplate(sessionId: string, request: ISetMagazineRequest): void
|
||||
{
|
||||
public createMagazineTemplate(sessionId: string, request: ISetMagazineRequest): void {
|
||||
const result: IMagazineBuild = {
|
||||
Id: request.Id,
|
||||
Name: request.Name,
|
||||
@ -201,12 +180,9 @@ export class BuildController
|
||||
profile.userbuilds.magazineBuilds ||= [];
|
||||
|
||||
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name);
|
||||
if (existingArrayId === -1)
|
||||
{
|
||||
if (existingArrayId === -1) {
|
||||
profile.userbuilds.magazineBuilds.push(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,18 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { IClientLogRequest } from "@spt/models/spt/logging/IClientLogRequest";
|
||||
import { LogBackgroundColor } from "@spt/models/spt/logging/LogBackgroundColor";
|
||||
import { LogLevel } from "@spt/models/spt/logging/LogLevel";
|
||||
import { LogTextColor } from "@spt/models/spt/logging/LogTextColor";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class ClientLogController
|
||||
{
|
||||
constructor(@inject("PrimaryLogger") protected logger: ILogger)
|
||||
{}
|
||||
export class ClientLogController {
|
||||
constructor(@inject("PrimaryLogger") protected logger: ILogger) {}
|
||||
|
||||
/**
|
||||
* Handle /singleplayer/log
|
||||
*/
|
||||
public clientLog(logRequest: IClientLogRequest): void
|
||||
{
|
||||
public clientLog(logRequest: IClientLogRequest): void {
|
||||
const message = `[${logRequest.Source}] ${logRequest.Message}`;
|
||||
const color = logRequest.Color ?? LogTextColor.WHITE;
|
||||
const backgroundColor = logRequest.BackgroundColor ?? LogBackgroundColor.DEFAULT;
|
||||
@ -23,13 +20,11 @@ export class ClientLogController
|
||||
// Allow supporting either string or enum levels
|
||||
// Required due to the C# modules serializing enums as their name
|
||||
let level = logRequest.Level;
|
||||
if (typeof level === "string")
|
||||
{
|
||||
if (typeof level === "string") {
|
||||
level = LogLevel[level.toUpperCase() as keyof typeof LogLevel];
|
||||
}
|
||||
|
||||
switch (level)
|
||||
{
|
||||
switch (level) {
|
||||
case LogLevel.ERROR:
|
||||
this.logger.error(message);
|
||||
break;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
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 { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class CustomizationController
|
||||
{
|
||||
export class CustomizationController {
|
||||
protected readonly clothingIds = {
|
||||
lowerParentId: "5cd944d01388ce000a659df9",
|
||||
upperParentId: "5cd944ca1388ce03a44dc2a4",
|
||||
@ -26,8 +25,7 @@ export class CustomizationController
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get purchasable clothing items from trader that match players side (usec/bear)
|
||||
@ -35,8 +33,7 @@ export class CustomizationController
|
||||
* @param sessionID Session id
|
||||
* @returns ISuit array
|
||||
*/
|
||||
public getTraderSuits(traderID: string, sessionID: string): ISuit[]
|
||||
{
|
||||
public getTraderSuits(traderID: string, sessionID: string): ISuit[] {
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
const clothing = this.databaseService.getCustomization();
|
||||
const suits = this.databaseService.getTrader(traderID).suits;
|
||||
@ -45,7 +42,9 @@ export class CustomizationController
|
||||
const matchingSuits = suits?.filter((suit) => suit.suiteId in clothing);
|
||||
|
||||
// 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)
|
||||
throw new Error(this.localisationService.getText("customisation-unable_to_get_trader_suits", traderID));
|
||||
|
||||
@ -60,22 +59,18 @@ export class CustomizationController
|
||||
pmcData: IPmcData,
|
||||
wearClothingRequest: IWearClothingRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
for (const suitId of wearClothingRequest.suites)
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
for (const suitId of wearClothingRequest.suites) {
|
||||
// Find desired clothing item in db
|
||||
const dbSuit = this.databaseService.getCustomization()[suitId];
|
||||
|
||||
// Legs
|
||||
if (dbSuit._parent === this.clothingIds.lowerParentId)
|
||||
{
|
||||
if (dbSuit._parent === this.clothingIds.lowerParentId) {
|
||||
pmcData.Customization.Feet = dbSuit._props.Feet;
|
||||
}
|
||||
|
||||
// Torso
|
||||
if (dbSuit._parent === this.clothingIds.upperParentId)
|
||||
{
|
||||
if (dbSuit._parent === this.clothingIds.upperParentId) {
|
||||
pmcData.Customization.Body = dbSuit._props.Body;
|
||||
pmcData.Customization.Hands = dbSuit._props.Hands;
|
||||
}
|
||||
@ -96,13 +91,11 @@ export class CustomizationController
|
||||
pmcData: IPmcData,
|
||||
buyClothingRequest: IBuyClothingRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
|
||||
if (!traderOffer)
|
||||
{
|
||||
if (!traderOffer) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer),
|
||||
);
|
||||
@ -111,8 +104,7 @@ export class CustomizationController
|
||||
}
|
||||
|
||||
const suitId = traderOffer.suiteId;
|
||||
if (this.outfitAlreadyPurchased(suitId, sessionId))
|
||||
{
|
||||
if (this.outfitAlreadyPurchased(suitId, sessionId)) {
|
||||
const suitDetails = this.databaseService.getCustomization()[suitId];
|
||||
this.logger.error(
|
||||
this.localisationService.getText("customisation-item_already_purchased", {
|
||||
@ -133,11 +125,9 @@ export class CustomizationController
|
||||
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);
|
||||
if (foundSuit === undefined)
|
||||
{
|
||||
if (foundSuit === undefined) {
|
||||
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
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -167,10 +156,8 @@ export class CustomizationController
|
||||
pmcData: IPmcData,
|
||||
clothingItems: ClothingItem[],
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
for (const sellItem of clothingItems)
|
||||
{
|
||||
): void {
|
||||
for (const sellItem of clothingItems) {
|
||||
this.payForClothingItem(sessionId, pmcData, sellItem, output);
|
||||
}
|
||||
}
|
||||
@ -187,11 +174,9 @@ export class CustomizationController
|
||||
pmcData: IPmcData,
|
||||
clothingItem: ClothingItem,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const relatedItem = pmcData.Inventory.items.find((x) => x._id === clothingItem.id);
|
||||
if (!relatedItem)
|
||||
{
|
||||
if (!relatedItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"customisation-unable_to_find_clothing_item_in_inventory",
|
||||
@ -202,19 +187,18 @@ export class CustomizationController
|
||||
return;
|
||||
}
|
||||
|
||||
if (clothingItem.del === true)
|
||||
{
|
||||
if (clothingItem.del === true) {
|
||||
output.profileChanges[sessionId].items.del.push(relatedItem);
|
||||
pmcData.Inventory.items.splice(pmcData.Inventory.items.indexOf(relatedItem), 1);
|
||||
}
|
||||
|
||||
if (!relatedItem.upd || !relatedItem.upd.StackObjectsCount)
|
||||
{
|
||||
throw new Error(this.localisationService.getText("customisation-suit_lacks_upd_or_stack_property", relatedItem._tpl));
|
||||
if (!relatedItem.upd || !relatedItem.upd.StackObjectsCount) {
|
||||
throw new Error(
|
||||
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;
|
||||
output.profileChanges[sessionId].items.change.push({
|
||||
_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();
|
||||
let result: ISuit[] = [];
|
||||
|
||||
for (const traderID in traders)
|
||||
{
|
||||
if (traders[traderID].base.customization_seller === true)
|
||||
{
|
||||
for (const traderID in traders) {
|
||||
if (traders[traderID].base.customization_seller === true) {
|
||||
result = [...result, ...this.getTraderSuits(traderID, sessionID)];
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectAll, injectable } from "tsyringe";
|
||||
import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot";
|
||||
import { DialogueHelper } from "@spt/helpers/DialogueHelper";
|
||||
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 { MailSendService } from "@spt/services/MailSendService";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectAll, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class DialogueController
|
||||
{
|
||||
export class DialogueController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@ -31,37 +30,36 @@ export class DialogueController
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@injectAll("DialogueChatBot") protected dialogueChatBots: IDialogueChatBot[],
|
||||
)
|
||||
{
|
||||
) {
|
||||
const coreConfigs = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE);
|
||||
// if give command is disabled or commando commands are disabled
|
||||
if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled)
|
||||
{
|
||||
const sptCommando = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando")!;
|
||||
if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled) {
|
||||
const sptCommando = this.dialogueChatBots.find(
|
||||
(c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando",
|
||||
)!;
|
||||
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1);
|
||||
}
|
||||
if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled)
|
||||
{
|
||||
const sptFriend = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend")!;
|
||||
if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled) {
|
||||
const sptFriend = this.dialogueChatBots.find(
|
||||
(c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend",
|
||||
)!;
|
||||
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1);
|
||||
}
|
||||
}
|
||||
|
||||
public registerChatBot(chatBot: IDialogueChatBot): void
|
||||
{
|
||||
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));
|
||||
public registerChatBot(chatBot: IDialogueChatBot): void {
|
||||
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),
|
||||
);
|
||||
}
|
||||
this.dialogueChatBots.push(chatBot);
|
||||
}
|
||||
|
||||
/** Handle onUpdate spt event */
|
||||
public update(): void
|
||||
{
|
||||
public update(): void {
|
||||
const profiles = this.saveServer.getProfiles();
|
||||
for (const sessionID in profiles)
|
||||
{
|
||||
for (const sessionID in profiles) {
|
||||
this.removeExpiredItemsFromMessages(sessionID);
|
||||
}
|
||||
}
|
||||
@ -70,8 +68,7 @@ export class DialogueController
|
||||
* Handle client/friend/list
|
||||
* @returns IGetFriendListDataResponse
|
||||
*/
|
||||
public getFriendList(sessionID: string): IGetFriendListDataResponse
|
||||
{
|
||||
public getFriendList(sessionID: string): IGetFriendListDataResponse {
|
||||
// Force a fake friend called SPT into friend list
|
||||
return { Friends: this.dialogueChatBots.map((v) => v.getChatBot()), Ignore: [], InIgnoreList: [] };
|
||||
}
|
||||
@ -83,11 +80,9 @@ export class DialogueController
|
||||
* @param sessionID Session Id
|
||||
* @returns array of dialogs
|
||||
*/
|
||||
public generateDialogueList(sessionID: string): DialogueInfo[]
|
||||
{
|
||||
public generateDialogueList(sessionID: string): 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));
|
||||
}
|
||||
|
||||
@ -100,8 +95,7 @@ export class DialogueController
|
||||
* @param sessionID Session Id
|
||||
* @returns DialogueInfo
|
||||
*/
|
||||
public getDialogueInfo(dialogueID: string, sessionID: string): DialogueInfo
|
||||
{
|
||||
public getDialogueInfo(dialogueID: string, sessionID: string): DialogueInfo {
|
||||
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionID);
|
||||
const dialogue = dialogs[dialogueID];
|
||||
|
||||
@ -125,18 +119,19 @@ export class DialogueController
|
||||
* @param sessionID Player id
|
||||
* @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);
|
||||
|
||||
// User to user messages are special in that they need the player to exist in them, add if they don't
|
||||
if (
|
||||
messageType === MessageType.USER_MESSAGE
|
||||
&& !dialog.Users?.some((userDialog) => userDialog._id === profile.characters.pmc.sessionId)
|
||||
)
|
||||
{
|
||||
if (!dialog.Users)
|
||||
{
|
||||
messageType === MessageType.USER_MESSAGE &&
|
||||
!dialog.Users?.some((userDialog) => userDialog._id === profile.characters.pmc.sessionId)
|
||||
) {
|
||||
if (!dialog.Users) {
|
||||
dialog.Users = [];
|
||||
}
|
||||
|
||||
@ -168,8 +163,7 @@ export class DialogueController
|
||||
public generateDialogueView(
|
||||
request: IGetMailDialogViewRequestData,
|
||||
sessionId: string,
|
||||
): IGetMailDialogViewResponseData
|
||||
{
|
||||
): IGetMailDialogViewResponseData {
|
||||
const dialogueId = request.dialogId;
|
||||
const fullProfile = this.saveServer.getProfile(sessionId);
|
||||
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)
|
||||
* @returns Dialogue
|
||||
*/
|
||||
protected getDialogByIdFromProfile(profile: ISptProfile, request: IGetMailDialogViewRequestData): Dialogue
|
||||
{
|
||||
if (!profile.dialogues[request.dialogId])
|
||||
{
|
||||
protected getDialogByIdFromProfile(profile: ISptProfile, request: IGetMailDialogViewRequestData): Dialogue {
|
||||
if (!profile.dialogues[request.dialogId]) {
|
||||
profile.dialogues[request.dialogId] = {
|
||||
_id: request.dialogId,
|
||||
attachmentsNew: 0,
|
||||
@ -206,14 +198,11 @@ export class DialogueController
|
||||
type: request.type,
|
||||
};
|
||||
|
||||
if (request.type === MessageType.USER_MESSAGE)
|
||||
{
|
||||
if (request.type === MessageType.USER_MESSAGE) {
|
||||
profile.dialogues[request.dialogId].Users = [];
|
||||
const chatBot = this.dialogueChatBots.find((cb) => cb.getChatBot()._id === request.dialogId);
|
||||
if (chatBot)
|
||||
{
|
||||
if (!profile.dialogues[request.dialogId].Users)
|
||||
{
|
||||
if (chatBot) {
|
||||
if (!profile.dialogues[request.dialogId].Users) {
|
||||
profile.dialogues[request.dialogId].Users = [];
|
||||
}
|
||||
profile.dialogues[request.dialogId].Users!.push(chatBot.getChatBot());
|
||||
@ -230,15 +219,12 @@ export class DialogueController
|
||||
* @param dialogUsers The participants of the mail
|
||||
* @returns IUserDialogInfo array
|
||||
*/
|
||||
protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers?: IUserDialogInfo[]): IUserDialogInfo[]
|
||||
{
|
||||
protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers?: IUserDialogInfo[]): IUserDialogInfo[] {
|
||||
const result: IUserDialogInfo[] = [];
|
||||
if (dialogUsers)
|
||||
{
|
||||
if (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
|
||||
const pmcProfile = fullProfile.characters.pmc;
|
||||
result.push({
|
||||
@ -264,14 +250,11 @@ export class DialogueController
|
||||
* @param dialogueID Dialog id
|
||||
* @returns Count of messages with attachments
|
||||
*/
|
||||
protected getUnreadMessagesWithAttachmentsCount(sessionID: string, dialogueID: string): number
|
||||
{
|
||||
protected getUnreadMessagesWithAttachmentsCount(sessionID: string, dialogueID: string): number {
|
||||
let newAttachmentCount = 0;
|
||||
const activeMessages = this.getActiveMessagesFromDialog(sessionID, dialogueID);
|
||||
for (const message of activeMessages)
|
||||
{
|
||||
if (message.hasRewards && !message.rewardCollected)
|
||||
{
|
||||
for (const message of activeMessages) {
|
||||
if (message.hasRewards && !message.rewardCollected) {
|
||||
newAttachmentCount++;
|
||||
}
|
||||
}
|
||||
@ -284,8 +267,7 @@ export class DialogueController
|
||||
* @param messages Messages to check
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -295,13 +277,16 @@ export class DialogueController
|
||||
* @param dialogueId id of the dialog to remove
|
||||
* @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 dialog = profile.dialogues[dialogueId];
|
||||
if (!dialog)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId }));
|
||||
if (!dialog) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("dialogue-unable_to_find_in_profile", {
|
||||
sessionId: sessionId,
|
||||
dialogueId: dialogueId,
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -310,12 +295,15 @@ export class DialogueController
|
||||
}
|
||||
|
||||
/** 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];
|
||||
if (!dialog)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId }));
|
||||
if (!dialog) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("dialogue-unable_to_find_in_profile", {
|
||||
sessionId: sessionId,
|
||||
dialogueId: dialogueId,
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -329,18 +317,19 @@ export class DialogueController
|
||||
* @param dialogueIds Dialog ids to set as read
|
||||
* @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);
|
||||
if (!dialogs)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_dialogs_in_profile", { sessionId: sessionId }));
|
||||
if (!dialogs) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("dialogue-unable_to_find_dialogs_in_profile", {
|
||||
sessionId: sessionId,
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const dialogId of dialogueIds)
|
||||
{
|
||||
for (const dialogId of dialogueIds) {
|
||||
dialogs[dialogId].new = 0;
|
||||
dialogs[dialogId].attachmentsNew = 0;
|
||||
}
|
||||
@ -353,13 +342,16 @@ export class DialogueController
|
||||
* @param sessionId Session id
|
||||
* @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 dialog = dialogs[dialogueId];
|
||||
if (!dialog)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId }));
|
||||
if (!dialog) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("dialogue-unable_to_find_in_profile", {
|
||||
sessionId: sessionId,
|
||||
dialogueId: dialogueId,
|
||||
}),
|
||||
);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@ -378,8 +370,7 @@ export class DialogueController
|
||||
}
|
||||
|
||||
/** 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);
|
||||
|
||||
return (
|
||||
@ -395,8 +386,7 @@ export class DialogueController
|
||||
* @param dialogueId Dialog to get mail attachments from
|
||||
* @returns Message array
|
||||
*/
|
||||
protected getActiveMessagesFromDialog(sessionId: string, dialogueId: string): Message[]
|
||||
{
|
||||
protected getActiveMessagesFromDialog(sessionId: string, dialogueId: string): Message[] {
|
||||
const timeNow = this.timeUtil.getTimestamp();
|
||||
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
|
||||
return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + (message.maxStorageTime ?? 0));
|
||||
@ -407,8 +397,7 @@ export class DialogueController
|
||||
* @param messages Messages to parse
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -416,10 +405,8 @@ export class DialogueController
|
||||
* Delete expired items from all messages in player profile. triggers when updating traders.
|
||||
* @param sessionId Session id
|
||||
*/
|
||||
protected removeExpiredItemsFromMessages(sessionId: string): void
|
||||
{
|
||||
for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionId))
|
||||
{
|
||||
protected removeExpiredItemsFromMessages(sessionId: string): void {
|
||||
for (const dialogueId in this.dialogueHelper.getDialogsForProfile(sessionId)) {
|
||||
this.removeExpiredItemsFromMessage(sessionId, dialogueId);
|
||||
}
|
||||
}
|
||||
@ -429,19 +416,15 @@ export class DialogueController
|
||||
* @param sessionId Session 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 dialog = dialogs[dialogueId];
|
||||
if (!dialog.messages)
|
||||
{
|
||||
if (!dialog.messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const message of dialog.messages)
|
||||
{
|
||||
if (this.messageHasExpired(message))
|
||||
{
|
||||
for (const message of dialog.messages) {
|
||||
if (this.messageHasExpired(message)) {
|
||||
message.items = {};
|
||||
}
|
||||
}
|
||||
@ -452,14 +435,12 @@ export class DialogueController
|
||||
* @param message Message to check expiry of
|
||||
* @returns true or false
|
||||
*/
|
||||
protected messageHasExpired(message: Message): boolean
|
||||
{
|
||||
protected messageHasExpired(message: Message): boolean {
|
||||
return this.timeUtil.getTimestamp() > message.dt + (message.maxStorageTime ?? 0);
|
||||
}
|
||||
|
||||
/** 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 };
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
import { HideoutHelper } from "@spt/helpers/HideoutHelper";
|
||||
@ -44,14 +43,14 @@ import { ProfileActivityService } from "@spt/services/ProfileActivityService";
|
||||
import { ProfileFixerService } from "@spt/services/ProfileFixerService";
|
||||
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class GameController
|
||||
{
|
||||
export class GameController {
|
||||
protected httpConfig: IHttpConfig;
|
||||
protected coreConfig: ICoreConfig;
|
||||
protected locationConfig: ILocationConfig;
|
||||
@ -83,8 +82,7 @@ export class GameController
|
||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
|
||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
|
||||
@ -95,8 +93,7 @@ export class GameController
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
}
|
||||
|
||||
public load(): void
|
||||
{
|
||||
public load(): void {
|
||||
// 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
|
||||
// add items gets left out,causing warnings
|
||||
@ -107,30 +104,25 @@ export class GameController
|
||||
/**
|
||||
* 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
|
||||
this.applicationContext.addValue(ContextVariableType.CLIENT_START_TIMESTAMP, startTimeStampMS);
|
||||
|
||||
this.profileActivityService.setActivityTimestamp(sessionID);
|
||||
|
||||
if (this.coreConfig.fixes.fixShotgunDispersion)
|
||||
{
|
||||
if (this.coreConfig.fixes.fixShotgunDispersion) {
|
||||
this.fixShotgunDispersions();
|
||||
}
|
||||
|
||||
if (this.locationConfig.addOpenZonesToAllMaps)
|
||||
{
|
||||
if (this.locationConfig.addOpenZonesToAllMaps) {
|
||||
this.openZoneService.applyZoneChangesToAllMaps();
|
||||
}
|
||||
|
||||
if (this.locationConfig.addCustomBotWavesToMaps)
|
||||
{
|
||||
if (this.locationConfig.addCustomBotWavesToMaps) {
|
||||
this.customLocationWaveService.applyWaveChangesToAllMaps();
|
||||
}
|
||||
|
||||
if (this.locationConfig.enableBotTypeLimits)
|
||||
{
|
||||
if (this.locationConfig.enableBotTypeLimits) {
|
||||
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
|
||||
// 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
|
||||
if (sessionID)
|
||||
{
|
||||
if (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
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(fullProfile.characters.pmc.WishList))
|
||||
{
|
||||
if (Array.isArray(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 = {};
|
||||
}
|
||||
|
||||
@ -167,34 +155,28 @@ export class GameController
|
||||
this.logger.debug(`Started game with sessionId: ${sessionID} ${pmcProfile.Info?.Nickname}`);
|
||||
|
||||
// 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);
|
||||
delete (fullProfile as any).aki;
|
||||
}
|
||||
|
||||
if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues)
|
||||
{
|
||||
if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues) {
|
||||
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile);
|
||||
}
|
||||
|
||||
if (pmcProfile.Health)
|
||||
{
|
||||
if (pmcProfile.Health) {
|
||||
this.updateProfileHealthValues(pmcProfile);
|
||||
}
|
||||
|
||||
if (this.locationConfig.fixEmptyBotWavesSettings.enabled)
|
||||
{
|
||||
if (this.locationConfig.fixEmptyBotWavesSettings.enabled) {
|
||||
this.fixBrokenOfflineMapWaves();
|
||||
}
|
||||
|
||||
if (this.locationConfig.rogueLighthouseSpawnTimeSettings.enabled)
|
||||
{
|
||||
if (this.locationConfig.rogueLighthouseSpawnTimeSettings.enabled) {
|
||||
this.fixRoguesSpawningInstantlyOnLighthouse();
|
||||
}
|
||||
|
||||
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.enabled)
|
||||
{
|
||||
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.enabled) {
|
||||
this.splitBotWavesIntoSingleWaves();
|
||||
}
|
||||
|
||||
@ -202,8 +184,7 @@ export class GameController
|
||||
|
||||
this.profileFixerService.addMissingHideoutAreasToProfile(fullProfile);
|
||||
|
||||
if (pmcProfile.Inventory)
|
||||
{
|
||||
if (pmcProfile.Inventory) {
|
||||
// MUST occur prior to `profileFixerService.checkForAndFixPmcProfileIssues()`
|
||||
this.profileFixerService.fixIncorrectAidValue(fullProfile);
|
||||
|
||||
@ -218,8 +199,7 @@ export class GameController
|
||||
|
||||
this.profileFixerService.addMissingSptVersionTagToProfile(fullProfile);
|
||||
|
||||
if (pmcProfile.Hideout)
|
||||
{
|
||||
if (pmcProfile.Hideout) {
|
||||
this.profileFixerService.addMissingHideoutBonusesToProfile(pmcProfile);
|
||||
this.profileFixerService.addMissingUpgradesPropertyToHideout(pmcProfile);
|
||||
this.hideoutHelper.setHideoutImprovementsToCompleted(pmcProfile);
|
||||
@ -241,40 +221,33 @@ export class GameController
|
||||
|
||||
this.validateQuestAssortUnlocksExist();
|
||||
|
||||
if (pmcProfile.Info)
|
||||
{
|
||||
if (pmcProfile.Info) {
|
||||
this.addPlayerToPMCNames(pmcProfile);
|
||||
|
||||
this.checkForAndRemoveUndefinedDialogs(fullProfile);
|
||||
}
|
||||
|
||||
if (this.seasonalEventService.isAutomaticEventDetectionEnabled())
|
||||
{
|
||||
if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) {
|
||||
this.seasonalEventService.enableSeasonalEvents(sessionID);
|
||||
}
|
||||
|
||||
if (pmcProfile?.Skills?.Common)
|
||||
{
|
||||
if (pmcProfile?.Skills?.Common) {
|
||||
this.warnOnActiveBotReloadSkill(pmcProfile);
|
||||
}
|
||||
|
||||
// Flea bsg blacklist is off
|
||||
if (!this.ragfairConfig.dynamic.blacklist.enableBsgList)
|
||||
{
|
||||
if (!this.ragfairConfig.dynamic.blacklist.enableBsgList) {
|
||||
this.setAllDbItemsAsSellableOnFlea();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected adjustHideoutCraftTimes(overrideSeconds: number): void
|
||||
{
|
||||
if (overrideSeconds === -1)
|
||||
{
|
||||
protected adjustHideoutCraftTimes(overrideSeconds: number): void {
|
||||
if (overrideSeconds === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const craft of this.databaseService.getHideout().production)
|
||||
{
|
||||
for (const craft of this.databaseService.getHideout().production) {
|
||||
// Only adjust crafts ABOVE the override
|
||||
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
|
||||
*/
|
||||
protected adjustHideoutBuildTimes(overrideSeconds: number): void
|
||||
{
|
||||
if (overrideSeconds === -1)
|
||||
{
|
||||
protected adjustHideoutBuildTimes(overrideSeconds: number): void {
|
||||
if (overrideSeconds === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const area of this.databaseService.getHideout().areas)
|
||||
{
|
||||
for (const stage of Object.values(area.stages))
|
||||
{
|
||||
for (const area of this.databaseService.getHideout().areas) {
|
||||
for (const stage of Object.values(area.stages)) {
|
||||
// Only adjust crafts ABOVE the override
|
||||
stage.constructionTime = Math.min(stage.constructionTime, overrideSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected adjustLocationBotValues(): void
|
||||
{
|
||||
protected adjustLocationBotValues(): void {
|
||||
const mapsDb = this.databaseService.getLocations();
|
||||
|
||||
for (const locationKey in this.botConfig.maxBotCap)
|
||||
{
|
||||
for (const locationKey in this.botConfig.maxBotCap) {
|
||||
const map: ILocation = mapsDb[locationKey];
|
||||
if (!map)
|
||||
{
|
||||
if (!map) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -322,15 +288,16 @@ export class GameController
|
||||
/**
|
||||
* Out of date/incorrectly made trader mods forget this data
|
||||
*/
|
||||
protected checkTraderRepairValuesExist(): void
|
||||
{
|
||||
protected checkTraderRepairValuesExist(): void {
|
||||
const traders = this.databaseService.getTraders();
|
||||
for (const trader of Object.values(traders))
|
||||
{
|
||||
if (!trader?.base?.repair)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("trader-missing_repair_property_using_default",
|
||||
{ traderId: trader.base._id, nickname: trader.base.nickname }));
|
||||
for (const trader of Object.values(traders)) {
|
||||
if (!trader?.base?.repair) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("trader-missing_repair_property_using_default", {
|
||||
traderId: trader.base._id,
|
||||
nickname: trader.base.nickname,
|
||||
}),
|
||||
);
|
||||
|
||||
// use ragfair trader as a default
|
||||
trader.base.repair = this.cloner.clone(traders.ragfair.base.repair);
|
||||
@ -338,49 +305,46 @@ export class GameController
|
||||
return;
|
||||
}
|
||||
|
||||
if (trader.base.repair?.quality === undefined)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("trader-missing_repair_quality_property_using_default",
|
||||
{ traderId: trader.base._id, nickname: trader.base.nickname }));
|
||||
if (trader.base.repair?.quality === undefined) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("trader-missing_repair_quality_property_using_default", {
|
||||
traderId: trader.base._id,
|
||||
nickname: trader.base.nickname,
|
||||
}),
|
||||
);
|
||||
|
||||
// use ragfair trader as a default
|
||||
trader.base.repair.quality = this.cloner.clone(
|
||||
traders.ragfair.base.repair.quality,
|
||||
);
|
||||
trader.base.repair.quality = this.cloner.clone(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;
|
||||
for (const [mapId, positionsToAdd] of Object.entries(looseLootPositionsToAdd))
|
||||
{
|
||||
if (!mapId)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("location-unable_to_add_custom_loot_position", mapId));
|
||||
for (const [mapId, positionsToAdd] of Object.entries(looseLootPositionsToAdd)) {
|
||||
if (!mapId) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("location-unable_to_add_custom_loot_position", mapId),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const positionToAdd of positionsToAdd)
|
||||
{
|
||||
for (const positionToAdd of positionsToAdd) {
|
||||
// Exists already, add new items to existing positions pool
|
||||
const existingLootPosition = mapLooseLoot.spawnpoints.find(
|
||||
(x) => x.template.Id === positionToAdd.template.Id,
|
||||
);
|
||||
|
||||
if (existingLootPosition)
|
||||
{
|
||||
if (existingLootPosition) {
|
||||
existingLootPosition.template.Items.push(...positionToAdd.template.Items);
|
||||
existingLootPosition.itemDistribution.push(...positionToAdd.itemDistribution);
|
||||
|
||||
@ -393,27 +357,27 @@ export class GameController
|
||||
}
|
||||
}
|
||||
|
||||
protected adjustLooseLootSpawnProbabilities(): void
|
||||
{
|
||||
protected adjustLooseLootSpawnProbabilities(): void {
|
||||
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;
|
||||
if (!mapLooseLootData)
|
||||
{
|
||||
if (!mapLooseLootData) {
|
||||
this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [lootKey, newChanceValue] of Object.entries(mapAdjustments))
|
||||
{
|
||||
const lootPostionToAdjust = mapLooseLootData.spawnpoints
|
||||
.find((spawnPoint) => spawnPoint.template.Id === lootKey);
|
||||
if (!lootPostionToAdjust)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("location-unable_to_adjust_loot_position_on_map",
|
||||
{ lootKey: lootKey, mapId: mapId }));
|
||||
for (const [lootKey, newChanceValue] of Object.entries(mapAdjustments)) {
|
||||
const lootPostionToAdjust = mapLooseLootData.spawnpoints.find(
|
||||
(spawnPoint) => spawnPoint.template.Id === lootKey,
|
||||
);
|
||||
if (!lootPostionToAdjust) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("location-unable_to_adjust_loot_position_on_map", {
|
||||
lootKey: lootKey,
|
||||
mapId: mapId,
|
||||
}),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -424,36 +388,28 @@ export class GameController
|
||||
}
|
||||
|
||||
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
|
||||
protected adjustMapBotLimits(): void
|
||||
{
|
||||
protected adjustMapBotLimits(): void {
|
||||
const mapsDb = this.databaseService.getLocations();
|
||||
if (!this.locationConfig.botTypeLimits)
|
||||
{
|
||||
if (!this.locationConfig.botTypeLimits) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const mapId in this.locationConfig.botTypeLimits)
|
||||
{
|
||||
for (const mapId in this.locationConfig.botTypeLimits) {
|
||||
const map: ILocation = mapsDb[mapId];
|
||||
if (!map)
|
||||
{
|
||||
if (!map) {
|
||||
this.logger.warning(
|
||||
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);
|
||||
if (index !== -1)
|
||||
{
|
||||
if (index !== -1) {
|
||||
// Existing bot type found in MinMaxBots array, edit
|
||||
const limitObjectToUpdate = map.base.MinMaxBots[index];
|
||||
limitObjectToUpdate.min = botToLimit.min;
|
||||
limitObjectToUpdate.max = botToLimit.max;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Bot type not found, add new object
|
||||
map.base.MinMaxBots.push({
|
||||
// Bot type not found, add new object
|
||||
@ -469,11 +425,10 @@ export class GameController
|
||||
/**
|
||||
* Handle client/game/config
|
||||
*/
|
||||
public getGameConfig(sessionID: string): IGameConfigResponse
|
||||
{
|
||||
public getGameConfig(sessionID: string): IGameConfigResponse {
|
||||
const profile = this.profileHelper.getPmcProfile(sessionID);
|
||||
const gameTime
|
||||
= profile.Stats?.Eft.OverallCounters.Items?.find(
|
||||
const gameTime =
|
||||
profile.Stats?.Eft.OverallCounters.Items?.find(
|
||||
(counter) => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"),
|
||||
)?.Value ?? 0;
|
||||
|
||||
@ -504,40 +459,35 @@ export class GameController
|
||||
/**
|
||||
* 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() };
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/match/group/current
|
||||
*/
|
||||
public getCurrentGroup(sessionId: string): ICurrentGroupResponse
|
||||
{
|
||||
public getCurrentGroup(sessionId: string): ICurrentGroupResponse {
|
||||
return { squad: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/checkVersion
|
||||
*/
|
||||
public getValidGameVersion(sessionId: string): ICheckVersionResponse
|
||||
{
|
||||
public getValidGameVersion(sessionId: string): ICheckVersionResponse {
|
||||
return { isvalid: true, latestVersion: this.coreConfig.compatibleTarkovVersion };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/game/keepalive
|
||||
*/
|
||||
public getKeepAlive(sessionId: string): IGameKeepAliveResponse
|
||||
{
|
||||
public getKeepAlive(sessionId: string): IGameKeepAliveResponse {
|
||||
this.profileActivityService.setActivityTimestamp(sessionId);
|
||||
return { msg: "OK", utc_time: new Date().getTime() / 1000 };
|
||||
}
|
||||
@ -545,8 +495,7 @@ export class GameController
|
||||
/**
|
||||
* 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
|
||||
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
|
||||
*/
|
||||
protected fixShotgunDispersions(): void
|
||||
{
|
||||
protected fixShotgunDispersions(): void {
|
||||
const itemDb = this.databaseService.getItems();
|
||||
|
||||
const shotguns = [
|
||||
Weapons.SHOTGUN_12G_SAIGA_12K,
|
||||
Weapons.SHOTGUN_20G_TOZ_106,
|
||||
Weapons.SHOTGUN_12G_M870,
|
||||
];
|
||||
for (const shotgunId of shotguns)
|
||||
{
|
||||
if (itemDb[shotgunId]._props.ShotgunDispersion)
|
||||
{
|
||||
const shotguns = [Weapons.SHOTGUN_12G_SAIGA_12K, Weapons.SHOTGUN_20G_TOZ_106, Weapons.SHOTGUN_12G_M870];
|
||||
for (const shotgunId of shotguns) {
|
||||
if (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
|
||||
* @param pmcProfile Player profile
|
||||
*/
|
||||
protected warnOnActiveBotReloadSkill(pmcProfile: IPmcData): void
|
||||
{
|
||||
protected warnOnActiveBotReloadSkill(pmcProfile: IPmcData): void {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
protected setAllDbItemsAsSellableOnFlea(): void
|
||||
{
|
||||
protected setAllDbItemsAsSellableOnFlea(): void {
|
||||
const dbItems = Object.values(this.databaseService.getItems());
|
||||
for (const item of dbItems)
|
||||
{
|
||||
if (item._type === "Item" && !item._props?.CanSellOnRagfair)
|
||||
{
|
||||
for (const item of dbItems) {
|
||||
if (item._type === "Item" && !item._props?.CanSellOnRagfair) {
|
||||
item._props.CanSellOnRagfair = true;
|
||||
}
|
||||
}
|
||||
@ -605,15 +542,13 @@ export class GameController
|
||||
* When player logs in, iterate over all active effects and reduce timer
|
||||
* @param pmcProfile Profile to adjust values for
|
||||
*/
|
||||
protected updateProfileHealthValues(pmcProfile: IPmcData): void
|
||||
{
|
||||
protected updateProfileHealthValues(pmcProfile: IPmcData): void {
|
||||
const healthLastUpdated = pmcProfile.Health.UpdateTime;
|
||||
const currentTimeStamp = this.timeUtil.getTimestamp();
|
||||
const diffSeconds = currentTimeStamp - healthLastUpdated;
|
||||
|
||||
// Last update is in past
|
||||
if (healthLastUpdated < currentTimeStamp)
|
||||
{
|
||||
if (healthLastUpdated < currentTimeStamp) {
|
||||
// Base values
|
||||
let energyRegenPerHour = 60;
|
||||
let hydrationRegenPerHour = 60;
|
||||
@ -632,53 +567,42 @@ export class GameController
|
||||
);
|
||||
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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));
|
||||
}
|
||||
if (bodyPart.Health.Current > bodyPart.Health.Maximum)
|
||||
{
|
||||
if (bodyPart.Health.Current > bodyPart.Health.Maximum) {
|
||||
bodyPart.Health.Current = bodyPart.Health.Maximum;
|
||||
}
|
||||
|
||||
// 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
|
||||
for (const effectKey in bodyPart.Effects)
|
||||
{
|
||||
for (const effectKey in bodyPart.Effects) {
|
||||
// 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
|
||||
if (diffSeconds > 1800)
|
||||
{
|
||||
if (diffSeconds > 1800) {
|
||||
delete bodyPart.Effects[effectKey];
|
||||
}
|
||||
|
||||
@ -686,8 +610,7 @@ export class GameController
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
*/
|
||||
protected fixBrokenOfflineMapWaves(): void
|
||||
{
|
||||
protected fixBrokenOfflineMapWaves(): void {
|
||||
const locations = this.databaseService.getLocations();
|
||||
for (const locationKey in locations)
|
||||
{
|
||||
for (const locationKey in locations) {
|
||||
// Skip ignored maps
|
||||
if (this.locationConfig.fixEmptyBotWavesSettings.ignoreMaps.includes(locationKey))
|
||||
{
|
||||
if (this.locationConfig.fixEmptyBotWavesSettings.ignoreMaps.includes(locationKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop over all of the locations waves and look for waves with identical min and max slots
|
||||
const location: ILocation = locations[locationKey];
|
||||
if (!location.base)
|
||||
{
|
||||
if (!location.base) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("location-unable_to_fix_broken_waves_missing_base", locationKey),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const wave of location.base.waves ?? [])
|
||||
{
|
||||
if (wave.slots_max - wave.slots_min === 0)
|
||||
{
|
||||
for (const wave of location.base.waves ?? []) {
|
||||
if (wave.slots_max - wave.slots_min === 0) {
|
||||
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}`,
|
||||
);
|
||||
@ -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
|
||||
*/
|
||||
protected fixRoguesSpawningInstantlyOnLighthouse(): void
|
||||
{
|
||||
protected fixRoguesSpawningInstantlyOnLighthouse(): void {
|
||||
const rogueSpawnDelaySeconds = this.locationConfig.rogueLighthouseSpawnTimeSettings.waitTimeSeconds;
|
||||
const lighthouse = this.databaseService.getLocations().lighthouse?.base;
|
||||
if (!lighthouse)
|
||||
{
|
||||
if (!lighthouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find Rogues that spawn instantly
|
||||
const instantRogueBossSpawns = lighthouse.BossLocationSpawn
|
||||
.filter((spawn) => spawn.BossName === "exUsec"
|
||||
&& spawn.Time === -1);
|
||||
for (const wave of instantRogueBossSpawns)
|
||||
{
|
||||
const instantRogueBossSpawns = lighthouse.BossLocationSpawn.filter(
|
||||
(spawn) => spawn.BossName === "exUsec" && spawn.Time === -1,
|
||||
);
|
||||
for (const wave of instantRogueBossSpawns) {
|
||||
wave.Time = rogueSpawnDelaySeconds;
|
||||
}
|
||||
}
|
||||
@ -761,21 +675,18 @@ export class GameController
|
||||
* Send starting gifts to profile after x days
|
||||
* @param pmcProfile Profile to add gifts to
|
||||
*/
|
||||
protected sendPraporGiftsToNewProfiles(pmcProfile: IPmcData): void
|
||||
{
|
||||
protected sendPraporGiftsToNewProfiles(pmcProfile: IPmcData): void {
|
||||
const timeStampProfileCreated = pmcProfile.Info.RegistrationDate;
|
||||
const oneDaySeconds = this.timeUtil.getHoursAsSeconds(24);
|
||||
const currentTimeStamp = this.timeUtil.getTimestamp();
|
||||
|
||||
// One day post-profile creation
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds)
|
||||
{
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds) {
|
||||
this.giftService.sendPraporStartingGift(pmcProfile.sessionId, 1);
|
||||
}
|
||||
|
||||
// Two day post-profile creation
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 2)
|
||||
{
|
||||
if (currentTimeStamp > timeStampProfileCreated + oneDaySeconds * 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
|
||||
* waves to one bot when they're waiting to spawn for too long
|
||||
*/
|
||||
protected splitBotWavesIntoSingleWaves(): void
|
||||
{
|
||||
protected splitBotWavesIntoSingleWaves(): void {
|
||||
const locations = this.databaseService.getLocations();
|
||||
for (const locationKey in locations)
|
||||
{
|
||||
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.ignoreMaps.includes(locationKey))
|
||||
{
|
||||
for (const locationKey in locations) {
|
||||
if (this.locationConfig.splitWaveIntoSingleSpawnsSettings.ignoreMaps.includes(locationKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over all maps
|
||||
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
|
||||
if (
|
||||
wave.slots_max - wave.slots_min
|
||||
>= this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold
|
||||
)
|
||||
{
|
||||
wave.slots_max - wave.slots_min >=
|
||||
this.locationConfig.splitWaveIntoSingleSpawnsSettings.waveSizeThreshold
|
||||
) {
|
||||
// Get count of bots to be spawned in wave
|
||||
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
|
||||
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
|
||||
const waveToAddClone = this.cloner.clone(wave);
|
||||
|
||||
// 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
|
||||
waveToAddClone.number = index;
|
||||
}
|
||||
@ -841,11 +745,9 @@ export class GameController
|
||||
let index = indexOfWaveToSplit + wavesAddedCount + 1;
|
||||
index < location.base.waves.length;
|
||||
index++
|
||||
)
|
||||
{
|
||||
) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -858,28 +760,24 @@ export class GameController
|
||||
* Get a list of installed mods and save their details to the profile being used
|
||||
* @param fullProfile Profile to add mod details to
|
||||
*/
|
||||
protected saveActiveModsToProfile(fullProfile: ISptProfile): void
|
||||
{
|
||||
protected saveActiveModsToProfile(fullProfile: ISptProfile): void {
|
||||
// Add empty mod array if undefined
|
||||
if (!fullProfile.spt.mods)
|
||||
{
|
||||
if (!fullProfile.spt.mods) {
|
||||
fullProfile.spt.mods = [];
|
||||
}
|
||||
|
||||
// Get active mods
|
||||
const activeMods = this.preSptModLoader.getImportedModDetails();
|
||||
for (const modKey in activeMods)
|
||||
{
|
||||
for (const modKey in activeMods) {
|
||||
const modDetails = activeMods[modKey];
|
||||
if (
|
||||
fullProfile.spt.mods.some(
|
||||
(mod) =>
|
||||
mod.author === modDetails.author
|
||||
&& mod.name === modDetails.name
|
||||
&& mod.version === modDetails.version,
|
||||
mod.author === modDetails.author &&
|
||||
mod.name === modDetails.name &&
|
||||
mod.version === modDetails.version,
|
||||
)
|
||||
)
|
||||
{
|
||||
) {
|
||||
// Exists already, skip
|
||||
continue;
|
||||
}
|
||||
@ -897,17 +795,14 @@ export class GameController
|
||||
/**
|
||||
* 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 traders = db.traders!;
|
||||
const quests = db.templates!.quests;
|
||||
for (const traderId of Object.values(Traders))
|
||||
{
|
||||
for (const traderId of Object.values(Traders)) {
|
||||
const traderData = traders[traderId];
|
||||
const traderAssorts = traderData?.assort;
|
||||
if (!traderAssorts)
|
||||
{
|
||||
if (!traderAssorts) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -919,11 +814,9 @@ export class GameController
|
||||
};
|
||||
|
||||
// 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
|
||||
if (!traderAssorts.loyal_level_items[assortKey])
|
||||
{
|
||||
if (!traderAssorts.loyal_level_items[assortKey]) {
|
||||
// Reverse lookup of enum key by value
|
||||
const messageValues = {
|
||||
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
|
||||
* @param pmcProfile Profile of player to get name from
|
||||
*/
|
||||
protected addPlayerToPMCNames(pmcProfile: IPmcData): void
|
||||
{
|
||||
protected addPlayerToPMCNames(pmcProfile: IPmcData): void {
|
||||
const playerName = pmcProfile.Info.Nickname;
|
||||
if (playerName)
|
||||
{
|
||||
if (playerName) {
|
||||
const bots = this.databaseService.getBots().types;
|
||||
|
||||
if (bots.bear)
|
||||
{
|
||||
if (bots.bear) {
|
||||
bots.bear.firstName.push(playerName);
|
||||
}
|
||||
|
||||
if (bots.usec)
|
||||
{
|
||||
if (bots.usec) {
|
||||
bots.usec.firstName.push(playerName);
|
||||
}
|
||||
}
|
||||
@ -964,11 +853,9 @@ export class GameController
|
||||
* Check for a dialog with the key 'undefined', and remove it
|
||||
* @param fullProfile Profile to check for dialog in
|
||||
*/
|
||||
protected checkForAndRemoveUndefinedDialogs(fullProfile: ISptProfile): void
|
||||
{
|
||||
protected checkForAndRemoveUndefinedDialogs(fullProfile: ISptProfile): void {
|
||||
const undefinedDialog = fullProfile.dialogues.undefined;
|
||||
if (undefinedDialog)
|
||||
{
|
||||
if (undefinedDialog) {
|
||||
delete fullProfile.dialogues.undefined;
|
||||
}
|
||||
}
|
||||
@ -976,12 +863,10 @@ export class GameController
|
||||
/**
|
||||
* Blank out the "test" mail message from prapor
|
||||
*/
|
||||
protected removePraporTestMessage(): void
|
||||
{
|
||||
protected removePraporTestMessage(): void {
|
||||
// Iterate over all languages (e.g. "en", "fr")
|
||||
const locales = this.databaseService.getLocales();
|
||||
for (const localeKey in locales.global)
|
||||
{
|
||||
for (const localeKey in locales.global) {
|
||||
locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
|
||||
}
|
||||
}
|
||||
@ -989,24 +874,21 @@ export class GameController
|
||||
/**
|
||||
* Make non-trigger-spawned raiders spawn earlier + always
|
||||
*/
|
||||
protected adjustLabsRaiderSpawnRate(): void
|
||||
{
|
||||
protected adjustLabsRaiderSpawnRate(): void {
|
||||
const labsBase = this.databaseService.getLocations().laboratory!.base;
|
||||
|
||||
// Find spawns with empty string for triggerId/TriggerName
|
||||
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn
|
||||
.filter((bossSpawn) => !bossSpawn.TriggerId
|
||||
&& !bossSpawn.TriggerName);
|
||||
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(
|
||||
(bossSpawn) => !bossSpawn.TriggerId && !bossSpawn.TriggerName,
|
||||
);
|
||||
|
||||
for (const boss of nonTriggerLabsBossSpawns)
|
||||
{
|
||||
for (const boss of nonTriggerLabsBossSpawns) {
|
||||
boss.BossChance = 100;
|
||||
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(
|
||||
`Server version: ${globalThis.G_SPTVERSION || this.coreConfig.sptVersion} ${globalThis.G_COMMIT}`,
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HandbookHelper } from "@spt/helpers/HandbookHelper";
|
||||
import { DatabaseServer } from "@spt/servers/DatabaseServer";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HandbookController
|
||||
{
|
||||
export class HandbookController {
|
||||
constructor(
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public load(): void
|
||||
{
|
||||
public load(): void {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HealthHelper } from "@spt/helpers/HealthHelper";
|
||||
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
|
||||
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 { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { PaymentService } from "@spt/services/PaymentService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HealthController
|
||||
{
|
||||
export class HealthController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||
@ -31,8 +30,7 @@ export class HealthController
|
||||
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||
@inject("HealthHelper") protected healthHelper: HealthHelper,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* stores in-raid player health
|
||||
@ -48,8 +46,7 @@ export class HealthController
|
||||
sessionID: string,
|
||||
addEffects = true,
|
||||
deleteExistingEffects = true,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
|
||||
}
|
||||
|
||||
@ -64,14 +61,12 @@ export class HealthController
|
||||
pmcData: IPmcData,
|
||||
request: IOffraidHealRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// Update medkit used (hpresource)
|
||||
const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item);
|
||||
if (!healingItemToUse)
|
||||
{
|
||||
if (!healingItemToUse) {
|
||||
const errorMessage = this.localisationService.getText(
|
||||
"health-healing_item_not_found",
|
||||
healingItemToUse._id,
|
||||
@ -84,20 +79,16 @@ export class HealthController
|
||||
// Ensure item has a upd object
|
||||
this.itemHelper.addUpdObjectToItem(healingItemToUse);
|
||||
|
||||
if (healingItemToUse.upd.MedKit)
|
||||
{
|
||||
if (healingItemToUse.upd.MedKit) {
|
||||
healingItemToUse.upd.MedKit.HpResource -= request.count;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Get max healing from db
|
||||
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
|
||||
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -112,14 +103,12 @@ export class HealthController
|
||||
* @param sessionID Session id
|
||||
* @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);
|
||||
let resourceLeft = 0;
|
||||
|
||||
const itemToConsume = pmcData.Inventory.items.find((item) => item._id === request.item);
|
||||
if (!itemToConsume)
|
||||
{
|
||||
if (!itemToConsume) {
|
||||
// Item not found, very bad
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
@ -128,17 +117,13 @@ export class HealthController
|
||||
}
|
||||
|
||||
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
|
||||
if (consumedItemMaxResource > 1)
|
||||
{
|
||||
if (consumedItemMaxResource > 1) {
|
||||
// Ensure item has a upd object
|
||||
this.itemHelper.addUpdObjectToItem(itemToConsume);
|
||||
|
||||
if (itemToConsume.upd.FoodDrink === undefined)
|
||||
{
|
||||
if (itemToConsume.upd.FoodDrink === undefined) {
|
||||
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count };
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
itemToConsume.upd.FoodDrink.HpPercent -= request.count;
|
||||
}
|
||||
|
||||
@ -146,8 +131,7 @@ export class HealthController
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -166,8 +150,7 @@ export class HealthController
|
||||
pmcData: IPmcData,
|
||||
healthTreatmentRequest: IHealthTreatmentRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
const payMoneyRequest: IProcessBuyTradeRequestData = {
|
||||
Action: healthTreatmentRequest.Action,
|
||||
@ -180,36 +163,30 @@ export class HealthController
|
||||
};
|
||||
|
||||
this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return output;
|
||||
}
|
||||
|
||||
for (const bodyPartKey in healthTreatmentRequest.difference.BodyParts)
|
||||
{
|
||||
for (const bodyPartKey in healthTreatmentRequest.difference.BodyParts) {
|
||||
// Get body part from request + from pmc profile
|
||||
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
|
||||
const profilePart = pmcData.Health.BodyParts[bodyPartKey];
|
||||
|
||||
// Bodypart healing is chosen when part request hp is above 0
|
||||
if (partRequest.Health > 0)
|
||||
{
|
||||
if (partRequest.Health > 0) {
|
||||
// Heal bodypart
|
||||
profilePart.Health.Current = profilePart.Health.Maximum;
|
||||
}
|
||||
|
||||
// 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
|
||||
for (const effect of partRequest.Effects)
|
||||
{
|
||||
for (const effect of partRequest.Effects) {
|
||||
delete pmcData.Health.BodyParts[bodyPartKey].Effects[effect];
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -227,8 +204,7 @@ export class HealthController
|
||||
* @param info Request data
|
||||
* @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
|
||||
// TODO:
|
||||
// Health effects (fractures etc) are handled in /player/health/sync.
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator";
|
||||
import { HideoutHelper } from "@spt/helpers/HideoutHelper";
|
||||
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
|
||||
@ -7,12 +6,7 @@ import { PaymentHelper } from "@spt/helpers/PaymentHelper";
|
||||
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import {
|
||||
HideoutArea,
|
||||
ITaskConditionCounter,
|
||||
Product,
|
||||
ScavCase,
|
||||
} from "@spt/models/eft/common/tables/IBotBase";
|
||||
import { HideoutArea, ITaskConditionCounter, Product, ScavCase } from "@spt/models/eft/common/tables/IBotBase";
|
||||
import { Item } from "@spt/models/eft/common/tables/IItem";
|
||||
import { HideoutUpgradeCompleteRequestData } from "@spt/models/eft/hideout/HideoutUpgradeCompleteRequestData";
|
||||
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 { PlayerService } from "@spt/services/PlayerService";
|
||||
import { ProfileActivityService } from "@spt/services/ProfileActivityService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class HideoutController
|
||||
{
|
||||
export class HideoutController {
|
||||
/** Key used in TaskConditionCounters array */
|
||||
protected static nameTaskConditionCountersCrafting = "CounterHoursCrafting";
|
||||
protected hideoutConfig: IHideoutConfig;
|
||||
@ -82,8 +76,7 @@ export class HideoutController
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("FenceService") protected fenceService: FenceService,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
||||
}
|
||||
|
||||
@ -100,19 +93,15 @@ export class HideoutController
|
||||
request: IHideoutUpgradeRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
const items = request.items.map((reqItem) =>
|
||||
{
|
||||
): void {
|
||||
const items = request.items.map((reqItem) => {
|
||||
const item = pmcData.Inventory.items.find((invItem) => invItem._id === reqItem.id);
|
||||
return { inventoryItem: item, requestedItem: reqItem };
|
||||
});
|
||||
|
||||
// If it's not money, its construction / barter items
|
||||
for (const item of items)
|
||||
{
|
||||
if (!item.inventoryItem)
|
||||
{
|
||||
for (const item of items) {
|
||||
if (!item.inventoryItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
||||
);
|
||||
@ -122,24 +111,20 @@ export class HideoutController
|
||||
}
|
||||
|
||||
if (
|
||||
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
||||
&& item.inventoryItem.upd
|
||||
&& item.inventoryItem.upd.StackObjectsCount
|
||||
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
||||
)
|
||||
{
|
||||
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
|
||||
item.inventoryItem.upd &&
|
||||
item.inventoryItem.upd.StackObjectsCount &&
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Construction time management
|
||||
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.httpResponse.appendErrorToOutput(output);
|
||||
|
||||
@ -149,8 +134,7 @@ export class HideoutController
|
||||
const hideoutDataDb = this.databaseService
|
||||
.getTables()
|
||||
.hideout.areas.find((area) => area.type === request.areaType);
|
||||
if (!hideoutDataDb)
|
||||
{
|
||||
if (!hideoutDataDb) {
|
||||
this.logger.error(
|
||||
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;
|
||||
if (ctime > 0)
|
||||
{
|
||||
if (this.profileHelper.isDeveloperAccount(sessionID))
|
||||
{
|
||||
if (ctime > 0) {
|
||||
if (this.profileHelper.isDeveloperAccount(sessionID)) {
|
||||
ctime = 40;
|
||||
}
|
||||
const timestamp = this.timeUtil.getTimestamp();
|
||||
@ -186,14 +168,12 @@ export class HideoutController
|
||||
request: HideoutUpgradeCompleteRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const hideout = this.databaseService.getHideout();
|
||||
const globals = this.databaseService.getGlobals();
|
||||
|
||||
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.httpResponse.appendErrorToOutput(output);
|
||||
|
||||
@ -206,8 +186,7 @@ export class HideoutController
|
||||
profileHideoutArea.constructing = false;
|
||||
|
||||
const hideoutData = hideout.areas.find((area) => area.type === profileHideoutArea.type);
|
||||
if (!hideoutData)
|
||||
{
|
||||
if (!hideoutData) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("hideout-unable_to_find_area_in_database", request.areaType),
|
||||
);
|
||||
@ -219,17 +198,14 @@ export class HideoutController
|
||||
// Apply bonuses
|
||||
const hideoutStage = hideoutData.stages[profileHideoutArea.level];
|
||||
const bonuses = hideoutStage.bonuses;
|
||||
if (bonuses?.length > 0)
|
||||
{
|
||||
for (const bonus of bonuses)
|
||||
{
|
||||
if (bonuses?.length > 0) {
|
||||
for (const bonus of bonuses) {
|
||||
this.hideoutHelper.applyPlayerUpgradesBonuses(pmcData, bonus);
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade includes a container improvement/addition
|
||||
if (hideoutStage?.container)
|
||||
{
|
||||
if (hideoutStage?.container) {
|
||||
this.addContainerImprovementToProfile(
|
||||
output,
|
||||
sessionID,
|
||||
@ -242,10 +218,9 @@ export class HideoutController
|
||||
|
||||
// Upgrading water collector / med station
|
||||
if (
|
||||
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR
|
||||
|| profileHideoutArea.type === HideoutAreas.MEDSTATION
|
||||
)
|
||||
{
|
||||
profileHideoutArea.type === HideoutAreas.WATER_COLLECTOR ||
|
||||
profileHideoutArea.type === HideoutAreas.MEDSTATION
|
||||
) {
|
||||
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
|
||||
* @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 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);
|
||||
if (wall?.level === 0)
|
||||
{
|
||||
if (wall?.level === 0) {
|
||||
wall.level = 3;
|
||||
}
|
||||
}
|
||||
@ -290,11 +262,9 @@ export class HideoutController
|
||||
profileParentHideoutArea: HideoutArea,
|
||||
dbHideoutArea: IHideoutArea,
|
||||
hideoutStage: Stage,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -302,29 +272,24 @@ export class HideoutController
|
||||
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
|
||||
if (dbHideoutArea.type !== HideoutAreas.PLACE_OF_FAME)
|
||||
{
|
||||
if (dbHideoutArea.type !== HideoutAreas.PLACE_OF_FAME) {
|
||||
// Inform client of changes
|
||||
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
|
||||
const childDbArea = this.databaseService.getHideout().areas
|
||||
.find((area) => area.parentArea === dbHideoutArea._id);
|
||||
if (childDbArea)
|
||||
{
|
||||
const childDbArea = this.databaseService
|
||||
.getHideout()
|
||||
.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
|
||||
if (!pmcData.Inventory.hideoutAreaStashes[childDbArea.type])
|
||||
{
|
||||
if (!pmcData.Inventory.hideoutAreaStashes[childDbArea.type]) {
|
||||
pmcData.Inventory.hideoutAreaStashes[childDbArea.type] = childDbArea._id;
|
||||
}
|
||||
|
||||
// Set child area level to same as parent area
|
||||
pmcData.Hideout.Areas
|
||||
.find((hideoutArea) => hideoutArea.type === childDbArea.type).level
|
||||
= pmcData.Hideout.Areas
|
||||
.find((x) => x.type === profileParentHideoutArea.type,
|
||||
).level;
|
||||
pmcData.Hideout.Areas.find((hideoutArea) => hideoutArea.type === childDbArea.type).level =
|
||||
pmcData.Hideout.Areas.find((x) => x.type === profileParentHideoutArea.type).level;
|
||||
|
||||
// Add/upgrade stash item in player inventory
|
||||
const childDbAreaStage = childDbArea.stages[profileParentHideoutArea.level];
|
||||
@ -345,11 +310,9 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
dbHideoutData: IHideoutArea,
|
||||
hideoutStage: Stage,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
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)
|
||||
existingInventoryItem._tpl = hideoutStage.container;
|
||||
|
||||
@ -373,10 +336,8 @@ export class HideoutController
|
||||
areaType: HideoutAreas,
|
||||
hideoutDbData: IHideoutArea,
|
||||
hideoutStage: Stage,
|
||||
): void
|
||||
{
|
||||
if (!output.profileChanges[sessionID].changedHideoutStashes)
|
||||
{
|
||||
): void {
|
||||
if (!output.profileChanges[sessionID].changedHideoutStashes) {
|
||||
output.profileChanges[sessionID].changedHideoutStashes = {};
|
||||
}
|
||||
|
||||
@ -398,19 +359,16 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
addItemToHideoutRequest: IHideoutPutItemInRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
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);
|
||||
return { inventoryItem: item, requestedItem: kvp[1], slot: kvp[0] };
|
||||
});
|
||||
|
||||
const hideoutArea = pmcData.Hideout.Areas.find((area) => area.type === addItemToHideoutRequest.areaType);
|
||||
if (!hideoutArea)
|
||||
{
|
||||
if (!hideoutArea) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"hideout-unable_to_find_area_in_database",
|
||||
@ -420,10 +378,8 @@ export class HideoutController
|
||||
return this.httpResponse.appendErrorToOutput(output);
|
||||
}
|
||||
|
||||
for (const item of itemsToAdd)
|
||||
{
|
||||
if (!item.inventoryItem)
|
||||
{
|
||||
for (const item of itemsToAdd) {
|
||||
if (!item.inventoryItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", {
|
||||
itemId: item.requestedItem.id,
|
||||
@ -465,19 +421,16 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeItemOutRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
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));
|
||||
return this.httpResponse.appendErrorToOutput(output);
|
||||
}
|
||||
|
||||
if (!hideoutArea.slots || hideoutArea.slots.length === 0)
|
||||
{
|
||||
if (!hideoutArea.slots || hideoutArea.slots.length === 0) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("hideout-unable_to_find_item_to_remove_from_area", hideoutArea.type),
|
||||
);
|
||||
@ -492,8 +445,7 @@ export class HideoutController
|
||||
HideoutAreas.GENERATOR,
|
||||
HideoutAreas.BITCOIN_FARM,
|
||||
].includes(hideoutArea.type)
|
||||
)
|
||||
{
|
||||
) {
|
||||
const response = this.removeResourceFromArea(sessionID, pmcData, request, output, hideoutArea);
|
||||
|
||||
// Force a refresh of productions/hideout areas with resources
|
||||
@ -521,13 +473,11 @@ export class HideoutController
|
||||
removeResourceRequest: IHideoutTakeItemOutRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
hideoutArea: HideoutArea,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const slotIndexToRemove = removeResourceRequest.slots[0];
|
||||
|
||||
// Assume only one item in slot
|
||||
const itemToReturn = hideoutArea.slots
|
||||
.find((slot) => slot.locationIndex === slotIndexToRemove).item[0];
|
||||
const itemToReturn = hideoutArea.slots.find((slot) => slot.locationIndex === slotIndexToRemove).item[0];
|
||||
|
||||
const request: IAddItemDirectRequest = {
|
||||
itemWithModsToAdd: [itemToReturn],
|
||||
@ -537,8 +487,7 @@ export class HideoutController
|
||||
};
|
||||
|
||||
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
|
||||
return output;
|
||||
}
|
||||
@ -562,16 +511,14 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutToggleAreaRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
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)
|
||||
this.hideoutHelper.updatePlayerHideout(sessionID);
|
||||
|
||||
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));
|
||||
return this.httpResponse.appendErrorToOutput(output);
|
||||
}
|
||||
@ -593,14 +540,14 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutSingleProductionStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// Start production
|
||||
this.hideoutHelper.registerProduction(pmcData, body, sessionID);
|
||||
|
||||
// Find the recipe of the production
|
||||
const recipe = this.databaseService.getHideout().production
|
||||
.find((production) => production._id === body.recipeId);
|
||||
const recipe = this.databaseService
|
||||
.getHideout()
|
||||
.production.find((production) => production._id === body.recipeId);
|
||||
|
||||
// Find the actual amount of items we need to remove because body can send weird data
|
||||
const recipeRequirementsClone = this.cloner.clone(
|
||||
@ -609,8 +556,7 @@ export class HideoutController
|
||||
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
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 requirement = recipeRequirementsClone.find(
|
||||
(requirement) => requirement.templateId === itemToCheck._tpl,
|
||||
@ -618,16 +564,14 @@ export class HideoutController
|
||||
|
||||
// Handle tools not having a `count`, but always only requiring 1
|
||||
const requiredCount = requirement.count ?? 1;
|
||||
if (requiredCount <= 0)
|
||||
{
|
||||
if (requiredCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.inventoryHelper.removeItemByCount(pmcData, itemToDelete.id, requiredCount, sessionID, output);
|
||||
|
||||
// Tools don't have a count
|
||||
if (requirement.type !== "Tool")
|
||||
{
|
||||
if (requirement.type !== "Tool") {
|
||||
requirement.count -= itemToDelete.count;
|
||||
}
|
||||
}
|
||||
@ -647,15 +591,12 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
body: IHideoutScavCaseStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
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);
|
||||
if (!inventoryItem)
|
||||
{
|
||||
if (!inventoryItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"hideout-unable_to_find_scavcase_requested_item_in_profile_inventory",
|
||||
@ -665,20 +606,15 @@ export class HideoutController
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.inventoryHelper.removeItem(pmcData, requestedItem.id, sessionID, output);
|
||||
}
|
||||
}
|
||||
|
||||
const recipe = this.databaseService.getHideout().scavcase
|
||||
.find((r) => r._id === body.recipeId);
|
||||
if (!recipe)
|
||||
{
|
||||
const recipe = this.databaseService.getHideout().scavcase.find((r) => r._id === body.recipeId);
|
||||
if (!recipe) {
|
||||
this.logger.error(
|
||||
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:
|
||||
// - 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"
|
||||
const adjustedCraftTime
|
||||
= recipe.ProductionTime
|
||||
- this.hideoutHelper.getSkillProductionTimeReduction(
|
||||
const adjustedCraftTime =
|
||||
recipe.ProductionTime -
|
||||
this.hideoutHelper.getSkillProductionTimeReduction(
|
||||
pmcData,
|
||||
recipe.ProductionTime,
|
||||
SkillTypes.CRAFTING,
|
||||
@ -717,11 +653,9 @@ export class HideoutController
|
||||
* @param productionTime Time to complete scav case 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);
|
||||
if (!fenceLevel)
|
||||
{
|
||||
if (!fenceLevel) {
|
||||
return productionTime;
|
||||
}
|
||||
|
||||
@ -734,8 +668,7 @@ export class HideoutController
|
||||
* @param rewards reward items to add to profile
|
||||
* @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 };
|
||||
}
|
||||
|
||||
@ -750,8 +683,7 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutContinuousProductionStartRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.hideoutHelper.registerProduction(pmcData, request, sessionID);
|
||||
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
@ -769,13 +701,11 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeProductionRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
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
|
||||
this.hideoutHelper.updatePlayerHideout(sessionID);
|
||||
this.hideoutHelper.getBTC(pmcData, request, sessionID, output);
|
||||
@ -784,16 +714,14 @@ export class HideoutController
|
||||
}
|
||||
|
||||
const recipe = hideoutDb.production.find((r) => r._id === request.recipeId);
|
||||
if (recipe)
|
||||
{
|
||||
if (recipe) {
|
||||
this.handleRecipe(sessionID, recipe, pmcData, request, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const scavCase = hideoutDb.scavcase.find((r) => r._id === request.recipeId);
|
||||
if (scavCase)
|
||||
{
|
||||
if (scavCase) {
|
||||
this.handleScavCase(sessionID, pmcData, request, output);
|
||||
|
||||
return output;
|
||||
@ -823,24 +751,19 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeProductionRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// Validate that we have a matching production
|
||||
const productionDict = Object.entries(pmcData.Hideout.Production);
|
||||
let prodId: string;
|
||||
for (const [productionId, production] of productionDict)
|
||||
{
|
||||
for (const [productionId, production] of productionDict) {
|
||||
// Skip undefined production objects
|
||||
if (!production)
|
||||
{
|
||||
if (!production) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.hideoutHelper.isProductionType(production))
|
||||
{
|
||||
if (this.hideoutHelper.isProductionType(production)) {
|
||||
// Production or ScavCase
|
||||
if (production.RecipeId === request.recipeId)
|
||||
{
|
||||
if (production.RecipeId === request.recipeId) {
|
||||
prodId = productionId; // Set to objects key
|
||||
break;
|
||||
}
|
||||
@ -848,8 +771,7 @@ export class HideoutController
|
||||
}
|
||||
|
||||
// If we're unable to find the production, send an error to the client
|
||||
if (prodId === undefined)
|
||||
{
|
||||
if (prodId === undefined) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"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
|
||||
const rewardIsPreset = this.presetHelper.hasPreset(recipe.endProduct);
|
||||
if (rewardIsPreset)
|
||||
{
|
||||
if (rewardIsPreset) {
|
||||
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
|
||||
@ -893,8 +814,7 @@ export class HideoutController
|
||||
}
|
||||
|
||||
const rewardIsStackable = this.itemHelper.isItemTplStackable(recipe.endProduct);
|
||||
if (rewardIsStackable)
|
||||
{
|
||||
if (rewardIsStackable) {
|
||||
// Create root item
|
||||
const rewardToAdd: Item = {
|
||||
_id: this.hashUtil.generate(),
|
||||
@ -905,32 +825,26 @@ export class HideoutController
|
||||
// Split item into separate items with acceptable stack sizes
|
||||
const splitReward = this.itemHelper.splitStackIntoSeparateItems(rewardToAdd);
|
||||
itemAndChildrenToSendToPlayer.push(...splitReward);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// 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)
|
||||
if (!rewardIsPreset)
|
||||
{
|
||||
if (!rewardIsPreset) {
|
||||
itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]);
|
||||
}
|
||||
|
||||
// Add multiple of item if recipe requests it
|
||||
// Start index at one so we ignore first item in array
|
||||
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]);
|
||||
itemAndChildrenToSendToPlayer.push(...[itemAndMods]);
|
||||
}
|
||||
}
|
||||
|
||||
// Recipe has an `isEncoded` requirement for reward(s), Add `RecodableComponent` property
|
||||
if (recipe.isEncoded)
|
||||
{
|
||||
for (const reward of itemAndChildrenToSendToPlayer)
|
||||
{
|
||||
if (recipe.isEncoded) {
|
||||
for (const reward of itemAndChildrenToSendToPlayer) {
|
||||
this.itemHelper.addUpdObjectToItem(reward[0]);
|
||||
|
||||
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
|
||||
const toolsToSendToPlayer: Item[][] = [];
|
||||
const production = pmcData.Hideout.Production[prodId];
|
||||
if (production.sptRequiredTools?.length > 0)
|
||||
{
|
||||
for (const tool of production.sptRequiredTools)
|
||||
{
|
||||
if (production.sptRequiredTools?.length > 0) {
|
||||
for (const tool of production.sptRequiredTools) {
|
||||
toolsToSendToPlayer.push([tool]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
craftingExpAmount += this.hideoutConfig.expCraftAmount; // Default is 10
|
||||
}
|
||||
@ -959,8 +870,7 @@ export class HideoutController
|
||||
// Update variable with time spent crafting item(s)
|
||||
// 1 point per 8 hours of crafting
|
||||
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
|
||||
const multiplierCrafting = Math.floor(hoursCrafting / this.hideoutConfig.hoursForSkillCrafting);
|
||||
craftingExpAmount += 1 * multiplierCrafting;
|
||||
@ -969,8 +879,7 @@ export class HideoutController
|
||||
|
||||
// Make sure we can fit both the craft result and tools in the stash
|
||||
const totalResultItems = toolsToSendToPlayer.concat(itemAndChildrenToSendToPlayer);
|
||||
if (!this.inventoryHelper.canPlaceItemsInInventory(sessionID, totalResultItems))
|
||||
{
|
||||
if (!this.inventoryHelper.canPlaceItemsInInventory(sessionID, totalResultItems)) {
|
||||
this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
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
|
||||
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
|
||||
const addToolsRequest: IAddItemsDirectRequest = {
|
||||
itemsWithModsToAdd: [toolItem],
|
||||
@ -991,8 +899,7 @@ export class HideoutController
|
||||
};
|
||||
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1005,8 +912,7 @@ export class HideoutController
|
||||
callback: undefined,
|
||||
};
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1023,13 +929,11 @@ export class HideoutController
|
||||
);
|
||||
|
||||
// Add Crafting skill to player profile
|
||||
if (craftingExpAmount > 0)
|
||||
{
|
||||
if (craftingExpAmount > 0) {
|
||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.CRAFTING, craftingExpAmount);
|
||||
|
||||
const intellectAmountToGive = 0.5 * Math.round(craftingExpAmount / 15);
|
||||
if (intellectAmountToGive > 0)
|
||||
{
|
||||
if (intellectAmountToGive > 0) {
|
||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive);
|
||||
}
|
||||
}
|
||||
@ -1043,8 +947,7 @@ export class HideoutController
|
||||
pmcData.Hideout.Production[prodId].sptIsContinuous = recipe.continuous;
|
||||
|
||||
// Flag normal (non continious) crafts as complete
|
||||
if (!recipe.continuous)
|
||||
{
|
||||
if (!recipe.continuous) {
|
||||
pmcData.Hideout.Production[prodId].inProgress = false;
|
||||
}
|
||||
}
|
||||
@ -1058,11 +961,9 @@ export class HideoutController
|
||||
protected getHoursCraftingTaskConditionCounter(
|
||||
pmcData: IPmcData,
|
||||
recipe: IHideoutProduction,
|
||||
): ITaskConditionCounter
|
||||
{
|
||||
): ITaskConditionCounter {
|
||||
let counterHoursCrafting = pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting];
|
||||
if (!counterHoursCrafting)
|
||||
{
|
||||
if (!counterHoursCrafting) {
|
||||
// Doesn't exist, create
|
||||
pmcData.TaskConditionCounters[HideoutController.nameTaskConditionCountersCrafting] = {
|
||||
id: recipe._id,
|
||||
@ -1087,25 +988,20 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeProductionRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const ongoingProductions = Object.entries(pmcData.Hideout.Production);
|
||||
let prodId: string;
|
||||
for (const production of ongoingProductions)
|
||||
{
|
||||
if (this.hideoutHelper.isProductionType(production[1]))
|
||||
{
|
||||
for (const production of ongoingProductions) {
|
||||
if (this.hideoutHelper.isProductionType(production[1])) {
|
||||
// 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
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prodId === undefined)
|
||||
{
|
||||
if (prodId === undefined) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"hideout-unable_to_find_production_in_profile_by_recipie_id",
|
||||
@ -1129,8 +1025,7 @@ export class HideoutController
|
||||
};
|
||||
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1150,8 +1045,7 @@ export class HideoutController
|
||||
* @param sessionId Session id
|
||||
* @returns IQteData array
|
||||
*/
|
||||
public getQteList(sessionId: string): IQteData[]
|
||||
{
|
||||
public getQteList(sessionId: string): IQteData[] {
|
||||
return this.databaseService.getHideout().qte;
|
||||
}
|
||||
|
||||
@ -1167,8 +1061,7 @@ export class HideoutController
|
||||
pmcData: IPmcData,
|
||||
request: IHandleQTEEventRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// {
|
||||
// "Action": "HideoutQuickTimeEvent",
|
||||
// "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).
|
||||
|
||||
pmcData.Health.Energy.Current -= 50;
|
||||
if (pmcData.Health.Energy.Current < 1)
|
||||
{
|
||||
if (pmcData.Health.Energy.Current < 1) {
|
||||
pmcData.Health.Energy.Current = 1;
|
||||
}
|
||||
|
||||
pmcData.Health.Hydration.Current -= 50;
|
||||
if (pmcData.Health.Hydration.Current < 1)
|
||||
{
|
||||
if (pmcData.Health.Hydration.Current < 1) {
|
||||
pmcData.Health.Hydration.Current = 1;
|
||||
}
|
||||
}
|
||||
@ -1199,15 +1090,13 @@ export class HideoutController
|
||||
* @param request shooting range score request
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void
|
||||
{
|
||||
public recordShootingRangePoints(sessionId: string, pmcData: IPmcData, request: IRecordShootingRangePoints): void {
|
||||
const shootingRangeKey = "ShootingRangePoints";
|
||||
const overallCounterItems = pmcData.Stats.Eft.OverallCounters.Items;
|
||||
|
||||
// Find counter by key
|
||||
let shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey));
|
||||
if (!shootingRangeHighScore)
|
||||
{
|
||||
if (!shootingRangeHighScore) {
|
||||
// Counter not found, add blank one
|
||||
overallCounterItems.push({ Key: [shootingRangeKey], Value: 0 });
|
||||
shootingRangeHighScore = overallCounterItems.find((counter) => counter.Key.includes(shootingRangeKey));
|
||||
@ -1226,22 +1115,18 @@ export class HideoutController
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutImproveAreaRequestData,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
// 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);
|
||||
return { inventoryItem: item, requestedItem: reqItem };
|
||||
});
|
||||
|
||||
// If it's not money, its construction / barter items
|
||||
for (const item of items)
|
||||
{
|
||||
if (!item.inventoryItem)
|
||||
{
|
||||
for (const item of items) {
|
||||
if (!item.inventoryItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("hideout-unable_to_find_item_in_inventory", item.requestedItem.id),
|
||||
);
|
||||
@ -1249,31 +1134,25 @@ export class HideoutController
|
||||
}
|
||||
|
||||
if (
|
||||
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl)
|
||||
&& item.inventoryItem.upd
|
||||
&& item.inventoryItem.upd.StackObjectsCount
|
||||
&& item.inventoryItem.upd.StackObjectsCount > item.requestedItem.count
|
||||
)
|
||||
{
|
||||
this.paymentHelper.isMoneyTpl(item.inventoryItem._tpl) &&
|
||||
item.inventoryItem.upd &&
|
||||
item.inventoryItem.upd.StackObjectsCount &&
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return this.httpResponse.appendErrorToOutput(output);
|
||||
}
|
||||
|
||||
const hideoutDbData = this.databaseService.getHideout().areas
|
||||
.find((area) => area.type === request.areaType);
|
||||
if (!hideoutDbData)
|
||||
{
|
||||
const hideoutDbData = this.databaseService.getHideout().areas.find((area) => area.type === request.areaType);
|
||||
if (!hideoutDbData) {
|
||||
this.logger.error(
|
||||
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 timestamp = this.timeUtil.getTimestamp();
|
||||
|
||||
if (!output.profileChanges[sessionId].improvements)
|
||||
{
|
||||
if (!output.profileChanges[sessionId].improvements) {
|
||||
output.profileChanges[sessionId].improvements = {};
|
||||
}
|
||||
|
||||
for (const improvement of improvements)
|
||||
{
|
||||
for (const improvement of improvements) {
|
||||
const improvementDetails = {
|
||||
completed: false,
|
||||
improveCompleteTimestamp: timestamp + improvement.improvementTime,
|
||||
@ -1313,13 +1190,11 @@ export class HideoutController
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutCancelProductionRequestData,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
const craftToCancel = pmcData.Hideout.Production[request.recipeId];
|
||||
if (!craftToCancel)
|
||||
{
|
||||
if (!craftToCancel) {
|
||||
const errorMessage = `Unable to find craft ${request.recipeId} to cancel`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -1337,18 +1212,15 @@ export class HideoutController
|
||||
/**
|
||||
* Function called every x seconds as part of onUpdate event
|
||||
*/
|
||||
public update(): void
|
||||
{
|
||||
for (const sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
public update(): void {
|
||||
for (const sessionID in this.saveServer.getProfiles()) {
|
||||
if (
|
||||
"Hideout" in this.saveServer.getProfile(sessionID).characters.pmc
|
||||
&& this.profileActivityService.activeWithinLastMinutes(
|
||||
"Hideout" in this.saveServer.getProfile(sessionID).characters.pmc &&
|
||||
this.profileActivityService.activeWithinLastMinutes(
|
||||
sessionID,
|
||||
this.hideoutConfig.updateProfileHideoutWhenActiveWithinMinutes,
|
||||
)
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.hideoutHelper.updatePlayerHideout(sessionID);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
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 { SaveServer } from "@spt/servers/SaveServer";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Logic for handling In Raid callbacks
|
||||
*/
|
||||
@injectable()
|
||||
export class InraidController
|
||||
{
|
||||
export class InraidController {
|
||||
protected inRaidConfig: IInRaidConfig;
|
||||
protected botConfig: IBotConfig;
|
||||
|
||||
@ -28,8 +27,7 @@ export class InraidController
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.inRaidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID);
|
||||
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
|
||||
}
|
||||
@ -39,18 +37,15 @@ export class InraidController
|
||||
* @param sessionID Session id
|
||||
* @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);
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
if (!profile)
|
||||
{
|
||||
if (!profile) {
|
||||
this.logger.error(this.localisationService.getText("inraid-no_profile_found", sessionID));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!profile.inraid)
|
||||
{
|
||||
if (!profile.inraid) {
|
||||
profile.inraid = { character: sessionID, location: info.locationId };
|
||||
|
||||
return;
|
||||
@ -66,8 +61,7 @@ export class InraidController
|
||||
* @param offraidData post-raid request data
|
||||
* @param sessionID Session id
|
||||
*/
|
||||
public savePostRaidProfileForScav(offraidData: IScavSaveRequestData, sessionID: string): void
|
||||
{
|
||||
public savePostRaidProfileForScav(offraidData: IScavSaveRequestData, sessionID: string): void {
|
||||
const serverScavProfile = this.profileHelper.getScavProfile(sessionID);
|
||||
|
||||
serverScavProfile.Inventory.items = offraidData.profile.Inventory.items;
|
||||
@ -77,18 +71,15 @@ export class InraidController
|
||||
* Get the inraid config from configs/inraid.json
|
||||
* @returns InRaid Config
|
||||
*/
|
||||
public getInraidConfig(): IInRaidConfig
|
||||
{
|
||||
public getInraidConfig(): IInRaidConfig {
|
||||
return this.inRaidConfig;
|
||||
}
|
||||
|
||||
public getTraitorScavHostileChance(url: string, sessionID: string): number
|
||||
{
|
||||
public getTraitorScavHostileChance(url: string, sessionID: string): number {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { DialogueHelper } from "@spt/helpers/DialogueHelper";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
@ -26,15 +25,15 @@ import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { MailSendService } from "@spt/services/MailSendService";
|
||||
import { PaymentService } from "@spt/services/PaymentService";
|
||||
import { RagfairPriceService } from "@spt/services/RagfairPriceService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { MathUtil } from "@spt/utils/MathUtil";
|
||||
import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class InsuranceController
|
||||
{
|
||||
export class InsuranceController {
|
||||
protected insuranceConfig: IInsuranceConfig;
|
||||
|
||||
constructor(
|
||||
@ -58,8 +57,7 @@ export class InsuranceController
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
||||
}
|
||||
|
||||
@ -68,11 +66,9 @@ export class InsuranceController
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
public processReturn(): void
|
||||
{
|
||||
public processReturn(): void {
|
||||
// Process each installed profile.
|
||||
for (const sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
for (const sessionID in this.saveServer.getProfiles()) {
|
||||
this.processReturnByProfile(sessionID);
|
||||
}
|
||||
}
|
||||
@ -82,14 +78,12 @@ export class InsuranceController
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
public processReturnByProfile(sessionID: string): void
|
||||
{
|
||||
public processReturnByProfile(sessionID: string): void {
|
||||
// Filter out items that don't need to be processed yet.
|
||||
const insuranceDetails = this.filterInsuredItems(sessionID);
|
||||
|
||||
// Skip profile if no insured items to process
|
||||
if (insuranceDetails.length === 0)
|
||||
{
|
||||
if (insuranceDetails.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -103,14 +97,12 @@ export class InsuranceController
|
||||
* @param time The time to check ready status against. Current time by default.
|
||||
* @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.
|
||||
const insuranceTime = time || this.timeUtil.getTimestamp();
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
@ -124,8 +116,7 @@ export class InsuranceController
|
||||
* @param sessionID The session ID that should receive the processed items.
|
||||
* @returns void
|
||||
*/
|
||||
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void
|
||||
{
|
||||
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void {
|
||||
this.logger.debug(
|
||||
`Processing ${insuranceDetails.length} insurance packages, which includes a total of ${this.countAllInsuranceItems(
|
||||
insuranceDetails,
|
||||
@ -136,8 +127,7 @@ export class InsuranceController
|
||||
const rootItemParentID = this.insuranceService.getRootItemParentID(sessionID);
|
||||
|
||||
// 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.
|
||||
const itemsToDelete = this.findItemsToDelete(rootItemParentID, insured);
|
||||
|
||||
@ -160,8 +150,7 @@ export class InsuranceController
|
||||
* @param insurance
|
||||
* @returns
|
||||
*/
|
||||
protected countAllInsuranceItems(insurance: Insurance[]): number
|
||||
{
|
||||
protected countAllInsuranceItems(insurance: Insurance[]): number {
|
||||
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.
|
||||
* @returns void
|
||||
*/
|
||||
protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void
|
||||
{
|
||||
protected removeInsurancePackageFromProfile(sessionID: string, insPackage: Insurance): void {
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
profile.insurance = profile.insurance.filter(
|
||||
(insurance) =>
|
||||
insurance.traderId !== insPackage.traderId
|
||||
|| insurance.systemData.date !== insPackage.systemData.date
|
||||
|| insurance.systemData.time !== insPackage.systemData.time
|
||||
|| insurance.systemData.location !== insPackage.systemData.location,
|
||||
insurance.traderId !== insPackage.traderId ||
|
||||
insurance.systemData.date !== insPackage.systemData.date ||
|
||||
insurance.systemData.time !== insPackage.systemData.time ||
|
||||
insurance.systemData.location !== insPackage.systemData.location,
|
||||
);
|
||||
|
||||
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.
|
||||
* @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>();
|
||||
|
||||
// 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.
|
||||
if (hasRegularItems)
|
||||
{
|
||||
if (hasRegularItems) {
|
||||
this.processRegularItems(insured, toDelete, parentAttachmentsMap);
|
||||
}
|
||||
|
||||
// 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
|
||||
// process moddable attachments from here on out.
|
||||
parentAttachmentsMap = this.removeNonModdableAttachments(parentAttachmentsMap, itemsMap);
|
||||
@ -224,8 +209,7 @@ export class InsuranceController
|
||||
}
|
||||
|
||||
// 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.`);
|
||||
}
|
||||
|
||||
@ -246,65 +230,58 @@ export class InsuranceController
|
||||
rootItemParentID: string,
|
||||
insured: Insurance,
|
||||
itemsMap: Map<string, Item>,
|
||||
): Map<string, Item[]>
|
||||
{
|
||||
): 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.
|
||||
const parentItem = insured.items.find((item) => item._id === insuredItem.parentId);
|
||||
|
||||
// The parent (not the hideout) could not be found. Skip and warn.
|
||||
if (!parentItem && insuredItem.parentId !== rootItemParentID)
|
||||
{
|
||||
this.logger.warning(this.localisationService
|
||||
.getText("insurance-unable_to_find_parent_of_item",
|
||||
{
|
||||
insuredItemId: insuredItem._id,
|
||||
insuredItemTpl: insuredItem._tpl,
|
||||
parentId: insuredItem.parentId,
|
||||
}));
|
||||
if (!parentItem && insuredItem.parentId !== rootItemParentID) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("insurance-unable_to_find_parent_of_item", {
|
||||
insuredItemId: insuredItem._id,
|
||||
insuredItemTpl: insuredItem._tpl,
|
||||
parentId: insuredItem.parentId,
|
||||
}),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (!this.itemHelper.getItem(insuredItem._tpl)[0])
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("insurance-unable_to_find_attachment_in_db",
|
||||
{
|
||||
if (!this.itemHelper.getItem(insuredItem._tpl)[0]) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("insurance-unable_to_find_attachment_in_db", {
|
||||
insuredItemId: insuredItem._id,
|
||||
insuredItemTpl: insuredItem._tpl,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!mainParent)
|
||||
{
|
||||
if (!mainParent) {
|
||||
// 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,
|
||||
insuredItemTpl: insuredItem._tpl,
|
||||
parentId: insuredItem.parentId,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mainParentToAttachmentsMap.set(mainParent._id, [insuredItem]);
|
||||
}
|
||||
}
|
||||
@ -323,40 +300,33 @@ export class InsuranceController
|
||||
protected removeNonModdableAttachments(
|
||||
parentAttachmentsMap: Map<string, Item[]>,
|
||||
itemsMap: Map<string, Item>,
|
||||
): Map<string, Item[]>
|
||||
{
|
||||
): 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 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.
|
||||
let attachmentParentItem = parentItem;
|
||||
|
||||
// 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
|
||||
// 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);
|
||||
if (directParentItem)
|
||||
{
|
||||
if (directParentItem) {
|
||||
attachmentParentItem = directParentItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.itemHelper.isRaidModdable(attachment, attachmentParentItem))
|
||||
{
|
||||
if (this.itemHelper.isRaidModdable(attachment, attachmentParentItem)) {
|
||||
moddableAttachments.push(attachment);
|
||||
}
|
||||
}
|
||||
|
||||
// If any moddable attachments remain, add them to the updated map.
|
||||
if (moddableAttachments.length > 0)
|
||||
{
|
||||
if (moddableAttachments.length > 0) {
|
||||
updatedMap.set(parentId, moddableAttachments);
|
||||
}
|
||||
}
|
||||
@ -378,40 +348,32 @@ export class InsuranceController
|
||||
insured: Insurance,
|
||||
toDelete: Set<string>,
|
||||
parentAttachmentsMap: Map<string, Item[]>,
|
||||
): void
|
||||
{
|
||||
for (const insuredItem of insured.items)
|
||||
{
|
||||
): void {
|
||||
for (const insuredItem of insured.items) {
|
||||
// Skip if the item is an attachment. These are handled separately.
|
||||
if (this.itemHelper.isAttachmentAttached(insuredItem))
|
||||
{
|
||||
if (this.itemHelper.isAttachmentAttached(insuredItem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Roll for item deletion
|
||||
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
|
||||
// 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.
|
||||
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.
|
||||
const itemAndChildren = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
insured.items,
|
||||
insuredItem._id,
|
||||
);
|
||||
for (const item of itemAndChildren)
|
||||
{
|
||||
for (const item of itemAndChildren) {
|
||||
toDelete.add(item._id);
|
||||
}
|
||||
|
||||
// Remove the parent (and its children) from the parentAttachmentsMap.
|
||||
parentAttachmentsMap.delete(insuredItem._id);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// This item doesn't have any children. Simply mark it for deletion.
|
||||
toDelete.add(insuredItem._id);
|
||||
}
|
||||
@ -432,14 +394,11 @@ export class InsuranceController
|
||||
itemsMap: Map<string, Item>,
|
||||
traderId: string,
|
||||
toDelete: Set<string>,
|
||||
): void
|
||||
{
|
||||
for (const [parentId, attachmentItems] of mainParentToAttachmentsMap)
|
||||
{
|
||||
): void {
|
||||
for (const [parentId, attachmentItems] of mainParentToAttachmentsMap) {
|
||||
// Skip processing if parentId is already marked for deletion, as all attachments for that parent will
|
||||
// already be marked for deletion as well.
|
||||
if (toDelete.has(parentId))
|
||||
{
|
||||
if (toDelete.has(parentId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -464,8 +423,7 @@ export class InsuranceController
|
||||
* @param toDelete The array that accumulates the IDs of the items to be deleted.
|
||||
* @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)
|
||||
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
|
||||
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(
|
||||
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
|
||||
const attachmentIdsToRemove = attachmentsProbabilityArray.draw(countOfAttachmentsToRemove, false);
|
||||
for (const attachmentId of attachmentIdsToRemove)
|
||||
{
|
||||
for (const attachmentId of attachmentIdsToRemove) {
|
||||
toDelete.add(attachmentId);
|
||||
}
|
||||
|
||||
@ -497,11 +453,9 @@ export class InsuranceController
|
||||
attachmentIdsToRemove: string[],
|
||||
attachments: Item[],
|
||||
attachmentPrices: Record<string, number>,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
let index = 1;
|
||||
for (const attachmentId of attachmentIdsToRemove)
|
||||
{
|
||||
for (const attachmentId of attachmentIdsToRemove) {
|
||||
this.logger.debug(
|
||||
`Attachment ${index} 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> = {};
|
||||
|
||||
// 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);
|
||||
if (price)
|
||||
{
|
||||
if (price) {
|
||||
result[attachment._id] = Math.round(price);
|
||||
}
|
||||
}
|
||||
@ -536,25 +487,20 @@ export class InsuranceController
|
||||
* @param traderId Trader attachment insured against
|
||||
* @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;
|
||||
|
||||
if (this.randomUtil.getChance100(this.insuranceConfig.chanceNoAttachmentsTakenPercent))
|
||||
{
|
||||
if (this.randomUtil.getChance100(this.insuranceConfig.chanceNoAttachmentsTakenPercent)) {
|
||||
return removeCount;
|
||||
}
|
||||
|
||||
for (const attachmentId of Object.keys(weightedAttachmentByPrice))
|
||||
{
|
||||
for (const attachmentId of Object.keys(weightedAttachmentByPrice)) {
|
||||
// Below min price to be taken, skip
|
||||
if (weightedAttachmentByPrice[attachmentId] < this.insuranceConfig.minAttachmentRoublePriceToBeTaken)
|
||||
{
|
||||
if (weightedAttachmentByPrice[attachmentId] < this.insuranceConfig.minAttachmentRoublePriceToBeTaken) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.rollForDelete(traderId))
|
||||
{
|
||||
if (this.rollForDelete(traderId)) {
|
||||
removeCount++;
|
||||
}
|
||||
}
|
||||
@ -569,8 +515,7 @@ export class InsuranceController
|
||||
* @param toDelete The items that should be deleted.
|
||||
* @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));
|
||||
}
|
||||
|
||||
@ -581,8 +526,7 @@ export class InsuranceController
|
||||
* @param insurance The context of insurance to use.
|
||||
* @returns void
|
||||
*/
|
||||
protected sendMail(sessionID: string, insurance: Insurance): void
|
||||
{
|
||||
protected sendMail(sessionID: string, insurance: Insurance): void {
|
||||
const labsId = "laboratory";
|
||||
// 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.
|
||||
@ -590,21 +534,17 @@ export class InsuranceController
|
||||
|
||||
// Map is labs + insurance is disabled in base.json
|
||||
if (
|
||||
insurance.systemData?.location?.toLowerCase() === labsId
|
||||
&& !(this.databaseService.getLocation(labsId).base.Insurance)
|
||||
)
|
||||
{
|
||||
insurance.systemData?.location?.toLowerCase() === labsId &&
|
||||
!this.databaseService.getLocation(labsId).base.Insurance
|
||||
) {
|
||||
// Trader has labs-specific messages
|
||||
// Wipe out returnable items
|
||||
if (traderDialogMessages.insuranceFailedLabs?.length > 0)
|
||||
{
|
||||
if (traderDialogMessages.insuranceFailedLabs?.length > 0) {
|
||||
const insuranceFailedLabTemplates = traderDialogMessages.insuranceFailedLabs;
|
||||
insurance.messageTemplateId = this.randomUtil.getArrayValue(insuranceFailedLabTemplates);
|
||||
insurance.items = [];
|
||||
}
|
||||
}
|
||||
else if (insurance.items.length === 0)
|
||||
{
|
||||
} else if (insurance.items.length === 0) {
|
||||
// Not labs and no items to return
|
||||
const insuranceFailedTemplates = traderDialogMessages.insuranceFailed;
|
||||
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.
|
||||
* @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);
|
||||
if (!trader)
|
||||
{
|
||||
if (!trader) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -664,26 +602,25 @@ export class InsuranceController
|
||||
* @param sessionID Session id
|
||||
* @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 itemsToInsureCount = body.items.length;
|
||||
const itemsToPay = [];
|
||||
const inventoryItemsHash = {};
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Get price of all items being insured
|
||||
for (const key of body.items)
|
||||
{
|
||||
for (const key of body.items) {
|
||||
itemsToPay.push({
|
||||
id: Money.ROUBLES, // TODO: update to handle different currencies
|
||||
count: this.insuranceService.getRoublePriceToInsureItemWithTrader(
|
||||
pmcData, inventoryItemsHash[key],
|
||||
body.tid),
|
||||
pmcData,
|
||||
inventoryItemsHash[key],
|
||||
body.tid,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -699,14 +636,12 @@ export class InsuranceController
|
||||
|
||||
// pay for the item insurance
|
||||
this.paymentService.payMoney(pmcData, options, sessionID, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// 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 });
|
||||
}
|
||||
|
||||
@ -723,33 +658,30 @@ export class InsuranceController
|
||||
* @param sessionID session id
|
||||
* @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 pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
const inventoryItemsHash: Record<string, Item> = {};
|
||||
|
||||
for (const item of pmcData.Inventory.items)
|
||||
{
|
||||
for (const item of pmcData.Inventory.items) {
|
||||
inventoryItemsHash[item._id] = item;
|
||||
}
|
||||
|
||||
// Loop over each trader in request
|
||||
for (const trader of request.traders)
|
||||
{
|
||||
for (const trader of request.traders) {
|
||||
const items: Record<string, number> = {};
|
||||
|
||||
for (const itemId of request.items)
|
||||
{
|
||||
for (const itemId of request.items) {
|
||||
// Ensure hash has item in it
|
||||
if (!inventoryItemsHash[itemId])
|
||||
{
|
||||
if (!inventoryItemsHash[itemId]) {
|
||||
this.logger.debug(`Item with id: ${itemId} missing from player inventory, skipping`);
|
||||
continue;
|
||||
}
|
||||
items[inventoryItemsHash[itemId]._tpl] = this.insuranceService.getRoublePriceToInsureItemWithTrader(
|
||||
pmcData, inventoryItemsHash[itemId],
|
||||
trader);
|
||||
pmcData,
|
||||
inventoryItemsHash[itemId],
|
||||
trader,
|
||||
);
|
||||
}
|
||||
|
||||
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.
|
||||
interface EnrichedItem extends Item
|
||||
{
|
||||
name: string
|
||||
dynamicPrice: number
|
||||
interface EnrichedItem extends Item {
|
||||
name: string;
|
||||
dynamicPrice: number;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { LootGenerator } from "@spt/generators/LootGenerator";
|
||||
import { HideoutHelper } from "@spt/helpers/HideoutHelper";
|
||||
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
|
||||
@ -44,14 +43,14 @@ import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { MapMarkerService } from "@spt/services/MapMarkerService";
|
||||
import { PlayerService } from "@spt/services/PlayerService";
|
||||
import { RagfairOfferService } from "@spt/services/RagfairOfferService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class InventoryController
|
||||
{
|
||||
export class InventoryController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@ -74,8 +73,7 @@ export class InventoryController
|
||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Move Item
|
||||
@ -92,28 +90,23 @@ export class InventoryController
|
||||
moveRequest: IInventoryMoveRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
): void {
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Changes made to result apply to character inventory
|
||||
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
|
||||
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail)
|
||||
{
|
||||
if (moveRequest.fromOwner?.type === "Trader" && !ownerInventoryItems.isMail) {
|
||||
this.appendTraderExploitErrorResponse(output);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for item in inventory before allowing internal transfer
|
||||
const originalItemLocation = ownerInventoryItems.from.find((item) => item._id === moveRequest.item);
|
||||
if (!originalItemLocation)
|
||||
{
|
||||
if (!originalItemLocation) {
|
||||
// Internal item move but item never existed, possible dupe glitch
|
||||
this.appendTraderExploitErrorResponse(output);
|
||||
return;
|
||||
@ -122,20 +115,16 @@ export class InventoryController
|
||||
const originalLocationSlotId = originalItemLocation?.slotId;
|
||||
|
||||
const moveResult = this.inventoryHelper.moveItemInternal(pmcData, ownerInventoryItems.from, moveRequest);
|
||||
if (!moveResult.success)
|
||||
{
|
||||
if (!moveResult.success) {
|
||||
this.httpResponseUtil.appendErrorToOutput(output, moveResult.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.inventoryHelper.moveItemToProfile(ownerInventoryItems.from, ownerInventoryItems.to, moveRequest);
|
||||
}
|
||||
}
|
||||
@ -145,8 +134,7 @@ export class InventoryController
|
||||
* @param output Item event router response
|
||||
* @returns Item event router response
|
||||
*/
|
||||
protected appendTraderExploitErrorResponse(output: IItemEventRouterResponse): void
|
||||
{
|
||||
protected appendTraderExploitErrorResponse(output: IItemEventRouterResponse): void {
|
||||
this.httpResponseUtil.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-edit_trader_item"),
|
||||
@ -164,17 +152,15 @@ export class InventoryController
|
||||
request: IInventoryRemoveRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
if (request.fromOwner?.type === "Mail")
|
||||
{
|
||||
): void {
|
||||
if (request.fromOwner?.type === "Mail") {
|
||||
this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, request, output);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const profileToRemoveItemFrom
|
||||
= !request.fromOwner || request.fromOwner.id === pmcData._id
|
||||
const profileToRemoveItemFrom =
|
||||
!request.fromOwner || request.fromOwner.id === pmcData._id
|
||||
? pmcData
|
||||
: this.profileHelper.getFullProfile(sessionID).characters.scav;
|
||||
|
||||
@ -195,22 +181,19 @@ export class InventoryController
|
||||
request: IInventorySplitRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// Changes made to result apply to character inventory
|
||||
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(request, sessionID);
|
||||
|
||||
// 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);
|
||||
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
|
||||
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`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -258,15 +241,13 @@ export class InventoryController
|
||||
body: IInventoryMergeRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// Changes made to result apply to character inventory
|
||||
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
|
||||
|
||||
// Get source item (can be from player or trader or mail)
|
||||
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`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -277,8 +258,7 @@ export class InventoryController
|
||||
|
||||
// Get item being merged into
|
||||
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`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -287,25 +267,20 @@ export class InventoryController
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!destinationItem.upd?.StackObjectsCount)
|
||||
{
|
||||
if (!destinationItem.upd?.StackObjectsCount) {
|
||||
// No stackcount on destination, add one
|
||||
destinationItem.upd = { StackObjectsCount: 1 };
|
||||
}
|
||||
|
||||
if (!sourceItem.upd)
|
||||
{
|
||||
if (!sourceItem.upd) {
|
||||
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
|
||||
sourceItem.upd.StackObjectsCount = 1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -313,8 +288,7 @@ export class InventoryController
|
||||
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);
|
||||
if (indexOfItemToRemove === -1)
|
||||
{
|
||||
if (indexOfItemToRemove === -1) {
|
||||
const errorMessage = `Unable to find item: ${sourceItem._id} to remove from sender inventory`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -342,14 +316,12 @@ export class InventoryController
|
||||
body: IInventoryTransferRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const inventoryItems = this.inventoryHelper.getOwnerInventoryItems(body, sessionID);
|
||||
const sourceItem = inventoryItems.from.find((item) => item._id === body.item);
|
||||
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}`;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -358,8 +330,7 @@ export class InventoryController
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!destinationItem)
|
||||
{
|
||||
if (!destinationItem) {
|
||||
const errorMessage = `Unable to transfer stack, cannot find destination: ${body.with} `;
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -368,25 +339,20 @@ export class InventoryController
|
||||
return output;
|
||||
}
|
||||
|
||||
if (!sourceItem.upd)
|
||||
{
|
||||
if (!sourceItem.upd) {
|
||||
sourceItem.upd = { StackObjectsCount: 1 };
|
||||
}
|
||||
|
||||
const sourceStackCount = sourceItem.upd.StackObjectsCount;
|
||||
if (sourceStackCount > body.count)
|
||||
{
|
||||
if (sourceStackCount > body.count) {
|
||||
// Source items stack count greater than new desired count
|
||||
sourceItem.upd.StackObjectsCount = sourceStackCount - body.count;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Moving a full stack onto a smaller stack
|
||||
sourceItem.upd.StackObjectsCount = sourceStackCount - 1;
|
||||
}
|
||||
|
||||
if (!destinationItem.upd)
|
||||
{
|
||||
if (!destinationItem.upd) {
|
||||
destinationItem.upd = { StackObjectsCount: 1 };
|
||||
}
|
||||
const destinationStackCount = destinationItem.upd.StackObjectsCount;
|
||||
@ -404,25 +370,31 @@ export class InventoryController
|
||||
pmcData: IPmcData,
|
||||
request: IInventorySwapRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// During post-raid scav transfer, the swap may be in the scav inventory
|
||||
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);
|
||||
}
|
||||
|
||||
const itemOne = playerData.Inventory.items.find((x) => x._id === request.item);
|
||||
if (!itemOne)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-unable_to_find_item_to_swap", { item1Id: request.item, item2Id: request.item2 }));
|
||||
if (!itemOne) {
|
||||
this.logger.error(
|
||||
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);
|
||||
if (!itemTwo)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-unable_to_find_item_to_swap", { item1Id: request.item2, item2Id: request.item }));
|
||||
if (!itemTwo) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("inventory-unable_to_find_item_to_swap", {
|
||||
item1Id: request.item2,
|
||||
item2Id: request.item,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// to.id is the parentid
|
||||
@ -432,23 +404,17 @@ export class InventoryController
|
||||
itemOne.slotId = request.to.container;
|
||||
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
delete itemOne.location;
|
||||
}
|
||||
|
||||
itemTwo.parentId = request.to2.id;
|
||||
itemTwo.slotId = request.to2.container;
|
||||
if (request.to2.location)
|
||||
{
|
||||
if (request.to2.location) {
|
||||
itemTwo.location = request.to2.location;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
delete itemTwo.location;
|
||||
}
|
||||
|
||||
@ -463,22 +429,21 @@ export class InventoryController
|
||||
pmcData: IPmcData,
|
||||
request: IInventoryFoldRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// May need to reassign to scav profile
|
||||
let playerData = pmcData;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
const itemToFold = playerData.Inventory.items.find((item) => item?._id === request.item);
|
||||
if (!itemToFold)
|
||||
{
|
||||
if (!itemToFold) {
|
||||
// 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: {} };
|
||||
}
|
||||
@ -502,20 +467,17 @@ export class InventoryController
|
||||
pmcData: IPmcData,
|
||||
body: IInventoryToggleRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// May need to reassign to scav profile
|
||||
let playerData = pmcData;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
const itemToToggle = playerData.Inventory.items.find((x) => x._id === body.item);
|
||||
if (itemToToggle)
|
||||
{
|
||||
if (itemToToggle) {
|
||||
this.itemHelper.addUpdObjectToItem(
|
||||
itemToToggle,
|
||||
this.localisationService.getText("inventory-item_to_toggle_missing_upd", itemToToggle._id),
|
||||
@ -538,16 +500,13 @@ export class InventoryController
|
||||
* @param sessionID session id
|
||||
* @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);
|
||||
if (!itemToTag)
|
||||
{
|
||||
if (!itemToTag) {
|
||||
return { warnings: [], profileChanges: {} };
|
||||
}
|
||||
|
||||
if (!itemToTag.upd)
|
||||
{
|
||||
if (!itemToTag.upd) {
|
||||
itemToTag.upd = {};
|
||||
}
|
||||
|
||||
@ -564,14 +523,11 @@ export class InventoryController
|
||||
* @param sessionID Session id
|
||||
* @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
|
||||
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
|
||||
if (pmcData.Inventory.fastPanel[index] === bindRequest.item)
|
||||
{
|
||||
if (pmcData.Inventory.fastPanel[index] === bindRequest.item) {
|
||||
pmcData.Inventory.fastPanel[index] = "";
|
||||
|
||||
break;
|
||||
@ -595,8 +551,7 @@ export class InventoryController
|
||||
request: IInventoryBindRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// Remove kvp from requested fast panel index
|
||||
delete pmcData.Inventory.fastPanel[request.index];
|
||||
}
|
||||
@ -614,48 +569,37 @@ export class InventoryController
|
||||
body: IInventoryExamineRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
let itemId = "";
|
||||
if ("fromOwner" in body)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ("fromOwner" in body) {
|
||||
try {
|
||||
itemId = this.getExaminedItemTpl(body);
|
||||
}
|
||||
catch
|
||||
{
|
||||
} catch {
|
||||
this.logger.error(this.localisationService.getText("inventory-examine_item_does_not_exist", body.item));
|
||||
}
|
||||
|
||||
// get hideout item
|
||||
if (body.fromOwner.type === "HideoutProduction")
|
||||
{
|
||||
if (body.fromOwner.type === "HideoutProduction") {
|
||||
itemId = body.item;
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemId)
|
||||
{
|
||||
if (!itemId) {
|
||||
// item template
|
||||
if (body.item in this.databaseService.getItems())
|
||||
{
|
||||
if (body.item in this.databaseService.getItems()) {
|
||||
itemId = body.item;
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemId)
|
||||
{
|
||||
if (!itemId) {
|
||||
// Player inventory
|
||||
const target = pmcData.Inventory.items.find((item) => item._id === body.item);
|
||||
if (target)
|
||||
{
|
||||
if (target) {
|
||||
itemId = target._tpl;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId)
|
||||
{
|
||||
if (itemId) {
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||
this.flagItemsAsInspectedAndRewardXp([itemId], fullProfile);
|
||||
}
|
||||
@ -668,14 +612,13 @@ export class InventoryController
|
||||
* @param itemTpls Inspected item tpls
|
||||
* @param fullProfile Profile to add xp to
|
||||
*/
|
||||
protected flagItemsAsInspectedAndRewardXp(itemTpls: string[], fullProfile: ISptProfile): void
|
||||
{
|
||||
for (const itemTpl of itemTpls)
|
||||
{
|
||||
protected flagItemsAsInspectedAndRewardXp(itemTpls: string[], fullProfile: ISptProfile): void {
|
||||
for (const itemTpl of itemTpls) {
|
||||
const item = this.itemHelper.getItem(itemTpl);
|
||||
if (!item[0])
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("inventory-unable_to_inspect_item_not_in_db", itemTpl));
|
||||
if (!item[0]) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("inventory-unable_to_inspect_item_not_in_db", itemTpl),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -700,47 +643,40 @@ export class InventoryController
|
||||
* @param request Response request
|
||||
* @returns tplId
|
||||
*/
|
||||
protected getExaminedItemTpl(request: IInventoryExamineRequestData): string
|
||||
{
|
||||
if (this.presetHelper.isPreset(request.item))
|
||||
{
|
||||
protected getExaminedItemTpl(request: IInventoryExamineRequestData): string {
|
||||
if (this.presetHelper.isPreset(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
|
||||
return this.fenceService.getRawFenceAssorts().items.find((x) => x._id === request.item)?._tpl;
|
||||
}
|
||||
|
||||
if (request.fromOwner.type === "Trader")
|
||||
{
|
||||
if (request.fromOwner.type === "Trader") {
|
||||
// Not fence
|
||||
// get tpl from trader assort
|
||||
return this.databaseService.getTrader(request.fromOwner.id).assort.items
|
||||
.find((item) => item._id === request.item)?._tpl;
|
||||
return this.databaseService
|
||||
.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
|
||||
const item = this.itemHelper.getItem(request.item);
|
||||
if (item[0])
|
||||
{
|
||||
if (item[0]) {
|
||||
return item[1]._id;
|
||||
}
|
||||
|
||||
// Try alternate way of getting offer if first approach fails
|
||||
let offer = this.ragfairOfferService.getOfferByOfferId(request.item);
|
||||
if (!offer)
|
||||
{
|
||||
if (!offer) {
|
||||
offer = this.ragfairOfferService.getOfferByOfferId(request.fromOwner.id);
|
||||
}
|
||||
|
||||
// Try find examine item inside offer items array
|
||||
const matchingItem = offer.items.find((offerItem) => offerItem._id === request.item);
|
||||
if (matchingItem)
|
||||
{
|
||||
if (matchingItem) {
|
||||
return matchingItem._tpl;
|
||||
}
|
||||
|
||||
@ -753,10 +689,8 @@ export class InventoryController
|
||||
pmcData: IPmcData,
|
||||
body: IInventoryReadEncyclopediaRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
for (const id of body.ids)
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
for (const id of body.ids) {
|
||||
pmcData.Encyclopedia[id] = true;
|
||||
}
|
||||
|
||||
@ -770,26 +704,22 @@ export class InventoryController
|
||||
* @param request sort request
|
||||
* @param sessionID Session id
|
||||
*/
|
||||
public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): void
|
||||
{
|
||||
for (const change of request.changedItems)
|
||||
{
|
||||
public sortInventory(pmcData: IPmcData, request: IInventorySortRequestData, sessionID: string): void {
|
||||
for (const change of request.changedItems) {
|
||||
const inventoryItem = pmcData.Inventory.items.find((item) => item._id === change._id);
|
||||
if (!inventoryItem)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("inventory-unable_to_sort_inventory_restart_game", change._id));
|
||||
if (!inventoryItem) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("inventory-unable_to_sort_inventory_restart_game", change._id),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
inventoryItem.parentId = change.parentId;
|
||||
inventoryItem.slotId = change.slotId;
|
||||
if (change.location)
|
||||
{
|
||||
if (change.location) {
|
||||
inventoryItem.location = change.location;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
delete inventoryItem.location;
|
||||
}
|
||||
}
|
||||
@ -808,8 +738,7 @@ export class InventoryController
|
||||
request: IInventoryCreateMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const adjustedMapItem = this.mapMarkerService.createMarkerOnMap(pmcData, request);
|
||||
|
||||
// Sync with client
|
||||
@ -828,8 +757,7 @@ export class InventoryController
|
||||
request: IInventoryDeleteMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const mapItem = this.mapMarkerService.deleteMarkerFromMap(pmcData, request);
|
||||
|
||||
// sync with client
|
||||
@ -848,8 +776,7 @@ export class InventoryController
|
||||
request: IInventoryEditMarkerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const mapItem = this.mapMarkerService.editMarkerOnMap(pmcData, request);
|
||||
|
||||
// sync with client
|
||||
@ -869,8 +796,7 @@ export class InventoryController
|
||||
body: IOpenRandomLootContainerRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
/** Container player opened in their inventory */
|
||||
const openedItem = pmcData.Inventory.items.find((item) => item._id === body.item);
|
||||
const containerDetailsDb = this.itemHelper.getItem(openedItem._tpl);
|
||||
@ -878,37 +804,32 @@ export class InventoryController
|
||||
|
||||
let foundInRaid = openedItem.upd?.SpawnedInSession;
|
||||
const rewards: Item[][] = [];
|
||||
const unlockedWeaponCrates = ["665829424de4820934746ce6", "665732e7ac60f009f270d1ef", "665888282c4a1b73af576b77"];// Temp fix for unlocked weapon crate hideout craft
|
||||
if (isSealedWeaponBox || unlockedWeaponCrates.includes(containerDetailsDb[1]._id))
|
||||
{
|
||||
const unlockedWeaponCrates = [
|
||||
"665829424de4820934746ce6",
|
||||
"665732e7ac60f009f270d1ef",
|
||||
"665888282c4a1b73af576b77",
|
||||
]; // Temp fix for unlocked weapon crate hideout craft
|
||||
if (isSealedWeaponBox || unlockedWeaponCrates.includes(containerDetailsDb[1]._id)) {
|
||||
const containerSettings = this.inventoryHelper.getInventoryConfig().sealedAirdropContainer;
|
||||
rewards.push(...this.lootGenerator.getSealedWeaponCaseLoot(containerSettings));
|
||||
|
||||
if (containerSettings.foundInRaid)
|
||||
{
|
||||
if (containerSettings.foundInRaid) {
|
||||
foundInRaid = containerSettings.foundInRaid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
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`);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
rewards.push(...this.lootGenerator.getRandomLootContainerLoot(rewardContainerDetails));
|
||||
|
||||
if (rewardContainerDetails.foundInRaid)
|
||||
{
|
||||
if (rewardContainerDetails.foundInRaid) {
|
||||
foundInRaid = rewardContainerDetails.foundInRaid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rewards.length > 0)
|
||||
{
|
||||
if (rewards.length > 0) {
|
||||
const addItemsRequest: IAddItemsDirectRequest = {
|
||||
itemsWithModsToAdd: rewards,
|
||||
foundInRaid: foundInRaid,
|
||||
@ -916,8 +837,7 @@ export class InventoryController
|
||||
useSortingTable: true,
|
||||
};
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -926,19 +846,16 @@ export class InventoryController
|
||||
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);
|
||||
for (const event of request.events)
|
||||
{
|
||||
for (const event of request.events) {
|
||||
// Hard coded to `SYSTEM` for now
|
||||
// TODO: make this dynamic
|
||||
const dialog = fullProfile.dialogues["59e7125688a45068a6249071"];
|
||||
const mail = dialog.messages.find((message) => message._id === event.MessageId);
|
||||
const mailEvent = mail.profileChangeEvents.find((changeEvent) => changeEvent._id === event.EventId);
|
||||
|
||||
switch (mailEvent.Type)
|
||||
{
|
||||
switch (mailEvent.Type) {
|
||||
case "TraderSalesSum":
|
||||
pmcData.TradersInfo[mailEvent.entity].salesSum = mailEvent.value;
|
||||
this.traderHelper.lvlUp(mailEvent.entity, pmcData);
|
||||
@ -955,11 +872,9 @@ export class InventoryController
|
||||
this.traderHelper.validateTraderStandingsAndPlayerLevelForProfile(sessionId);
|
||||
this.logger.success(`Set profile xp to: ${mailEvent.value}`);
|
||||
break;
|
||||
case "SkillPoints":
|
||||
{
|
||||
case "SkillPoints": {
|
||||
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}`);
|
||||
continue;
|
||||
}
|
||||
@ -967,8 +882,7 @@ export class InventoryController
|
||||
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
|
||||
break;
|
||||
}
|
||||
case "ExamineAllItems":
|
||||
{
|
||||
case "ExamineAllItems": {
|
||||
const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node");
|
||||
this.flagItemsAsInspectedAndRewardXp(
|
||||
itemsToInspect.map((x) => x._id),
|
||||
@ -984,8 +898,7 @@ export class InventoryController
|
||||
|
||||
break;
|
||||
case "AssortmentUnlockRule":
|
||||
if (!fullProfile.spt.blacklistedItemTpls)
|
||||
{
|
||||
if (!fullProfile.spt.blacklistedItemTpls) {
|
||||
fullProfile.spt.blacklistedItemTpls = [];
|
||||
}
|
||||
fullProfile.spt.blacklistedItemTpls.push(mailEvent.entity);
|
||||
@ -996,11 +909,9 @@ export class InventoryController
|
||||
const areaName = mailEvent.entity;
|
||||
const newValue = mailEvent.value;
|
||||
const hideoutAreaCode = HideoutAreas[areaName.toUpperCase()];
|
||||
if (hideoutAreaCode !== undefined)
|
||||
{
|
||||
if (hideoutAreaCode !== undefined) {
|
||||
const desiredArea = pmcData.Hideout.Areas.find((area) => area.type === hideoutAreaCode);
|
||||
if (desiredArea)
|
||||
{
|
||||
if (desiredArea) {
|
||||
desiredArea.level = newValue;
|
||||
}
|
||||
}
|
||||
@ -1014,23 +925,17 @@ export class InventoryController
|
||||
}
|
||||
}
|
||||
|
||||
public setFavoriteItem(pmcData: IPmcData, request: ISetFavoriteItems, sessionId: string): void
|
||||
{
|
||||
if (!pmcData.Inventory.favoriteItems)
|
||||
{
|
||||
public setFavoriteItem(pmcData: IPmcData, request: ISetFavoriteItems, sessionId: string): void {
|
||||
if (!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
|
||||
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId);
|
||||
if (indexOfItemAlreadyFavorited > -1)
|
||||
{
|
||||
if (indexOfItemAlreadyFavorited > -1) {
|
||||
pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pmcData.Inventory.favoriteItems.push(itemId);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
|
||||
@ -18,10 +17,10 @@ import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class LauncherController
|
||||
{
|
||||
export class LauncherController {
|
||||
protected coreConfig: ICoreConfig;
|
||||
|
||||
constructor(
|
||||
@ -36,16 +35,15 @@ export class LauncherController
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("PreSptModLoader") protected preSptModLoader: PreSptModLoader,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||
}
|
||||
|
||||
public connect(): IConnectResponse
|
||||
{
|
||||
public connect(): IConnectResponse {
|
||||
// Get all possible profile types + filter out any that are blacklisted
|
||||
const profileKeys = Object.keys(this.databaseService.getProfiles())
|
||||
.filter((key) => !this.coreConfig.features.createNewProfileTypesBlacklist.includes(key));
|
||||
const profileKeys = Object.keys(this.databaseService.getProfiles()).filter(
|
||||
(key) => !this.coreConfig.features.createNewProfileTypesBlacklist.includes(key),
|
||||
);
|
||||
|
||||
return {
|
||||
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"
|
||||
* @returns Dictionary of profile types with related descriptive text
|
||||
*/
|
||||
protected getProfileDescriptions(): Record<string, string>
|
||||
{
|
||||
protected getProfileDescriptions(): Record<string, string> {
|
||||
const result = {};
|
||||
const dbProfiles = this.databaseService.getProfiles();
|
||||
for (const profileKey in dbProfiles)
|
||||
{
|
||||
for (const profileKey in dbProfiles) {
|
||||
const localeKey = dbProfiles[profileKey]?.descriptionLocaleKey;
|
||||
if (!localeKey)
|
||||
{
|
||||
if (!localeKey) {
|
||||
this.logger.warning(this.localisationService.getText("launcher-missing_property", profileKey));
|
||||
continue;
|
||||
}
|
||||
@ -78,18 +73,14 @@ export class LauncherController
|
||||
return result;
|
||||
}
|
||||
|
||||
public find(sessionId: string): Info
|
||||
{
|
||||
public find(sessionId: string): Info {
|
||||
return this.saveServer.getProfiles()[sessionId]?.info;
|
||||
}
|
||||
|
||||
public login(info: ILoginRequestData): string
|
||||
{
|
||||
for (const sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
public login(info: ILoginRequestData): string {
|
||||
for (const sessionID in this.saveServer.getProfiles()) {
|
||||
const account = this.saveServer.getProfile(sessionID).info;
|
||||
if (info.username === account.username)
|
||||
{
|
||||
if (info.username === account.username) {
|
||||
return sessionID;
|
||||
}
|
||||
}
|
||||
@ -97,12 +88,9 @@ export class LauncherController
|
||||
return "";
|
||||
}
|
||||
|
||||
public register(info: IRegisterData): string
|
||||
{
|
||||
for (const sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
if (info.username === this.saveServer.getProfile(sessionID).info.username)
|
||||
{
|
||||
public register(info: IRegisterData): string {
|
||||
for (const sessionID in this.saveServer.getProfiles()) {
|
||||
if (info.username === this.saveServer.getProfile(sessionID).info.username) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -110,8 +98,7 @@ export class LauncherController
|
||||
return this.createAccount(info);
|
||||
}
|
||||
|
||||
protected createAccount(info: IRegisterData): string
|
||||
{
|
||||
protected createAccount(info: IRegisterData): string {
|
||||
const profileId = this.generateProfileId();
|
||||
const scavId = this.generateProfileId();
|
||||
const newProfileDetails: Info = {
|
||||
@ -131,39 +118,33 @@ export class LauncherController
|
||||
return profileId;
|
||||
}
|
||||
|
||||
protected generateProfileId(): string
|
||||
{
|
||||
protected generateProfileId(): string {
|
||||
const timestamp = this.timeUtil.getTimestamp();
|
||||
|
||||
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 counterStr = counter.toString(16).padStart(16, "0");
|
||||
|
||||
return timeStampStr.toLowerCase() + counterStr.toLowerCase();
|
||||
}
|
||||
|
||||
public changeUsername(info: IChangeRequestData): string
|
||||
{
|
||||
public changeUsername(info: IChangeRequestData): string {
|
||||
const sessionID = this.login(info);
|
||||
|
||||
if (sessionID)
|
||||
{
|
||||
if (sessionID) {
|
||||
this.saveServer.getProfile(sessionID).info.username = info.change;
|
||||
}
|
||||
|
||||
return sessionID;
|
||||
}
|
||||
|
||||
public changePassword(info: IChangeRequestData): string
|
||||
{
|
||||
public changePassword(info: IChangeRequestData): string {
|
||||
const sessionID = this.login(info);
|
||||
|
||||
if (sessionID)
|
||||
{
|
||||
if (sessionID) {
|
||||
this.saveServer.getProfile(sessionID).info.password = info.change;
|
||||
}
|
||||
|
||||
@ -175,17 +156,14 @@ export class LauncherController
|
||||
* @param info IRegisterData
|
||||
* @returns Session id
|
||||
*/
|
||||
public wipe(info: IRegisterData): string
|
||||
{
|
||||
if (!this.coreConfig.allowProfileWipe)
|
||||
{
|
||||
public wipe(info: IRegisterData): string {
|
||||
if (!this.coreConfig.allowProfileWipe) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionID = this.login(info);
|
||||
|
||||
if (sessionID)
|
||||
{
|
||||
if (sessionID) {
|
||||
const profile = this.saveServer.getProfile(sessionID);
|
||||
profile.info.edition = info.edition;
|
||||
profile.info.wipe = true;
|
||||
@ -194,8 +172,7 @@ export class LauncherController
|
||||
return sessionID;
|
||||
}
|
||||
|
||||
public getCompatibleTarkovVersion(): string
|
||||
{
|
||||
public getCompatibleTarkovVersion(): string {
|
||||
return this.coreConfig.compatibleTarkovVersion;
|
||||
}
|
||||
|
||||
@ -203,8 +180,7 @@ export class LauncherController
|
||||
* Get the mods the server has currently loaded
|
||||
* @returns Dictionary of mod name and mod details
|
||||
*/
|
||||
public getLoadedServerMods(): Record<string, IPackageJsonData>
|
||||
{
|
||||
public getLoadedServerMods(): Record<string, IPackageJsonData> {
|
||||
return this.preSptModLoader.getImportedModDetails();
|
||||
}
|
||||
|
||||
@ -213,12 +189,10 @@ export class LauncherController
|
||||
* @param sessionId Player id
|
||||
* @returns Array of mod details
|
||||
*/
|
||||
public getServerModsProfileUsed(sessionId: string): ModDetails[]
|
||||
{
|
||||
public getServerModsProfileUsed(sessionId: string): ModDetails[] {
|
||||
const profile = this.profileHelper.getFullProfile(sessionId);
|
||||
|
||||
if (profile?.spt?.mods)
|
||||
{
|
||||
if (profile?.spt?.mods) {
|
||||
return this.preSptModLoader.getProfileModsGroupedByModName(profile?.spt?.mods);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ILocationsGenerateAllResponse } from "@spt/models/eft/common/ILocationsSourceDestinationBase";
|
||||
import { IGetAirdropLootResponse } from "@spt/models/eft/location/IGetAirdropLootResponse";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
@ -9,10 +8,10 @@ import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { AirdropService } from "@spt/services/AirdropService";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class LocationController
|
||||
{
|
||||
export class LocationController {
|
||||
protected locationConfig: ILocationConfig;
|
||||
|
||||
constructor(
|
||||
@ -21,8 +20,7 @@ export class LocationController
|
||||
@inject("AirdropService") protected airdropService: AirdropService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
|
||||
}
|
||||
|
||||
@ -32,15 +30,12 @@ export class LocationController
|
||||
* @param sessionId Players Id
|
||||
* @returns ILocationsGenerateAllResponse
|
||||
*/
|
||||
public generateAll(sessionId: string): ILocationsGenerateAllResponse
|
||||
{
|
||||
public generateAll(sessionId: string): ILocationsGenerateAllResponse {
|
||||
const locationsFromDb = this.databaseService.getLocations();
|
||||
const locations: ILocations = {};
|
||||
for (const mapName in locationsFromDb)
|
||||
{
|
||||
for (const mapName in locationsFromDb) {
|
||||
const mapBase = locationsFromDb[mapName]?.base;
|
||||
if (!mapBase)
|
||||
{
|
||||
if (!mapBase) {
|
||||
this.logger.debug(`Map: ${mapName} has no base json file, skipping generation`);
|
||||
continue;
|
||||
}
|
||||
@ -55,8 +50,7 @@ export class LocationController
|
||||
}
|
||||
|
||||
/** Handle client/airdrop/loot */
|
||||
public getAirdropLoot(): IGetAirdropLootResponse
|
||||
{
|
||||
public getAirdropLoot(): IGetAirdropLootResponse {
|
||||
return this.airdropService.generateAirdropLoot();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
||||
import { ContextVariableType } from "@spt/context/ContextVariableType";
|
||||
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 { ProfileSnapshotService } from "@spt/services/ProfileSnapshotService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class MatchController
|
||||
{
|
||||
export class MatchController {
|
||||
protected matchConfig: IMatchConfig;
|
||||
protected pmcConfig: IPmcConfig;
|
||||
|
||||
@ -35,26 +34,22 @@ export class MatchController
|
||||
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
||||
@inject("LocationLifecycleService") protected locationLifecycleService: LocationLifecycleService,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.matchConfig = this.configServer.getConfig(ConfigTypes.MATCH);
|
||||
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
|
||||
}
|
||||
|
||||
public getEnabled(): boolean
|
||||
{
|
||||
public getEnabled(): boolean {
|
||||
return this.matchConfig.enabled;
|
||||
}
|
||||
|
||||
/** Handle client/match/group/delete */
|
||||
public deleteGroup(info: any): void
|
||||
{
|
||||
public deleteGroup(info: any): void {
|
||||
this.matchLocationService.deleteGroup(info);
|
||||
}
|
||||
|
||||
/** 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: [] };
|
||||
|
||||
// get list of players joining into the match
|
||||
@ -77,8 +72,7 @@ export class MatchController
|
||||
}
|
||||
|
||||
/** Handle client/match/group/status */
|
||||
public getGroupStatus(info: IMatchGroupStatusRequest): IMatchGroupStatusResponse
|
||||
{
|
||||
public getGroupStatus(info: IMatchGroupStatusRequest): IMatchGroupStatusResponse {
|
||||
return { players: [], maxPveCountExceeded: false };
|
||||
}
|
||||
|
||||
@ -87,16 +81,14 @@ export class MatchController
|
||||
* @param request Raid config request
|
||||
* @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
|
||||
this.applicationContext.addValue(ContextVariableType.RAID_CONFIGURATION, request);
|
||||
|
||||
// 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
|
||||
if (!this.pmcConfig.useDifficultyOverride)
|
||||
{
|
||||
if (!this.pmcConfig.useDifficultyOverride) {
|
||||
this.pmcConfig.difficulty = this.convertDifficultyDropdownIntoBotDifficulty(
|
||||
request.wavesSettings.botDifficulty,
|
||||
);
|
||||
@ -112,11 +104,9 @@ export class MatchController
|
||||
* @param botDifficulty dropdown difficulty value
|
||||
* @returns bot difficulty
|
||||
*/
|
||||
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string
|
||||
{
|
||||
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string {
|
||||
// Edge case medium - must be altered
|
||||
if (botDifficulty.toLowerCase() === "medium")
|
||||
{
|
||||
if (botDifficulty.toLowerCase() === "medium") {
|
||||
return "normal";
|
||||
}
|
||||
|
||||
@ -124,14 +114,12 @@ export class MatchController
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,22 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { Note } from "@spt/models/eft/common/tables/IBotBase";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { INoteActionData } from "@spt/models/eft/notes/INoteActionData";
|
||||
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class NoteController
|
||||
{
|
||||
constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder)
|
||||
{}
|
||||
export class NoteController {
|
||||
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 };
|
||||
pmcData.Notes.Notes.push(newNote);
|
||||
|
||||
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];
|
||||
noteToEdit.Time = body.note.Time;
|
||||
noteToEdit.Text = body.note.Text;
|
||||
@ -28,8 +24,7 @@ export class NoteController
|
||||
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);
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { HttpServerHelper } from "@spt/helpers/HttpServerHelper";
|
||||
import { NotifierHelper } from "@spt/helpers/NotifierHelper";
|
||||
import { INotifierChannel } from "@spt/models/eft/notifier/INotifier";
|
||||
import { NotificationService } from "@spt/services/NotificationService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class NotifierController
|
||||
{
|
||||
export class NotifierController {
|
||||
protected pollInterval = 300;
|
||||
protected timeout = 15000;
|
||||
|
||||
@ -14,8 +13,7 @@ export class NotifierController
|
||||
@inject("NotifierHelper") protected notifierHelper: NotifierHelper,
|
||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
||||
@inject("NotificationService") protected notificationService: NotificationService,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Resolve an array of session notifications.
|
||||
@ -24,10 +22,8 @@ export class NotifierController
|
||||
* one or more appear or when a timeout expires.
|
||||
* If no notifications are available after the timeout, use a default message.
|
||||
*/
|
||||
public async notifyAsync(sessionID: string): Promise<unknown>
|
||||
{
|
||||
return new Promise((resolve) =>
|
||||
{
|
||||
public async notifyAsync(sessionID: string): Promise<unknown> {
|
||||
return new Promise((resolve) => {
|
||||
// keep track of our timeout
|
||||
let counter = 0;
|
||||
|
||||
@ -35,17 +31,14 @@ export class NotifierController
|
||||
* Check for notifications, resolve if any, otherwise poll
|
||||
* intermittently for a period of time.
|
||||
*/
|
||||
const checkNotifications = () =>
|
||||
{
|
||||
const checkNotifications = () => {
|
||||
/**
|
||||
* If there are no pending messages we should either check again later
|
||||
* 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
|
||||
if (counter > this.timeout)
|
||||
{
|
||||
if (counter > this.timeout) {
|
||||
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}`;
|
||||
}
|
||||
|
||||
/** Handle client/notifier/channel/create */
|
||||
public getChannel(sessionID: string): INotifierChannel
|
||||
{
|
||||
public getChannel(sessionID: string): INotifierChannel {
|
||||
return {
|
||||
server: this.httpServerHelper.buildUrl(),
|
||||
channel_id: sessionID,
|
||||
|
@ -1,28 +1,23 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
||||
import { IPreset } from "@spt/models/eft/common/IGlobals";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class PresetController
|
||||
{
|
||||
export class PresetController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("PresetHelper") protected presetHelper: PresetHelper,
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
public initialize(): void
|
||||
{
|
||||
public initialize(): void {
|
||||
const presets: [string, IPreset][] = Object.entries(this.databaseService.getGlobals().ItemPresets);
|
||||
const reverse: Record<string, string[]> = {};
|
||||
|
||||
for (const [id, preset] of presets)
|
||||
{
|
||||
if (id !== preset._id)
|
||||
{
|
||||
for (const [id, preset] of presets) {
|
||||
if (id !== preset._id) {
|
||||
this.logger.error(
|
||||
`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;
|
||||
|
||||
if (!(tpl in reverse))
|
||||
{
|
||||
if (!(tpl in reverse)) {
|
||||
reverse[tpl] = [];
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
||||
import { DialogueHelper } from "@spt/helpers/DialogueHelper";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
@ -31,13 +30,13 @@ import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { MailSendService } from "@spt/services/MailSendService";
|
||||
import { ProfileFixerService } from "@spt/services/ProfileFixerService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class ProfileController
|
||||
{
|
||||
export class ProfileController {
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@ -56,14 +55,12 @@ export class ProfileController
|
||||
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle /launcher/profiles
|
||||
*/
|
||||
public getMiniProfiles(): IMiniProfile[]
|
||||
{
|
||||
public getMiniProfiles(): IMiniProfile[] {
|
||||
const allProfiles = Object.keys(this.saveServer.getProfiles());
|
||||
|
||||
return allProfiles.map((sessionId) => this.getMiniProfile(sessionId));
|
||||
@ -72,11 +69,9 @@ export class ProfileController
|
||||
/**
|
||||
* Handle launcher/profile/info
|
||||
*/
|
||||
public getMiniProfile(sessionID: string): IMiniProfile
|
||||
{
|
||||
public getMiniProfile(sessionID: string): IMiniProfile {
|
||||
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`);
|
||||
}
|
||||
|
||||
@ -84,8 +79,7 @@ export class ProfileController
|
||||
const maxlvl = this.profileHelper.getMaxLevel();
|
||||
|
||||
// Player hasn't completed profile creation process, send defaults
|
||||
if (!pmc?.Info?.Level)
|
||||
{
|
||||
if (!pmc?.Info?.Level) {
|
||||
return {
|
||||
username: profile.info?.username ?? "",
|
||||
nickname: "unknown",
|
||||
@ -109,9 +103,7 @@ export class ProfileController
|
||||
side: pmc.Info.Side,
|
||||
currlvl: pmc.Info.Level,
|
||||
currexp: pmc.Info.Experience ?? 0,
|
||||
prevexp: currlvl === 0
|
||||
? 0
|
||||
: this.profileHelper.getExperience(currlvl),
|
||||
prevexp: currlvl === 0 ? 0 : this.profileHelper.getExperience(currlvl),
|
||||
nextlvl: nextlvl,
|
||||
maxlvl: maxlvl,
|
||||
edition: profile.info?.edition ?? "",
|
||||
@ -123,8 +115,7 @@ export class ProfileController
|
||||
/**
|
||||
* Handle client/game/profile/list
|
||||
*/
|
||||
public getCompleteProfile(sessionID: string): IPmcData[]
|
||||
{
|
||||
public getCompleteProfile(sessionID: string): IPmcData[] {
|
||||
return this.profileHelper.getCompleteProfile(sessionID);
|
||||
}
|
||||
|
||||
@ -134,11 +125,11 @@ export class ProfileController
|
||||
* @param sessionID Player id
|
||||
* @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 profileTemplate: ITemplateSide
|
||||
= this.cloner.clone(this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()]);
|
||||
const profileTemplate: ITemplateSide = this.cloner.clone(
|
||||
this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()],
|
||||
);
|
||||
const pmcData = profileTemplate.character;
|
||||
|
||||
// Delete existing profile
|
||||
@ -166,8 +157,7 @@ export class ProfileController
|
||||
|
||||
this.updateInventoryEquipmentId(pmcData);
|
||||
|
||||
if (!pmcData.UnlockedInfo)
|
||||
{
|
||||
if (!pmcData.UnlockedInfo) {
|
||||
pmcData.UnlockedInfo = { unlockedProductionRecipe: [] };
|
||||
}
|
||||
|
||||
@ -200,14 +190,12 @@ export class ProfileController
|
||||
|
||||
this.saveServer.addProfile(profileDetails);
|
||||
|
||||
if (profileTemplate.trader.setQuestsAvailableForStart)
|
||||
{
|
||||
if (profileTemplate.trader.setQuestsAvailableForStart) {
|
||||
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
|
||||
}
|
||||
|
||||
// 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, [
|
||||
QuestStatus.AvailableForStart,
|
||||
QuestStatus.Started,
|
||||
@ -234,8 +222,7 @@ export class ProfileController
|
||||
this.saveServer.saveProfile(sessionID);
|
||||
|
||||
// Requires to enable seasonal changes after creating fresh profile
|
||||
if (this.seasonalEventService.isAutomaticEventDetectionEnabled())
|
||||
{
|
||||
if (this.seasonalEventService.isAutomaticEventDetectionEnabled()) {
|
||||
this.seasonalEventService.enableSeasonalEvents(sessionID);
|
||||
}
|
||||
|
||||
@ -246,22 +233,18 @@ export class ProfileController
|
||||
* make profiles pmcData.Inventory.equipment unique
|
||||
* @param pmcData Profile to update
|
||||
*/
|
||||
protected updateInventoryEquipmentId(pmcData: IPmcData): void
|
||||
{
|
||||
protected updateInventoryEquipmentId(pmcData: IPmcData): void {
|
||||
const oldEquipmentId = pmcData.Inventory.equipment;
|
||||
pmcData.Inventory.equipment = this.hashUtil.generate();
|
||||
|
||||
for (const item of pmcData.Inventory.items)
|
||||
{
|
||||
if (item.parentId === oldEquipmentId)
|
||||
{
|
||||
for (const item of pmcData.Inventory.items) {
|
||||
if (item.parentId === oldEquipmentId) {
|
||||
item.parentId = pmcData.Inventory.equipment;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item._id === oldEquipmentId)
|
||||
{
|
||||
if (item._id === oldEquipmentId) {
|
||||
item._id = pmcData.Inventory.equipment;
|
||||
}
|
||||
}
|
||||
@ -271,14 +254,10 @@ export class ProfileController
|
||||
* Delete a profile
|
||||
* @param sessionID Id of profile to delete
|
||||
*/
|
||||
protected deleteProfileBySessionId(sessionID: string): void
|
||||
{
|
||||
if (sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
protected deleteProfileBySessionId(sessionID: string): void {
|
||||
if (sessionID in this.saveServer.getProfiles()) {
|
||||
this.saveServer.deleteProfileById(sessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID),
|
||||
);
|
||||
@ -296,10 +275,8 @@ export class ProfileController
|
||||
profileDetails: ISptProfile,
|
||||
sessionID: string,
|
||||
response: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
for (const quest of profileDetails.characters.pmc.Quests)
|
||||
{
|
||||
): void {
|
||||
for (const quest of profileDetails.characters.pmc.Quests) {
|
||||
const questFromDb = this.questHelper.getQuestFromDb(quest.qid, profileDetails.characters.pmc);
|
||||
|
||||
// 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
|
||||
* @param sessionId Session id of profile to reset
|
||||
*/
|
||||
protected resetAllTradersInProfile(sessionId: string): void
|
||||
{
|
||||
for (const traderId in this.databaseService.getTraders())
|
||||
{
|
||||
protected resetAllTradersInProfile(sessionId: string): void {
|
||||
for (const traderId in this.databaseService.getTraders()) {
|
||||
this.traderHelper.resetTrader(sessionId, traderId);
|
||||
}
|
||||
}
|
||||
@ -345,23 +320,19 @@ export class ProfileController
|
||||
* @param sessionID
|
||||
* @returns IPmcData object
|
||||
*/
|
||||
public generatePlayerScav(sessionID: string): IPmcData
|
||||
{
|
||||
public generatePlayerScav(sessionID: string): IPmcData {
|
||||
return this.playerScavGenerator.generate(sessionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/game/profile/nickname/validate
|
||||
*/
|
||||
public validateNickname(info: IValidateNicknameRequestData, sessionID: string): string
|
||||
{
|
||||
if (info.nickname.length < 3)
|
||||
{
|
||||
public validateNickname(info: IValidateNicknameRequestData, sessionID: string): string {
|
||||
if (info.nickname.length < 3) {
|
||||
return "tooshort";
|
||||
}
|
||||
|
||||
if (this.profileHelper.isNicknameTaken(info, sessionID))
|
||||
{
|
||||
if (this.profileHelper.isNicknameTaken(info, sessionID)) {
|
||||
return "taken";
|
||||
}
|
||||
|
||||
@ -372,12 +343,10 @@ export class ProfileController
|
||||
* Handle client/game/profile/nickname/change event
|
||||
* 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);
|
||||
|
||||
if (output === "OK")
|
||||
{
|
||||
if (output === "OK") {
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
|
||||
pmcData.Info.Nickname = info.nickname;
|
||||
@ -390,8 +359,7 @@ export class ProfileController
|
||||
/**
|
||||
* 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);
|
||||
pmcData.Info.Voice = info.voice;
|
||||
}
|
||||
@ -399,8 +367,7 @@ export class ProfileController
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// return some of the current player info for now
|
||||
@ -421,8 +388,7 @@ export class ProfileController
|
||||
/**
|
||||
* Handle client/profile/status
|
||||
*/
|
||||
public getProfileStatus(sessionId: string): GetProfileStatusResponseData
|
||||
{
|
||||
public getProfileStatus(sessionId: string): GetProfileStatusResponseData {
|
||||
const account = this.saveServer.getProfile(sessionId).info;
|
||||
const response: GetProfileStatusResponseData = {
|
||||
maxPveCountExceeded: false,
|
||||
@ -442,8 +408,7 @@ export class ProfileController
|
||||
return response;
|
||||
}
|
||||
|
||||
public getOtherProfile(sessionId: string, request: IGetOtherProfileRequest): IGetOtherProfileResponse
|
||||
{
|
||||
public getOtherProfile(sessionId: string, request: IGetOtherProfileRequest): IGetOtherProfileResponse {
|
||||
const player = this.profileHelper.getFullProfile(sessionId);
|
||||
const playerPmc = player.characters.pmc;
|
||||
const playerScav = player.characters.scav;
|
||||
@ -493,21 +458,17 @@ export class ProfileController
|
||||
/**
|
||||
* Handle client/profile/settings
|
||||
*/
|
||||
public setChosenProfileIcon(sessionId: string, request: IGetProfileSettingsRequest): boolean
|
||||
{
|
||||
public setChosenProfileIcon(sessionId: string, request: IGetProfileSettingsRequest): boolean {
|
||||
const profileToUpdate = this.profileHelper.getPmcProfile(sessionId);
|
||||
if (!profileToUpdate)
|
||||
{
|
||||
if (!profileToUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.memberCategory !== null)
|
||||
{
|
||||
if (request.memberCategory !== null) {
|
||||
profileToUpdate.Info.SelectedMemberCategory = request.memberCategory;
|
||||
}
|
||||
|
||||
if (request.squadInviteRestriction !== null)
|
||||
{
|
||||
if (request.squadInviteRestriction !== null) {
|
||||
profileToUpdate.Info.SquadInviteRestriction = request.squadInviteRestriction;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { DialogueHelper } from "@spt/helpers/DialogueHelper";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
@ -27,13 +26,13 @@ import { LocaleService } from "@spt/services/LocaleService";
|
||||
import { LocalisationService } from "@spt/services/LocalisationService";
|
||||
import { MailSendService } from "@spt/services/MailSendService";
|
||||
import { PlayerService } from "@spt/services/PlayerService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class QuestController
|
||||
{
|
||||
export class QuestController {
|
||||
protected questConfig: IQuestConfig;
|
||||
|
||||
constructor(
|
||||
@ -54,8 +53,7 @@ export class QuestController
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||
}
|
||||
|
||||
@ -66,44 +64,37 @@ export class QuestController
|
||||
* @param sessionID session id
|
||||
* @returns array of IQuest
|
||||
*/
|
||||
public getClientQuests(sessionID: string): IQuest[]
|
||||
{
|
||||
public getClientQuests(sessionID: string): IQuest[] {
|
||||
const questsToShowPlayer: IQuest[] = [];
|
||||
const allQuests = this.questHelper.getQuestsFromDb();
|
||||
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
|
||||
const questInProfile = profile.Quests.find((x) => x.qid === quest._id);
|
||||
if (questInProfile)
|
||||
{
|
||||
if (questInProfile) {
|
||||
quest.sptStatus = questInProfile.status;
|
||||
questsToShowPlayer.push(quest);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (!this.questHelper.showEventQuestToPlayer(quest._id))
|
||||
{
|
||||
if (!this.questHelper.showEventQuestToPlayer(quest._id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Player can use trader mods then remove them, leaving quests behind
|
||||
const trader = profile.TradersInfo[quest.traderId];
|
||||
if (!trader)
|
||||
{
|
||||
if (!trader) {
|
||||
this.logger.debug(
|
||||
`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
|
||||
if (
|
||||
questRequirements.length === 0
|
||||
&& loyaltyRequirements.length === 0
|
||||
&& standingRequirements.length === 0
|
||||
)
|
||||
{
|
||||
questRequirements.length === 0 &&
|
||||
loyaltyRequirements.length === 0 &&
|
||||
standingRequirements.length === 0
|
||||
) {
|
||||
quest.sptStatus = QuestStatus.AvailableForStart;
|
||||
questsToShowPlayer.push(quest);
|
||||
continue;
|
||||
@ -134,34 +124,29 @@ export class QuestController
|
||||
// Check the status of each quest condition, if any are not completed
|
||||
// then this quest should not be visible
|
||||
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
|
||||
const prerequisiteQuest = profile.Quests.find((profileQuest) =>
|
||||
conditionToFulfil.target.includes(profileQuest.qid),
|
||||
);
|
||||
if (!prerequisiteQuest)
|
||||
{
|
||||
if (!prerequisiteQuest) {
|
||||
haveCompletedPreviousQuest = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Prereq does not have its status requirement fulfilled
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
|
||||
// Has a wait timer
|
||||
if (conditionToFulfil.availableAfter > 0)
|
||||
{
|
||||
if (conditionToFulfil.availableAfter > 0) {
|
||||
// Compare current time to unlock time for previous quest
|
||||
const previousQuestCompleteTime = prerequisiteQuest.statusTimers[prerequisiteQuest.status];
|
||||
const unlockTime = previousQuestCompleteTime + conditionToFulfil.availableAfter;
|
||||
if (unlockTime > this.timeUtil.getTimestamp())
|
||||
{
|
||||
if (unlockTime > this.timeUtil.getTimestamp()) {
|
||||
this.logger.debug(
|
||||
`Quest ${quest.QuestName} is locked for another ${
|
||||
unlockTime - this.timeUtil.getTimestamp()
|
||||
@ -172,33 +157,27 @@ export class QuestController
|
||||
}
|
||||
|
||||
// Previous quest not completed, skip
|
||||
if (!haveCompletedPreviousQuest)
|
||||
{
|
||||
if (!haveCompletedPreviousQuest) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let passesLoyaltyRequirements = true;
|
||||
for (const condition of loyaltyRequirements)
|
||||
{
|
||||
if (!this.questHelper.traderLoyaltyLevelRequirementCheck(condition, profile))
|
||||
{
|
||||
for (const condition of loyaltyRequirements) {
|
||||
if (!this.questHelper.traderLoyaltyLevelRequirementCheck(condition, profile)) {
|
||||
passesLoyaltyRequirements = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let passesStandingRequirements = true;
|
||||
for (const condition of standingRequirements)
|
||||
{
|
||||
if (!this.questHelper.traderStandingRequirementCheck(condition, profile))
|
||||
{
|
||||
for (const condition of standingRequirements) {
|
||||
if (!this.questHelper.traderStandingRequirementCheck(condition, profile)) {
|
||||
passesStandingRequirements = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (haveCompletedPreviousQuest && passesLoyaltyRequirements && passesStandingRequirements)
|
||||
{
|
||||
if (haveCompletedPreviousQuest && passesLoyaltyRequirements && passesStandingRequirements) {
|
||||
quest.sptStatus = QuestStatus.AvailableForStart;
|
||||
questsToShowPlayer.push(quest);
|
||||
}
|
||||
@ -213,21 +192,16 @@ export class QuestController
|
||||
* @param playerLevel level of player to test against quest
|
||||
* @returns true if quest can be seen/accepted by player of defined level
|
||||
*/
|
||||
protected playerLevelFulfillsQuestRequirement(quest: IQuest, playerLevel: number): boolean
|
||||
{
|
||||
if (!quest.conditions)
|
||||
{
|
||||
protected playerLevelFulfillsQuestRequirement(quest: IQuest, playerLevel: number): boolean {
|
||||
if (!quest.conditions) {
|
||||
// No conditions
|
||||
return true;
|
||||
}
|
||||
|
||||
const levelConditions = this.questConditionHelper.getLevelConditions(quest.conditions.AvailableForStart);
|
||||
if (levelConditions.length)
|
||||
{
|
||||
for (const levelCondition of levelConditions)
|
||||
{
|
||||
if (!this.questHelper.doesPlayerLevelFulfilCondition(playerLevel, levelCondition))
|
||||
{
|
||||
if (levelConditions.length) {
|
||||
for (const levelCondition of levelConditions) {
|
||||
if (!this.questHelper.doesPlayerLevelFulfilCondition(playerLevel, levelCondition)) {
|
||||
// Not valid, exit out
|
||||
return false;
|
||||
}
|
||||
@ -252,23 +226,19 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
acceptedQuest: IAcceptQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// Does quest exist in profile
|
||||
// Restarting a failed quest can mean quest exists in profile
|
||||
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid);
|
||||
if (existingQuestStatus)
|
||||
{
|
||||
if (existingQuestStatus) {
|
||||
// Update existing
|
||||
this.questHelper.resetQuestState(pmcData, QuestStatus.Started, acceptedQuest.qid);
|
||||
|
||||
// Need to send client an empty list of completedConditions (Unsure if this does anything)
|
||||
acceptQuestResponse.profileChanges[sessionID].questsStatus.push(existingQuestStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Add new quest to server profile
|
||||
const newQuest = this.questHelper.getQuestReadyForProfile(pmcData, QuestStatus.Started, acceptedQuest);
|
||||
pmcData.Quests.push(newQuest);
|
||||
@ -324,8 +294,7 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
acceptedQuest: IAcceptQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
// Create and store quest status object inside player profile
|
||||
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(
|
||||
pmcData,
|
||||
@ -336,8 +305,7 @@ export class QuestController
|
||||
|
||||
// Look for the generated quest cache in profile.RepeatableQuests
|
||||
const repeatableQuestProfile = this.getRepeatableQuestFromProfile(pmcData, acceptedQuest);
|
||||
if (!repeatableQuestProfile)
|
||||
{
|
||||
if (!repeatableQuestProfile) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"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
|
||||
if (
|
||||
repeatableQuestProfile.side === "Scav"
|
||||
&& ["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)
|
||||
)
|
||||
{
|
||||
repeatableQuestProfile.side === "Scav" &&
|
||||
["PickUp", "Exploration", "Elimination"].includes(repeatableQuestProfile.type)
|
||||
) {
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||
if (!fullProfile.characters.scav.Quests)
|
||||
{
|
||||
if (!fullProfile.characters.scav.Quests) {
|
||||
fullProfile.characters.scav.Quests = [];
|
||||
}
|
||||
|
||||
@ -371,20 +337,21 @@ export class QuestController
|
||||
protected createAcceptedQuestClientResponse(
|
||||
sessionID: string,
|
||||
pmcData: IPmcData,
|
||||
repeatableQuestProfile: IRepeatableQuest): IItemEventRouterResponse
|
||||
{
|
||||
const repeatableSettings = pmcData.RepeatableQuests
|
||||
.find((quest) => quest.name === repeatableQuestProfile.sptRepatableGroupName);
|
||||
repeatableQuestProfile: IRepeatableQuest,
|
||||
): IItemEventRouterResponse {
|
||||
const repeatableSettings = pmcData.RepeatableQuests.find(
|
||||
(quest) => quest.name === repeatableQuestProfile.sptRepatableGroupName,
|
||||
);
|
||||
|
||||
const change = {};
|
||||
change[repeatableQuestProfile._id] = repeatableSettings!.changeRequirement[repeatableQuestProfile._id];
|
||||
|
||||
const repeatableData: IPmcDataRepeatableQuest = {
|
||||
id:
|
||||
repeatableSettings.id
|
||||
?? this.questConfig.repeatableQuests
|
||||
.find((repeatableQuest) => repeatableQuest.name === repeatableQuestProfile.sptRepatableGroupName)
|
||||
.id,
|
||||
repeatableSettings.id ??
|
||||
this.questConfig.repeatableQuests.find(
|
||||
(repeatableQuest) => repeatableQuest.name === repeatableQuestProfile.sptRepatableGroupName,
|
||||
).id,
|
||||
name: repeatableSettings.name,
|
||||
endTime: repeatableSettings.endTime,
|
||||
changeRequirement: change,
|
||||
@ -396,8 +363,7 @@ export class QuestController
|
||||
|
||||
// Nullguard
|
||||
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests)
|
||||
{
|
||||
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests) {
|
||||
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [];
|
||||
}
|
||||
|
||||
@ -416,13 +382,10 @@ export class QuestController
|
||||
protected getRepeatableQuestFromProfile(
|
||||
pmcData: IPmcData,
|
||||
acceptedQuest: IAcceptQuestRequestData,
|
||||
): IRepeatableQuest
|
||||
{
|
||||
for (const repeatableQuest of pmcData.RepeatableQuests)
|
||||
{
|
||||
): IRepeatableQuest {
|
||||
for (const repeatableQuest of pmcData.RepeatableQuests) {
|
||||
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}`);
|
||||
matchingQuest.sptRepatableGroupName = repeatableQuest.name;
|
||||
|
||||
@ -447,8 +410,7 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
body: ICompleteQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
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
|
||||
const questsToFail = this.getQuestsFailedByCompletingQuest(completedQuestId, pmcData);
|
||||
if (questsToFail?.length > 0)
|
||||
{
|
||||
if (questsToFail?.length > 0) {
|
||||
this.failQuests(sessionID, pmcData, questsToFail, completeQuestResponse);
|
||||
}
|
||||
|
||||
@ -487,16 +448,13 @@ export class QuestController
|
||||
completeQuestResponse.profileChanges[sessionID].quests.push(...questDelta);
|
||||
|
||||
// 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(
|
||||
(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
|
||||
if (repeatableQuest.side === "Scav")
|
||||
{
|
||||
if (repeatableQuest.side === "Scav") {
|
||||
this.removeQuestFromScavProfile(sessionID, repeatableQuest._id);
|
||||
}
|
||||
}
|
||||
@ -504,8 +462,7 @@ export class QuestController
|
||||
|
||||
// Hydrate client response questsStatus array with data
|
||||
const questStatusChanges = this.getQuestsWithDifferentStatuses(preCompleteProfileQuests, pmcData.Quests);
|
||||
if (questStatusChanges)
|
||||
{
|
||||
if (questStatusChanges) {
|
||||
completeQuestResponse.profileChanges[sessionID].questsStatus.push(...questStatusChanges);
|
||||
}
|
||||
|
||||
@ -520,14 +477,11 @@ export class QuestController
|
||||
* @param completedQuestId quest completed id
|
||||
* @returns array of IQuest objects
|
||||
*/
|
||||
protected getQuestsFailedByCompletingQuest(completedQuestId: string, pmcProfile: IPmcData): IQuest[]
|
||||
{
|
||||
protected getQuestsFailedByCompletingQuest(completedQuestId: string, pmcProfile: IPmcData): IQuest[] {
|
||||
const questsInDb = this.questHelper.getQuestsFromDb();
|
||||
return questsInDb.filter((quest) =>
|
||||
{
|
||||
return questsInDb.filter((quest) => {
|
||||
// No fail conditions, skip
|
||||
if (!quest.conditions.Fail || quest.conditions.Fail.length === 0)
|
||||
{
|
||||
if (!quest.conditions.Fail || quest.conditions.Fail.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -536,8 +490,7 @@ export class QuestController
|
||||
pmcProfile.Quests.some(
|
||||
(profileQuest) => profileQuest.qid === quest._id && profileQuest.status === QuestStatus.Fail,
|
||||
)
|
||||
)
|
||||
{
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -550,17 +503,16 @@ export class QuestController
|
||||
* @param sessionId Player id
|
||||
* @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 repeatableInScavProfile = fullProfile.characters.scav.Quests?.find((x) => x.qid === questIdToRemove);
|
||||
if (!repeatableInScavProfile)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("quest-unable_to_remove_scav_quest_from_profile",
|
||||
{
|
||||
if (!repeatableInScavProfile) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("quest-unable_to_remove_scav_quest_from_profile", {
|
||||
scavQuestId: questIdToRemove,
|
||||
profileId: sessionId,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -580,22 +532,18 @@ export class QuestController
|
||||
protected getQuestsWithDifferentStatuses(
|
||||
preQuestStatusus: IQuestStatus[],
|
||||
postQuestStatuses: IQuestStatus[],
|
||||
): IQuestStatus[] | undefined
|
||||
{
|
||||
): IQuestStatus[] | undefined {
|
||||
const result: IQuestStatus[] = [];
|
||||
|
||||
for (const quest of postQuestStatuses)
|
||||
{
|
||||
for (const quest of postQuestStatuses) {
|
||||
// Add quest if status differs or quest not found
|
||||
const preQuest = preQuestStatusus.find((x) => x.qid === quest.qid);
|
||||
if (!preQuest || preQuest.status !== quest.status)
|
||||
{
|
||||
if (!preQuest || preQuest.status !== quest.status) {
|
||||
result.push(quest);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length === 0)
|
||||
{
|
||||
if (result.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -614,8 +562,7 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
completedQuestId: string,
|
||||
questRewards: Item[],
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const quest = this.questHelper.getQuestFromDb(completedQuestId, pmcData);
|
||||
|
||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||
@ -634,24 +581,20 @@ export class QuestController
|
||||
* @param quests Quests to look for wait conditions in
|
||||
* @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
|
||||
for (const quest of quests)
|
||||
{
|
||||
for (const quest of quests) {
|
||||
// If quest has prereq of completed quest + availableAfter value > 0 (quest has wait time)
|
||||
const nextQuestWaitCondition = quest.conditions.AvailableForStart.find(
|
||||
(x) => x.target?.includes(completedQuestId) && x.availableAfter > 0,
|
||||
);
|
||||
if (nextQuestWaitCondition)
|
||||
{
|
||||
if (nextQuestWaitCondition) {
|
||||
// Now + wait time
|
||||
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
|
||||
|
||||
// Update quest in profile with status of AvailableAfter
|
||||
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
|
||||
if (existingQuestInProfile)
|
||||
{
|
||||
if (existingQuestInProfile) {
|
||||
existingQuestInProfile.availableAfter = availableAfterTimestamp;
|
||||
existingQuestInProfile.status = QuestStatus.AvailableAfter;
|
||||
existingQuestInProfile.startTime = 0;
|
||||
@ -686,21 +629,16 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
questsToFail: IQuest[],
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
for (const questToFail of questsToFail)
|
||||
{
|
||||
): void {
|
||||
for (const questToFail of questsToFail) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
const isActiveQuestInPlayerProfile = pmcData.Quests.find((quest) => quest.qid === questToFail._id);
|
||||
if (isActiveQuestInPlayerProfile)
|
||||
{
|
||||
if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail)
|
||||
{
|
||||
if (isActiveQuestInPlayerProfile) {
|
||||
if (isActiveQuestInPlayerProfile.status !== QuestStatus.Fail) {
|
||||
const failBody: IFailQuestRequestData = {
|
||||
Action: "QuestFail",
|
||||
qid: questToFail._id,
|
||||
@ -708,9 +646,7 @@ export class QuestController
|
||||
};
|
||||
this.questHelper.failQuest(pmcData, failBody, sessionID, output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Failing an entirely new quest that doesnt exist in profile
|
||||
const statusTimers = {};
|
||||
statusTimers[QuestStatus.Fail] = this.timeUtil.getTimestamp();
|
||||
@ -736,8 +672,7 @@ export class QuestController
|
||||
pmcData: IPmcData,
|
||||
handoverQuestRequest: IHandoverQuestRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const quest = this.questHelper.getQuestFromDb(handoverQuestRequest.qid, pmcData);
|
||||
const handoverQuestTypes = ["HandoverItem", "WeaponAssembly"];
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
@ -747,25 +682,22 @@ export class QuestController
|
||||
|
||||
// Decrement number of items handed in
|
||||
let handoverRequirements: IQuestCondition;
|
||||
for (const condition of quest.conditions.AvailableForFinish)
|
||||
{
|
||||
for (const condition of quest.conditions.AvailableForFinish) {
|
||||
if (
|
||||
condition.id === handoverQuestRequest.conditionId
|
||||
&& handoverQuestTypes.includes(condition.conditionType)
|
||||
)
|
||||
{
|
||||
condition.id === handoverQuestRequest.conditionId &&
|
||||
handoverQuestTypes.includes(condition.conditionType)
|
||||
) {
|
||||
handedInCount = Number.parseInt(<string>condition.value);
|
||||
isItemHandoverQuest = condition.conditionType === handoverQuestTypes[0];
|
||||
handoverRequirements = condition;
|
||||
|
||||
const profileCounter
|
||||
= handoverQuestRequest.conditionId in pmcData.TaskConditionCounters
|
||||
const profileCounter =
|
||||
handoverQuestRequest.conditionId in pmcData.TaskConditionCounters
|
||||
? pmcData.TaskConditionCounters[handoverQuestRequest.conditionId].value
|
||||
: 0;
|
||||
handedInCount -= profileCounter;
|
||||
|
||||
if (handedInCount <= 0)
|
||||
{
|
||||
if (handedInCount <= 0) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText(
|
||||
"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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl)))
|
||||
{
|
||||
if (!(matchingItemInProfile && handoverRequirements.target.includes(matchingItemInProfile._tpl))) {
|
||||
// Item handed in by player doesnt match what was requested
|
||||
return this.showQuestItemHandoverMatchError(
|
||||
handoverQuestRequest,
|
||||
@ -808,8 +737,7 @@ export class QuestController
|
||||
// Remove the right quantity of given items
|
||||
const itemCountToRemove = Math.min(itemHandover.count, handedInCount - totalItemCountToRemove);
|
||||
totalItemCountToRemove += itemCountToRemove;
|
||||
if (itemHandover.count - itemCountToRemove > 0)
|
||||
{
|
||||
if (itemHandover.count - itemCountToRemove > 0) {
|
||||
// Remove single item with no children
|
||||
this.questHelper.changeItemStack(
|
||||
pmcData,
|
||||
@ -818,13 +746,10 @@ export class QuestController
|
||||
sessionID,
|
||||
output,
|
||||
);
|
||||
if (totalItemCountToRemove === handedInCount)
|
||||
{
|
||||
if (totalItemCountToRemove === handedInCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Remove item with children
|
||||
const toRemove = this.itemHelper.findAndReturnChildrenByItems(pmcData.Inventory.items, itemHandover.id);
|
||||
let index = pmcData.Inventory.items.length;
|
||||
@ -833,17 +758,14 @@ export class QuestController
|
||||
output.profileChanges[sessionID].items.del.push({ _id: itemHandover.id });
|
||||
|
||||
// Important: loop backward when removing items from the array we're looping on
|
||||
while (index-- > 0)
|
||||
{
|
||||
if (toRemove.includes(pmcData.Inventory.items[index]._id))
|
||||
{
|
||||
while (index-- > 0) {
|
||||
if (toRemove.includes(pmcData.Inventory.items[index]._id)) {
|
||||
// Remove the item
|
||||
const removedItem = pmcData.Inventory.items.splice(index, 1)[0];
|
||||
|
||||
// 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
|
||||
if (typeof removedItem.location === "number")
|
||||
{
|
||||
if (typeof removedItem.location === "number") {
|
||||
const childItems = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
pmcData.Inventory.items,
|
||||
removedItem.parentId,
|
||||
@ -852,8 +774,7 @@ export class QuestController
|
||||
|
||||
// Sort by the current `location` and update
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -881,8 +802,7 @@ export class QuestController
|
||||
protected showRepeatableQuestInvalidConditionError(
|
||||
handoverQuestRequest: IHandoverQuestRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const errorMessage = this.localisationService.getText("repeatable-quest_handover_failed_condition_invalid", {
|
||||
questId: handoverQuestRequest.qid,
|
||||
conditionId: handoverQuestRequest.conditionId,
|
||||
@ -905,8 +825,7 @@ export class QuestController
|
||||
itemHandedOver: Item,
|
||||
handoverRequirements: IQuestCondition,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const errorMessage = this.localisationService.getText("quest-handover_wrong_item", {
|
||||
questId: handoverQuestRequest.qid,
|
||||
handedInTpl: itemHandedOver?._tpl ?? "UNKNOWN",
|
||||
@ -930,10 +849,8 @@ export class QuestController
|
||||
conditionId: string,
|
||||
questId: string,
|
||||
counterValue: number,
|
||||
): void
|
||||
{
|
||||
if (pmcData.TaskConditionCounters[conditionId] !== undefined)
|
||||
{
|
||||
): void {
|
||||
if (pmcData.TaskConditionCounters[conditionId] !== undefined) {
|
||||
pmcData.TaskConditionCounters[conditionId].value += counterValue;
|
||||
|
||||
return;
|
||||
@ -959,8 +876,7 @@ export class QuestController
|
||||
request: IFailQuestRequestData,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
this.questHelper.failQuest(pmcData, request, sessionID, output);
|
||||
|
||||
return output;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
|
||||
import { HandbookHelper } from "@spt/helpers/HandbookHelper";
|
||||
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
|
||||
@ -43,13 +42,13 @@ import { RagfairRequiredItemsService } from "@spt/services/RagfairRequiredItemsS
|
||||
import { RagfairTaxService } from "@spt/services/RagfairTaxService";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
/**
|
||||
* Handle RagfairCallback events
|
||||
*/
|
||||
@injectable()
|
||||
export class RagfairController
|
||||
{
|
||||
export class RagfairController {
|
||||
protected ragfairConfig: IRagfairConfig;
|
||||
|
||||
constructor(
|
||||
@ -78,8 +77,7 @@ export class RagfairController
|
||||
@inject("RagfairOfferGenerator") protected ragfairOfferGenerator: RagfairOfferGenerator,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
}
|
||||
|
||||
@ -90,8 +88,7 @@ export class RagfairController
|
||||
* @param searchRequest Search request data
|
||||
* @returns IGetOffersResult
|
||||
*/
|
||||
public getOffers(sessionID: string, searchRequest: ISearchRequestData): IGetOffersResult
|
||||
{
|
||||
public getOffers(sessionID: string, searchRequest: ISearchRequestData): IGetOffersResult {
|
||||
const profile = this.profileHelper.getFullProfile(sessionID);
|
||||
|
||||
const itemsToAdd = this.ragfairHelper.filterCategories(sessionID, searchRequest);
|
||||
@ -105,8 +102,7 @@ export class RagfairController
|
||||
result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, profile.characters.pmc);
|
||||
|
||||
// Client requested a category refresh
|
||||
if (searchRequest.updateOfferCount)
|
||||
{
|
||||
if (searchRequest.updateOfferCount) {
|
||||
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
|
||||
for (const offer of result.offers)
|
||||
{
|
||||
if (offer.user.memberType === MemberCategory.TRADER)
|
||||
{
|
||||
for (const offer of result.offers) {
|
||||
if (offer.user.memberType === MemberCategory.TRADER) {
|
||||
// for the items, check the barter schemes. The method getDisplayableAssorts sets a flag sptQuestLocked
|
||||
// to true if the quest is not completed yet
|
||||
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts))
|
||||
{
|
||||
if (this.ragfairOfferHelper.traderOfferItemQuestLocked(offer, traderAssorts)) {
|
||||
offer.locked = true;
|
||||
}
|
||||
|
||||
@ -140,8 +133,7 @@ export class RagfairController
|
||||
result.offersCount = result.offers.length;
|
||||
|
||||
// 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 end = Math.min((searchRequest.page + 1) * searchRequest.limit, result.offers.length);
|
||||
result.offers = result.offers.slice(start, end);
|
||||
@ -156,8 +148,7 @@ export class RagfairController
|
||||
* @param request Request data
|
||||
* @returns IRagfairOffer
|
||||
*/
|
||||
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer
|
||||
{
|
||||
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer {
|
||||
const offers = this.ragfairOfferService.getOffers();
|
||||
const offerToReturn = offers.find((offer) => offer.intId === request.id);
|
||||
|
||||
@ -177,16 +168,13 @@ export class RagfairController
|
||||
itemsToAdd: string[],
|
||||
traderAssorts: Record<string, ITraderAssort>,
|
||||
pmcProfile: IPmcData,
|
||||
): IRagfairOffer[]
|
||||
{
|
||||
): IRagfairOffer[] {
|
||||
// Searching for items in preset menu
|
||||
if (searchRequest.buildCount)
|
||||
{
|
||||
if (searchRequest.buildCount) {
|
||||
return this.ragfairOfferHelper.getOffersForBuild(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
|
||||
}
|
||||
|
||||
if (searchRequest.neededSearchId?.length > 0)
|
||||
{
|
||||
if (searchRequest.neededSearchId?.length > 0) {
|
||||
return this.ragfairOfferHelper.getOffersThatRequireItem(searchRequest, pmcProfile);
|
||||
}
|
||||
|
||||
@ -204,23 +192,17 @@ export class RagfairController
|
||||
pmcProfile: IPmcData,
|
||||
searchRequest: ISearchRequestData,
|
||||
offers: IRagfairOffer[],
|
||||
): Record<string, number>
|
||||
{
|
||||
): Record<string, number> {
|
||||
// Linked/required search categories
|
||||
const playerHasFleaUnlocked
|
||||
= pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel;
|
||||
const playerHasFleaUnlocked =
|
||||
pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel;
|
||||
let offerPool = [];
|
||||
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
|
||||
{
|
||||
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) {
|
||||
offerPool = offers;
|
||||
}
|
||||
else if (!(this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)))
|
||||
{
|
||||
} else if (!(this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))) {
|
||||
// Get all categories
|
||||
offerPool = this.ragfairOfferService.getOffers();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.logger.error(this.localisationService.getText("ragfair-unable_to_get_categories"));
|
||||
this.logger.debug(JSON.stringify(searchRequest));
|
||||
return {};
|
||||
@ -233,12 +215,10 @@ export class RagfairController
|
||||
* Add index to all offers passed in (0-indexed)
|
||||
* @param offers Offers to add index value to
|
||||
*/
|
||||
protected addIndexValueToOffers(offers: IRagfairOffer[]): void
|
||||
{
|
||||
protected addIndexValueToOffers(offers: IRagfairOffer[]): void {
|
||||
let counter = 0;
|
||||
|
||||
for (const offer of offers)
|
||||
{
|
||||
for (const offer of offers) {
|
||||
offer.intId = ++counter;
|
||||
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 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
|
||||
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
|
||||
* @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 traderAssorts = this.traderHelper.getTraderAssortsByTraderId(offer.user.id).items;
|
||||
|
||||
const assortPurchased = traderAssorts.find((x) => x._id === offer.items[0]._id);
|
||||
if (!assortPurchased)
|
||||
{
|
||||
if (!assortPurchased) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("ragfair-unable_to_adjust_stack_count_assort_not_found", {
|
||||
offerId: offer.items[0]._id,
|
||||
@ -296,8 +273,7 @@ export class RagfairController
|
||||
* @param info Search request
|
||||
* @returns True if it is a 'linked' search type
|
||||
*/
|
||||
protected isLinkedSearch(info: ISearchRequestData): boolean
|
||||
{
|
||||
protected isLinkedSearch(info: ISearchRequestData): boolean {
|
||||
return info.linkedSearchId !== "";
|
||||
}
|
||||
|
||||
@ -306,26 +282,22 @@ export class RagfairController
|
||||
* @param info Search request
|
||||
* @returns True if it is a 'required' search type
|
||||
*/
|
||||
protected isRequiredSearch(info: ISearchRequestData): boolean
|
||||
{
|
||||
protected isRequiredSearch(info: ISearchRequestData): boolean {
|
||||
return info.neededSearchId !== "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
for (const sessionID in this.saveServer.getProfiles())
|
||||
{
|
||||
for (const sessionID in this.saveServer.getProfiles()) {
|
||||
// Check profile is capable of creating offers
|
||||
const pmcProfile = profilesDict[sessionID].characters.pmc;
|
||||
if (
|
||||
pmcProfile.RagfairInfo !== undefined
|
||||
&& pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel
|
||||
)
|
||||
{
|
||||
pmcProfile.RagfairInfo !== undefined &&
|
||||
pmcProfile.Info.Level >= this.databaseService.getGlobals().config.RagFair.minUserLevel
|
||||
) {
|
||||
this.ragfairOfferHelper.processOffersOnProfile(sessionID);
|
||||
}
|
||||
}
|
||||
@ -336,26 +308,22 @@ export class RagfairController
|
||||
* @param getPriceRequest
|
||||
* @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
|
||||
const offers = this.ragfairOfferService.getOffersOfType(getPriceRequest.templateId);
|
||||
|
||||
// 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
|
||||
let min = Number.MAX_VALUE;
|
||||
let max = 0;
|
||||
|
||||
// Get the average offer price, excluding barter offers
|
||||
let avgOfferCount = 0;
|
||||
const avg
|
||||
= offers.reduce((sum, offer) =>
|
||||
{
|
||||
const avg =
|
||||
offers.reduce((sum, offer) => {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -366,12 +334,9 @@ export class RagfairController
|
||||
const perItemPrice = offer.requirementsCost / offerItemCount;
|
||||
|
||||
// Handle min/max calculations based on the per-item price
|
||||
if (perItemPrice < min)
|
||||
{
|
||||
if (perItemPrice < min) {
|
||||
min = perItemPrice;
|
||||
}
|
||||
else if (perItemPrice > max)
|
||||
{
|
||||
} else if (perItemPrice > max) {
|
||||
max = perItemPrice;
|
||||
}
|
||||
|
||||
@ -380,8 +345,7 @@ export class RagfairController
|
||||
}, 0) / Math.max(avgOfferCount, 1);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -390,8 +354,7 @@ export class RagfairController
|
||||
|
||||
// No offers listed, get price from live ragfair price list prices.json
|
||||
let tplPrice = this.databaseService.getPrices()[getPriceRequest.templateId];
|
||||
if (!tplPrice)
|
||||
{
|
||||
if (!tplPrice) {
|
||||
// No flea price, get handbook price
|
||||
tplPrice = this.handbookHelper.getTemplatePrice(getPriceRequest.templateId);
|
||||
}
|
||||
@ -410,25 +373,21 @@ export class RagfairController
|
||||
pmcData: IPmcData,
|
||||
offerRequest: IAddOfferRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
const fullProfile = this.saveServer.getProfile(sessionID);
|
||||
|
||||
const validationMessage = "";
|
||||
if (!this.isValidPlayerOfferRequest(offerRequest, validationMessage))
|
||||
{
|
||||
if (!this.isValidPlayerOfferRequest(offerRequest, validationMessage)) {
|
||||
return this.httpResponse.appendErrorToOutput(output, validationMessage);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
switch (typeOfOffer)
|
||||
{
|
||||
switch (typeOfOffer) {
|
||||
case FleaOfferType.SINGLE:
|
||||
return this.createSingleOffer(sessionID, offerRequest, fullProfile, output);
|
||||
case FleaOfferType.MULTI:
|
||||
@ -451,16 +410,15 @@ export class RagfairController
|
||||
sessionID: string,
|
||||
offerRequest: IAddOfferRequestData,
|
||||
fullProfile: ISptProfile,
|
||||
output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
{
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse {
|
||||
const pmcData = fullProfile.characters.pmc;
|
||||
const itemsToListCount = offerRequest.items.length; // Does not count stack size, only items
|
||||
|
||||
// Find items to be listed on flea from player inventory
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError }
|
||||
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError)
|
||||
{
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
|
||||
this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || 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
|
||||
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[rootItem._tpl];
|
||||
if (itemPriceModifer)
|
||||
{
|
||||
if (itemPriceModifer) {
|
||||
averageOfferPriceSingleItem *= itemPriceModifer;
|
||||
}
|
||||
|
||||
@ -502,8 +459,7 @@ export class RagfairController
|
||||
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal);
|
||||
|
||||
// Subtract flea market fee from stash
|
||||
if (this.ragfairConfig.sell.fees)
|
||||
{
|
||||
if (this.ragfairConfig.sell.fees) {
|
||||
const taxFeeChargeFailed = this.chargePlayerTaxFee(
|
||||
sessionID,
|
||||
rootItem,
|
||||
@ -513,8 +469,7 @@ export class RagfairController
|
||||
offerRequest,
|
||||
output,
|
||||
);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
if (taxFeeChargeFailed) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -524,8 +479,7 @@ export class RagfairController
|
||||
output.profileChanges[sessionID].ragFairOffers.push(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);
|
||||
}
|
||||
|
||||
@ -546,8 +500,8 @@ export class RagfairController
|
||||
sessionID: string,
|
||||
offerRequest: IAddOfferRequestData,
|
||||
fullProfile: ISptProfile,
|
||||
output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
{
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse {
|
||||
const pmcData = fullProfile.characters.pmc;
|
||||
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
|
||||
const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
pmcData.Inventory.items,
|
||||
offerRequest.items[0]);
|
||||
offerRequest.items[0],
|
||||
);
|
||||
|
||||
// Find items to be listed on flea (+ children) from player inventory
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError }
|
||||
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError)
|
||||
{
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
|
||||
this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || 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
|
||||
// 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.StackObjectsCount = stackCountTotal;
|
||||
|
||||
// Create flea object
|
||||
const offer = this.createPlayerOffer(
|
||||
sessionID,
|
||||
offerRequest.requirements,
|
||||
firstListingAndChidren,
|
||||
false,
|
||||
);
|
||||
const offer = this.createPlayerOffer(sessionID, offerRequest.requirements, firstListingAndChidren, false);
|
||||
|
||||
// This is the item that will be listed on flea, has merged stackObjectCount
|
||||
const newRootOfferItem = offer.items[0];
|
||||
@ -592,8 +540,7 @@ export class RagfairController
|
||||
|
||||
// Check for and apply item price modifer if it exists in config
|
||||
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl];
|
||||
if (itemPriceModifer)
|
||||
{
|
||||
if (itemPriceModifer) {
|
||||
averageOfferPrice *= itemPriceModifer;
|
||||
}
|
||||
|
||||
@ -617,8 +564,7 @@ export class RagfairController
|
||||
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal);
|
||||
|
||||
// Subtract flea market fee from stash
|
||||
if (this.ragfairConfig.sell.fees)
|
||||
{
|
||||
if (this.ragfairConfig.sell.fees) {
|
||||
const taxFeeChargeFailed = this.chargePlayerTaxFee(
|
||||
sessionID,
|
||||
newRootOfferItem,
|
||||
@ -628,8 +574,7 @@ export class RagfairController
|
||||
offerRequest,
|
||||
output,
|
||||
);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
if (taxFeeChargeFailed) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -639,8 +584,7 @@ export class RagfairController
|
||||
output.profileChanges[sessionID].ragFairOffers.push(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);
|
||||
}
|
||||
|
||||
@ -661,8 +605,8 @@ export class RagfairController
|
||||
sessionID: string,
|
||||
offerRequest: IAddOfferRequestData,
|
||||
fullProfile: ISptProfile,
|
||||
output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
{
|
||||
output: IItemEventRouterResponse,
|
||||
): IItemEventRouterResponse {
|
||||
const pmcData = fullProfile.characters.pmc;
|
||||
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
|
||||
const firstListingAndChidren = this.itemHelper.findAndReturnChildrenAsItems(
|
||||
pmcData.Inventory.items,
|
||||
offerRequest.items[0]);
|
||||
offerRequest.items[0],
|
||||
);
|
||||
|
||||
// Find items to be listed on flea (+ children) from player inventory
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError }
|
||||
= this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || itemsInInventoryError)
|
||||
{
|
||||
const { items: itemsAndChildrenInInventoryToList, errorMessage: itemsInInventoryError } =
|
||||
this.getItemsToListOnFleaFromInventory(pmcData, offerRequest.items);
|
||||
if (!itemsAndChildrenInInventoryToList || 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
|
||||
// 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.StackObjectsCount = stackCountTotal;
|
||||
|
||||
// Create flea object
|
||||
const offer = this.createPlayerOffer(
|
||||
sessionID,
|
||||
offerRequest.requirements,
|
||||
firstListingAndChidren,
|
||||
true,
|
||||
);
|
||||
const offer = this.createPlayerOffer(sessionID, offerRequest.requirements, firstListingAndChidren, true);
|
||||
|
||||
// This is the item that will be listed on flea, has merged stackObjectCount
|
||||
const newRootOfferItem = offer.items[0];
|
||||
@ -707,8 +645,7 @@ export class RagfairController
|
||||
|
||||
// Check for and apply item price modifer if it exists in config
|
||||
const itemPriceModifer = this.ragfairConfig.dynamic.itemPriceMultiplier[newRootOfferItem._tpl];
|
||||
if (itemPriceModifer)
|
||||
{
|
||||
if (itemPriceModifer) {
|
||||
singleItemPrice *= itemPriceModifer;
|
||||
}
|
||||
|
||||
@ -732,8 +669,7 @@ export class RagfairController
|
||||
offer.sellResult = this.ragfairSellHelper.rollForSale(sellChancePercent, stackCountTotal, true);
|
||||
|
||||
// Subtract flea market fee from stash
|
||||
if (this.ragfairConfig.sell.fees)
|
||||
{
|
||||
if (this.ragfairConfig.sell.fees) {
|
||||
const taxFeeChargeFailed = this.chargePlayerTaxFee(
|
||||
sessionID,
|
||||
newRootOfferItem,
|
||||
@ -743,8 +679,7 @@ export class RagfairController
|
||||
offerRequest,
|
||||
output,
|
||||
);
|
||||
if (taxFeeChargeFailed)
|
||||
{
|
||||
if (taxFeeChargeFailed) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -754,8 +689,7 @@ export class RagfairController
|
||||
output.profileChanges[sessionID].ragFairOffers.push(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);
|
||||
}
|
||||
|
||||
@ -768,18 +702,12 @@ export class RagfairController
|
||||
* @param offerRequest Client request
|
||||
* @returns FleaOfferType
|
||||
*/
|
||||
protected getOfferType(offerRequest: IAddOfferRequestData): FleaOfferType
|
||||
{
|
||||
if (offerRequest.items.length == 1 && !offerRequest.sellInOnePiece)
|
||||
{
|
||||
protected getOfferType(offerRequest: IAddOfferRequestData): FleaOfferType {
|
||||
if (offerRequest.items.length == 1 && !offerRequest.sellInOnePiece) {
|
||||
return FleaOfferType.SINGLE;
|
||||
}
|
||||
else if (offerRequest.items.length > 1 && !offerRequest.sellInOnePiece)
|
||||
{
|
||||
} else if (offerRequest.items.length > 1 && !offerRequest.sellInOnePiece) {
|
||||
return FleaOfferType.MULTI;
|
||||
}
|
||||
else if (offerRequest.sellInOnePiece)
|
||||
{
|
||||
} else if (offerRequest.sellInOnePiece) {
|
||||
return FleaOfferType.PACK;
|
||||
}
|
||||
|
||||
@ -805,19 +733,18 @@ export class RagfairController
|
||||
itemStackCount: number,
|
||||
offerRequest: IAddOfferRequestData,
|
||||
output: IItemEventRouterResponse,
|
||||
): boolean
|
||||
{
|
||||
): boolean {
|
||||
// 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 tax = storedClientTaxValue
|
||||
? storedClientTaxValue.fee
|
||||
: this.ragfairTaxService.calculateTax(
|
||||
rootItem,
|
||||
pmcData,
|
||||
requirementsPriceInRub,
|
||||
itemStackCount,
|
||||
offerRequest.sellInOnePiece,
|
||||
);
|
||||
rootItem,
|
||||
pmcData,
|
||||
requirementsPriceInRub,
|
||||
itemStackCount,
|
||||
offerRequest.sellInOnePiece,
|
||||
);
|
||||
|
||||
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);
|
||||
this.paymentService.payMoney(pmcData, buyTradeRequest, sessionID, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
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
|
||||
* @returns Is offer valid
|
||||
*/
|
||||
protected isValidPlayerOfferRequest(offerRequest: IAddOfferRequestData, errorMessage: string): boolean
|
||||
{
|
||||
if (!offerRequest?.items || offerRequest.items.length === 0)
|
||||
{
|
||||
protected isValidPlayerOfferRequest(offerRequest: IAddOfferRequestData, errorMessage: string): boolean {
|
||||
if (!offerRequest?.items || offerRequest.items.length === 0) {
|
||||
this.logger.error(this.localisationService.getText("ragfair-invalid_player_offer_request"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!offerRequest.requirements)
|
||||
{
|
||||
if (!offerRequest.requirements) {
|
||||
this.logger.error(this.localisationService.getText("ragfair-unable_to_place_offer_with_no_requirements"));
|
||||
|
||||
return false;
|
||||
@ -868,21 +791,16 @@ export class RagfairController
|
||||
* @param requirements
|
||||
* @returns Rouble price
|
||||
*/
|
||||
protected calculateRequirementsPriceInRub(requirements: Requirement[]): number
|
||||
{
|
||||
protected calculateRequirementsPriceInRub(requirements: Requirement[]): number {
|
||||
let requirementsPriceInRub = 0;
|
||||
for (const item of requirements)
|
||||
{
|
||||
for (const item of requirements) {
|
||||
const requestedItemTpl = item._tpl;
|
||||
|
||||
if (this.paymentHelper.isMoneyTpl(requestedItemTpl))
|
||||
{
|
||||
if (this.paymentHelper.isMoneyTpl(requestedItemTpl)) {
|
||||
requirementsPriceInRub += this.handbookHelper.inRUB(item.count, requestedItemTpl);
|
||||
}
|
||||
else
|
||||
{
|
||||
requirementsPriceInRub
|
||||
+= this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
|
||||
} else {
|
||||
requirementsPriceInRub +=
|
||||
this.ragfairPriceService.getDynamicPriceForItem(requestedItemTpl) * item.count;
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,17 +816,14 @@ export class RagfairController
|
||||
protected getItemsToListOnFleaFromInventory(
|
||||
pmcData: IPmcData,
|
||||
itemIdsFromFleaOfferRequest: string[],
|
||||
): { items: Item[][] | undefined, errorMessage: string | undefined }
|
||||
{
|
||||
): { items: Item[][] | undefined; errorMessage: string | undefined } {
|
||||
const itemsToReturn: Item[][] = [];
|
||||
let errorMessage: string | undefined = undefined;
|
||||
|
||||
// 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);
|
||||
if (!item)
|
||||
{
|
||||
if (!item) {
|
||||
errorMessage = this.localisationService.getText("ragfair-unable_to_find_item_in_inventory", {
|
||||
id: itemId,
|
||||
});
|
||||
@ -921,8 +836,7 @@ export class RagfairController
|
||||
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");
|
||||
this.logger.error(errorMessage);
|
||||
|
||||
@ -937,11 +851,9 @@ export class RagfairController
|
||||
requirements: Requirement[],
|
||||
items: Item[],
|
||||
sellInOnePiece: boolean,
|
||||
): IRagfairOffer
|
||||
{
|
||||
): IRagfairOffer {
|
||||
const loyalLevel = 1;
|
||||
const formattedItems: Item[] = items.map((item) =>
|
||||
{
|
||||
const formattedItems: Item[] = items.map((item) => {
|
||||
const isChild = items.some((subItem) => subItem._id === item.parentId);
|
||||
|
||||
return {
|
||||
@ -953,12 +865,12 @@ export class RagfairController
|
||||
};
|
||||
});
|
||||
|
||||
const formattedRequirements: IBarterScheme[] = requirements.map((item) =>
|
||||
{
|
||||
const formattedRequirements: IBarterScheme[] = requirements.map((item) => {
|
||||
return {
|
||||
_tpl: item._tpl,
|
||||
count: item.count,
|
||||
onlyFunctional: item.onlyFunctional };
|
||||
onlyFunctional: item.onlyFunctional,
|
||||
};
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public getStaticPrices(): Record<string, number>
|
||||
{
|
||||
public getStaticPrices(): Record<string, number> {
|
||||
return this.ragfairPriceService.getAllStaticPrices();
|
||||
}
|
||||
|
||||
@ -988,14 +898,12 @@ export class RagfairController
|
||||
* @param sessionId Players id
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public removeOffer(removeRequest: IRemoveOfferRequestData, sessionId: string): IItemEventRouterResponse
|
||||
{
|
||||
public removeOffer(removeRequest: IRemoveOfferRequestData, sessionId: string): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
const pmcData = this.saveServer.getProfile(sessionId).characters.pmc;
|
||||
const playerProfileOffers = pmcData.RagfairInfo.offers;
|
||||
if (!playerProfileOffers)
|
||||
{
|
||||
if (!playerProfileOffers) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("ragfair-unable_to_remove_offer_not_found_in_profile", {
|
||||
profileId: sessionId,
|
||||
@ -1007,8 +915,7 @@ export class RagfairController
|
||||
}
|
||||
|
||||
const playerOfferIndex = playerProfileOffers.findIndex((offer) => offer._id === removeRequest.offerId);
|
||||
if (playerOfferIndex === -1)
|
||||
{
|
||||
if (playerOfferIndex === -1) {
|
||||
this.logger.error(
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
|
||||
offerId: removeRequest.offerId,
|
||||
@ -1021,8 +928,7 @@ export class RagfairController
|
||||
}
|
||||
|
||||
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
|
||||
const newEndTime = this.ragfairConfig.sell.expireSeconds + this.timeUtil.getTimestamp();
|
||||
playerProfileOffers[playerOfferIndex].endTime = Math.round(newEndTime);
|
||||
@ -1037,8 +943,7 @@ export class RagfairController
|
||||
* @param sessionId Players id
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public extendOffer(extendRequest: IExtendOfferRequestData, sessionId: string): IItemEventRouterResponse
|
||||
{
|
||||
public extendOffer(extendRequest: IExtendOfferRequestData, sessionId: string): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
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 secondsToAdd = extendRequest.renewalTime * TimeUtil.ONE_HOUR_AS_SECONDS;
|
||||
|
||||
if (playerOfferIndex === -1)
|
||||
{
|
||||
if (playerOfferIndex === -1) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
|
||||
offerId: extendRequest.offerId,
|
||||
@ -1060,14 +964,12 @@ export class RagfairController
|
||||
}
|
||||
|
||||
// MOD: Pay flea market fee
|
||||
if (this.ragfairConfig.sell.fees)
|
||||
{
|
||||
if (this.ragfairConfig.sell.fees) {
|
||||
const count = playerOffers[playerOfferIndex].sellInOnePiece
|
||||
? 1
|
||||
: playerOffers[playerOfferIndex].items.reduce((sum, item) =>
|
||||
{
|
||||
return sum + item.upd.StackObjectsCount;
|
||||
}, 0);
|
||||
: playerOffers[playerOfferIndex].items.reduce((sum, item) => {
|
||||
return sum + item.upd.StackObjectsCount;
|
||||
}, 0);
|
||||
|
||||
const tax = this.ragfairTaxService.calculateTax(
|
||||
playerOffers[playerOfferIndex].items[0],
|
||||
@ -1079,8 +981,7 @@ export class RagfairController
|
||||
|
||||
const request = this.createBuyTradeRequestObject("RUB", tax);
|
||||
this.paymentService.payMoney(pmcData, request, sessionId, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("ragfair-unable_to_pay_commission_fee"),
|
||||
@ -1100,8 +1001,7 @@ export class RagfairController
|
||||
* @param value Amount of currency
|
||||
* @returns IProcessBuyTradeRequestData
|
||||
*/
|
||||
protected createBuyTradeRequestObject(currency: string, value: number): IProcessBuyTradeRequestData
|
||||
{
|
||||
protected createBuyTradeRequestObject(currency: string, value: number): IProcessBuyTradeRequestData {
|
||||
return {
|
||||
tid: "ragfair",
|
||||
Action: "TradingConfirm",
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { QuestHelper } from "@spt/helpers/QuestHelper";
|
||||
import { RepairHelper } from "@spt/helpers/RepairHelper";
|
||||
@ -13,10 +12,10 @@ import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { PaymentService } from "@spt/services/PaymentService";
|
||||
import { RepairService } from "@spt/services/RepairService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class RepairController
|
||||
{
|
||||
export class RepairController {
|
||||
protected repairConfig: IRepairConfig;
|
||||
|
||||
constructor(
|
||||
@ -29,8 +28,7 @@ export class RepairController
|
||||
@inject("RepairHelper") protected repairHelper: RepairHelper,
|
||||
@inject("RepairService") protected repairService: RepairService,
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle TraderRepair event
|
||||
@ -44,13 +42,11 @@ export class RepairController
|
||||
sessionID: string,
|
||||
body: ITraderRepairActionDataRequest,
|
||||
pmcData: IPmcData,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// 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);
|
||||
|
||||
this.repairService.payForRepair(
|
||||
@ -62,8 +58,7 @@ export class RepairController
|
||||
output,
|
||||
);
|
||||
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -89,8 +84,7 @@ export class RepairController
|
||||
sessionID: string,
|
||||
body: IRepairActionDataRequest,
|
||||
pmcData: IPmcData,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// repair item
|
||||
|
@ -1,13 +1,9 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { QuestHelper } from "@spt/helpers/QuestHelper";
|
||||
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import {
|
||||
IPmcDataRepeatableQuest,
|
||||
IRepeatableQuest,
|
||||
} from "@spt/models/eft/common/tables/IRepeatableQuests";
|
||||
import { IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt/models/eft/common/tables/IRepeatableQuests";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
|
||||
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 { PaymentService } from "@spt/services/PaymentService";
|
||||
import { ProfileFixerService } from "@spt/services/ProfileFixerService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { ObjectId } from "@spt/utils/ObjectId";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class RepeatableQuestController
|
||||
{
|
||||
export class RepeatableQuestController {
|
||||
protected questConfig: IQuestConfig;
|
||||
|
||||
constructor(
|
||||
@ -54,8 +50,7 @@ export class RepeatableQuestController
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
|
||||
}
|
||||
|
||||
@ -84,30 +79,26 @@ export class RepeatableQuestController
|
||||
*
|
||||
* @returns {array} Array of "repeatableQuestObjects" as described above
|
||||
*/
|
||||
public getClientRepeatableQuests(sessionID: string): IPmcDataRepeatableQuest[]
|
||||
{
|
||||
public getClientRepeatableQuests(sessionID: string): IPmcDataRepeatableQuest[] {
|
||||
const returnData: Array<IPmcDataRepeatableQuest> = [];
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionID)!;
|
||||
const pmcData = fullProfile.characters.pmc;
|
||||
const currentTime = this.timeUtil.getTimestamp();
|
||||
|
||||
// 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
|
||||
const generatedRepeatables = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
|
||||
const repeatableTypeLower = repeatableConfig.name.toLowerCase();
|
||||
|
||||
const canAccessRepeatables = this.canProfileAccessRepeatableQuests(repeatableConfig, pmcData);
|
||||
if (!canAccessRepeatables)
|
||||
{
|
||||
if (!canAccessRepeatables) {
|
||||
// Dont send any repeatables, even existing ones
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
// 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 lifeline = 0;
|
||||
while (!quest && questTypePool.types.length > 0)
|
||||
{
|
||||
while (!quest && questTypePool.types.length > 0) {
|
||||
quest = this.repeatableQuestGenerator.generateRepeatableQuest(
|
||||
pmcData.Info.Level,
|
||||
pmcData.TradersInfo,
|
||||
@ -145,8 +134,7 @@ export class RepeatableQuestController
|
||||
repeatableConfig,
|
||||
);
|
||||
lifeline++;
|
||||
if (lifeline > 10)
|
||||
{
|
||||
if (lifeline > 10) {
|
||||
this.logger.debug(
|
||||
"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
|
||||
if (questTypePool.types.length === 0)
|
||||
{
|
||||
if (questTypePool.types.length === 0) {
|
||||
break;
|
||||
}
|
||||
quest.side = repeatableConfig.side;
|
||||
@ -170,8 +157,7 @@ export class RepeatableQuestController
|
||||
fullProfile.spt.freeRepeatableRefreshUsedCount[repeatableTypeLower] = 0;
|
||||
|
||||
// Create stupid redundant change requirements from quest data
|
||||
for (const quest of generatedRepeatables.activeQuests)
|
||||
{
|
||||
for (const quest of generatedRepeatables.activeQuests) {
|
||||
generatedRepeatables.changeRequirement[quest._id] = {
|
||||
changeCost: quest.changeCost,
|
||||
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 pmcData Player profile
|
||||
*/
|
||||
protected processExpiredQuests(generatedRepeatables: IPmcDataRepeatableQuest, pmcData: IPmcData): void
|
||||
{
|
||||
protected processExpiredQuests(generatedRepeatables: IPmcDataRepeatableQuest, pmcData: IPmcData): void {
|
||||
const questsToKeep = [];
|
||||
for (const activeQuest of generatedRepeatables.activeQuests)
|
||||
{
|
||||
for (const activeQuest of generatedRepeatables.activeQuests) {
|
||||
const questStatusInProfile = pmcData.Quests.find((quest) => quest.qid === activeQuest._id);
|
||||
if (!questStatusInProfile)
|
||||
{
|
||||
if (!questStatusInProfile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep finished quests in list so player can hand in
|
||||
if (questStatusInProfile.status === QuestStatus.AvailableForFinish)
|
||||
{
|
||||
if (questStatusInProfile.status === QuestStatus.AvailableForFinish) {
|
||||
questsToKeep.push(activeQuest);
|
||||
this.logger.debug(
|
||||
`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
|
||||
* @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
|
||||
if (repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
|
||||
{
|
||||
if (repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
return false;
|
||||
@ -267,11 +246,10 @@ export class RepeatableQuestController
|
||||
* @param pmcData Player profile to check
|
||||
* @returns True if unlocked
|
||||
*/
|
||||
protected playerHasDailyScavQuestsUnlocked(pmcData: IPmcData): boolean
|
||||
{
|
||||
return pmcData?.Hideout?.Areas
|
||||
?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER)
|
||||
?.level >= 1;
|
||||
protected playerHasDailyScavQuestsUnlocked(pmcData: IPmcData): boolean {
|
||||
return (
|
||||
pmcData?.Hideout?.Areas?.find((hideoutArea) => hideoutArea.type === HideoutAreas.INTEL_CENTER)?.level >= 1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,8 +258,7 @@ export class RepeatableQuestController
|
||||
* @param repeatableConfig Config of daily type to check
|
||||
* @returns True if unlocked
|
||||
*/
|
||||
protected playerHasDailyPmcQuestsUnlocked(pmcData: IPmcData, repeatableConfig: IRepeatableQuestConfig): boolean
|
||||
{
|
||||
protected playerHasDailyPmcQuestsUnlocked(pmcData: IPmcData, repeatableConfig: IRepeatableQuestConfig): boolean {
|
||||
return pmcData.Info.Level >= repeatableConfig.minPlayerLevel;
|
||||
}
|
||||
|
||||
@ -291,17 +268,15 @@ export class RepeatableQuestController
|
||||
* @param pmcData Player profile
|
||||
* @returns Quest count
|
||||
*/
|
||||
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number
|
||||
{
|
||||
protected getQuestCount(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): number {
|
||||
if (
|
||||
repeatableConfig.name.toLowerCase() === "daily"
|
||||
&& this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)
|
||||
)
|
||||
{
|
||||
repeatableConfig.name.toLowerCase() === "daily" &&
|
||||
this.profileHelper.hasEliteSkillLevel(SkillTypes.CHARISMA, pmcData)
|
||||
) {
|
||||
// Elite charisma skill gives extra daily quest(s)
|
||||
return (
|
||||
repeatableConfig.numQuests
|
||||
+ this.databaseService.getGlobals().config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
|
||||
repeatableConfig.numQuests +
|
||||
this.databaseService.getGlobals().config.SkillsSettings.Charisma.BonusSettings.EliteBonusSettings
|
||||
.RepeatableQuestExtraCount
|
||||
);
|
||||
}
|
||||
@ -318,13 +293,13 @@ export class RepeatableQuestController
|
||||
protected getRepeatableQuestSubTypeFromProfile(
|
||||
repeatableConfig: IRepeatableQuestConfig,
|
||||
pmcData: IPmcData,
|
||||
): IPmcDataRepeatableQuest
|
||||
{
|
||||
): IPmcDataRepeatableQuest {
|
||||
// Get from profile, add if missing
|
||||
let repeatableQuestDetails = pmcData.RepeatableQuests
|
||||
.find((repeatable) => repeatable.name === repeatableConfig.name);
|
||||
if (!repeatableQuestDetails) // Not in profile, generate
|
||||
{
|
||||
let repeatableQuestDetails = pmcData.RepeatableQuests.find(
|
||||
(repeatable) => repeatable.name === repeatableConfig.name,
|
||||
);
|
||||
if (!repeatableQuestDetails) {
|
||||
// Not in profile, generate
|
||||
const hasAccess = this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(pmcData);
|
||||
repeatableQuestDetails = {
|
||||
id: repeatableConfig.id,
|
||||
@ -347,13 +322,11 @@ export class RepeatableQuestController
|
||||
/**
|
||||
* 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 numberOfQuests = number;
|
||||
|
||||
if (factory)
|
||||
{
|
||||
if (factory) {
|
||||
// First is factory extract always add for debugging
|
||||
randomQuests.push(dailiesPool[0]);
|
||||
numberOfQuests -= 1;
|
||||
@ -361,14 +334,11 @@ export class RepeatableQuestController
|
||||
|
||||
randomQuests = randomQuests.concat(this.randomUtil.drawRandomFromList(dailiesPool, numberOfQuests, false));
|
||||
|
||||
for (const element of randomQuests)
|
||||
{
|
||||
for (const element of randomQuests) {
|
||||
element._id = this.objectId.generate();
|
||||
const conditions = element.conditions.AvailableForFinish;
|
||||
for (const condition of conditions)
|
||||
{
|
||||
if ("counter" in condition._props)
|
||||
{
|
||||
for (const condition of conditions) {
|
||||
if ("counter" in condition._props) {
|
||||
condition._props.counter.id = this.objectId.generate();
|
||||
}
|
||||
}
|
||||
@ -384,18 +354,15 @@ export class RepeatableQuestController
|
||||
* @param pmcLevel level of pmc generating quest pool
|
||||
* @returns IQuestTypePool
|
||||
*/
|
||||
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool
|
||||
{
|
||||
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool {
|
||||
const questPool = this.createBaseQuestPool(repeatableConfig);
|
||||
|
||||
// Get the allowed locations based on the PMC's level
|
||||
const locations = this.getAllowedLocationsForPmcLevel(repeatableConfig.locations, pmcLevel);
|
||||
|
||||
// Populate Exploration and Pickup quest locations
|
||||
for (const location in locations)
|
||||
{
|
||||
if (location !== ELocationName.ANY)
|
||||
{
|
||||
for (const location in locations) {
|
||||
if (location !== ELocationName.ANY) {
|
||||
questPool.pool.Exploration.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);
|
||||
|
||||
// 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
|
||||
if (target.isBoss)
|
||||
{
|
||||
if (target.isBoss) {
|
||||
questPool.pool.Elimination.targets[targetKey] = { locations: ["any"] };
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Non-boss targets
|
||||
const possibleLocations = Object.keys(locations);
|
||||
|
||||
const allowedLocations = (targetKey === "Savage")
|
||||
? possibleLocations.filter((location) => location !== "laboratory") // Exclude labs for Savage targets.
|
||||
: possibleLocations;
|
||||
const allowedLocations =
|
||||
targetKey === "Savage"
|
||||
? possibleLocations.filter((location) => location !== "laboratory") // Exclude labs for Savage targets.
|
||||
: possibleLocations;
|
||||
|
||||
questPool.pool.Elimination.targets[targetKey] = { locations: allowedLocations };
|
||||
}
|
||||
@ -431,8 +395,7 @@ export class RepeatableQuestController
|
||||
return questPool;
|
||||
}
|
||||
|
||||
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool
|
||||
{
|
||||
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool {
|
||||
return {
|
||||
types: repeatableConfig.types.slice(),
|
||||
pool: { Exploration: { locations: {} }, Elimination: { targets: {} }, Pickup: { locations: {} } },
|
||||
@ -448,23 +411,18 @@ export class RepeatableQuestController
|
||||
protected getAllowedLocationsForPmcLevel(
|
||||
locations: Record<ELocationName, string[]>,
|
||||
pmcLevel: number,
|
||||
): Partial<Record<ELocationName, string[]>>
|
||||
{
|
||||
): Partial<Record<ELocationName, string[]>> {
|
||||
const allowedLocation: Partial<Record<ELocationName, string[]>> = {};
|
||||
|
||||
for (const location in locations)
|
||||
{
|
||||
for (const location in locations) {
|
||||
const locationNames = [];
|
||||
for (const locationName of locations[location])
|
||||
{
|
||||
if (this.isPmcLevelAllowedOnLocation(locationName, pmcLevel))
|
||||
{
|
||||
for (const locationName of locations[location]) {
|
||||
if (this.isPmcLevelAllowedOnLocation(locationName, pmcLevel)) {
|
||||
locationNames.push(locationName);
|
||||
}
|
||||
}
|
||||
|
||||
if (locationNames.length > 0)
|
||||
{
|
||||
if (locationNames.length > 0) {
|
||||
allowedLocation[location] = locationNames;
|
||||
}
|
||||
}
|
||||
@ -478,36 +436,29 @@ export class RepeatableQuestController
|
||||
* @param pmcLevel The level of the pmc
|
||||
* @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
|
||||
if (location === ELocationName.ANY)
|
||||
{
|
||||
if (location === ELocationName.ANY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const locationBase = this.databaseService.getLocation(location.toLowerCase())?.base;
|
||||
if (!locationBase)
|
||||
{
|
||||
if (!locationBase) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pmcLevel <= locationBase.RequiredPlayerLevelMax && pmcLevel >= locationBase.RequiredPlayerLevelMin;
|
||||
}
|
||||
|
||||
public debugLogRepeatableQuestIds(pmcData: IPmcData): void
|
||||
{
|
||||
for (const repeatable of pmcData.RepeatableQuests)
|
||||
{
|
||||
public debugLogRepeatableQuestIds(pmcData: IPmcData): void {
|
||||
for (const repeatable of pmcData.RepeatableQuests) {
|
||||
const activeQuestsIds = [];
|
||||
const inactiveQuestsIds = [];
|
||||
for (const active of repeatable.activeQuests)
|
||||
{
|
||||
for (const active of repeatable.activeQuests) {
|
||||
activeQuestsIds.push(active._id);
|
||||
}
|
||||
|
||||
for (const inactive of repeatable.inactiveQuests)
|
||||
{
|
||||
for (const inactive of repeatable.inactiveQuests) {
|
||||
inactiveQuestsIds.push(inactive._id);
|
||||
}
|
||||
|
||||
@ -529,15 +480,16 @@ export class RepeatableQuestController
|
||||
pmcData: IPmcData,
|
||||
changeRequest: IRepeatableQuestChangeRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||
|
||||
// Check for existing quest in (daily/weekly/scav arrays)
|
||||
const { quest: questToReplace, repeatableType: repeatablesInProfile }
|
||||
= this.getRepeatableById(changeRequest.qid, pmcData);
|
||||
const { quest: questToReplace, repeatableType: repeatablesInProfile } = this.getRepeatableById(
|
||||
changeRequest.qid,
|
||||
pmcData,
|
||||
);
|
||||
|
||||
// Subtype name of quest - daily/weekly/scav
|
||||
const repeatableTypeLower = repeatablesInProfile.name.toLowerCase();
|
||||
@ -546,8 +498,9 @@ export class RepeatableQuestController
|
||||
const replacedQuestTraderId = questToReplace.traderId;
|
||||
|
||||
// Update active quests to exclude the quest we're replacing
|
||||
repeatablesInProfile.activeQuests = repeatablesInProfile.activeQuests
|
||||
.filter((quest) => quest._id !== changeRequest.qid);
|
||||
repeatablesInProfile.activeQuests = repeatablesInProfile.activeQuests.filter(
|
||||
(quest) => quest._id !== changeRequest.qid,
|
||||
);
|
||||
|
||||
// Save for later cost calculation
|
||||
const previousChangeRequirement = this.cloner.clone(repeatablesInProfile.changeRequirement[changeRequest.qid]);
|
||||
@ -556,15 +509,14 @@ export class RepeatableQuestController
|
||||
delete repeatablesInProfile.changeRequirement[changeRequest.qid];
|
||||
|
||||
// Get config for this repeatable sub-type (daily/weekly/scav)
|
||||
const repeatableConfig = this.questConfig.repeatableQuests
|
||||
.find((config) => config.name === repeatablesInProfile.name,
|
||||
);
|
||||
const repeatableConfig = this.questConfig.repeatableQuests.find(
|
||||
(config) => config.name === repeatablesInProfile.name,
|
||||
);
|
||||
|
||||
// Generate meta-data for what type/levelrange of quests can be generated for player
|
||||
const allowedQuestTypes = this.generateQuestPool(repeatableConfig, pmcData.Info.Level);
|
||||
const newRepeatableQuest = this.attemptToGenerateRepeatableQuest(pmcData, allowedQuestTypes, repeatableConfig);
|
||||
if (!newRepeatableQuest)
|
||||
{
|
||||
if (!newRepeatableQuest) {
|
||||
// Unable to find quest being replaced
|
||||
const message = `Unable to generate repeatable quest of type: ${repeatableTypeLower} to replace trader: ${replacedQuestTraderId} quest ${changeRequest.qid}`;
|
||||
this.logger.error(message);
|
||||
@ -593,20 +545,17 @@ export class RepeatableQuestController
|
||||
|
||||
// Check if we should charge player for replacing quest
|
||||
const isFreeToReplace = this.useFreeRefreshIfAvailable(fullProfile, repeatablesInProfile, repeatableTypeLower);
|
||||
if (!isFreeToReplace)
|
||||
{
|
||||
if (!isFreeToReplace) {
|
||||
// Reduce standing with trader for not doing their quest
|
||||
const traderOfReplacedQuest = pmcData.TradersInfo[replacedQuestTraderId];
|
||||
traderOfReplacedQuest.standing -= previousChangeRequirement.changeStandingCost;
|
||||
|
||||
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
|
||||
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);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -618,8 +567,7 @@ export class RepeatableQuestController
|
||||
// Purge inactive repeatables
|
||||
repeatableToChangeClone.inactiveQuests = [];
|
||||
|
||||
if (!repeatableToChangeClone)
|
||||
{
|
||||
if (!repeatableToChangeClone) {
|
||||
// Unable to find quest being replaced
|
||||
const message = this.localisationService.getText("quest-unable_to_find_repeatable_to_replace");
|
||||
this.logger.error(message);
|
||||
@ -642,15 +590,11 @@ export class RepeatableQuestController
|
||||
* @param pmcData Profile that contains quests to look through
|
||||
* @returns IGetRepeatableByIdResult
|
||||
*/
|
||||
protected getRepeatableById(questId: string, pmcData: IPmcData): IGetRepeatableByIdResult
|
||||
{
|
||||
for (const repeatablesInProfile of pmcData.RepeatableQuests)
|
||||
{
|
||||
protected getRepeatableById(questId: string, pmcData: IPmcData): IGetRepeatableByIdResult {
|
||||
for (const repeatablesInProfile of pmcData.RepeatableQuests) {
|
||||
// Check for existing quest in (daily/weekly/scav arrays)
|
||||
const questToReplace = repeatablesInProfile.activeQuests
|
||||
.find((repeatable) => repeatable._id === questId);
|
||||
if (!questToReplace)
|
||||
{
|
||||
const questToReplace = repeatablesInProfile.activeQuests.find((repeatable) => repeatable._id === questId);
|
||||
if (!questToReplace) {
|
||||
// Not found, skip to next repeatable sub-type
|
||||
continue;
|
||||
}
|
||||
@ -665,14 +609,11 @@ export class RepeatableQuestController
|
||||
pmcData: IPmcData,
|
||||
questTypePool: IQuestTypePool,
|
||||
repeatableConfig: IRepeatableQuestConfig,
|
||||
): IRepeatableQuest
|
||||
{
|
||||
): IRepeatableQuest {
|
||||
const maxAttempts = 10;
|
||||
let newRepeatableQuest: IRepeatableQuest = undefined;
|
||||
let attempts = 0;
|
||||
while (attempts < maxAttempts
|
||||
&& questTypePool.types.length > 0)
|
||||
{
|
||||
while (attempts < maxAttempts && questTypePool.types.length > 0) {
|
||||
newRepeatableQuest = this.repeatableQuestGenerator.generateRepeatableQuest(
|
||||
pmcData.Info.Level,
|
||||
pmcData.TradersInfo,
|
||||
@ -680,8 +621,7 @@ export class RepeatableQuestController
|
||||
repeatableConfig,
|
||||
);
|
||||
|
||||
if (newRepeatableQuest)
|
||||
{
|
||||
if (newRepeatableQuest) {
|
||||
// Successfully generated a quest, exit loop
|
||||
break;
|
||||
}
|
||||
@ -689,11 +629,8 @@ export class RepeatableQuestController
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (attempts > maxAttempts)
|
||||
{
|
||||
this.logger.debug(
|
||||
"We were stuck in repeatable quest generation. This should never happen. Please report",
|
||||
);
|
||||
if (attempts > maxAttempts) {
|
||||
this.logger.debug("We were stuck in repeatable quest generation. This should never happen. Please report");
|
||||
}
|
||||
|
||||
return newRepeatableQuest;
|
||||
@ -710,11 +647,10 @@ export class RepeatableQuestController
|
||||
protected useFreeRefreshIfAvailable(
|
||||
fullProfile: ISptProfile,
|
||||
repeatableSubType: IPmcDataRepeatableQuest,
|
||||
repeatableTypeName: string): boolean
|
||||
{
|
||||
repeatableTypeName: string,
|
||||
): boolean {
|
||||
// No free refreshes, exit early
|
||||
if (repeatableSubType.freeChangesAvailable <= 0)
|
||||
{
|
||||
if (repeatableSubType.freeChangesAvailable <= 0) {
|
||||
// Reset counter to 0
|
||||
repeatableSubType.freeChangesAvailable = 0;
|
||||
|
||||
@ -722,12 +658,12 @@ export class RepeatableQuestController
|
||||
}
|
||||
|
||||
// Only certain game versions have access to free refreshes
|
||||
const hasAccessToFreeRefreshSystem
|
||||
= this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(fullProfile.characters.pmc);
|
||||
const hasAccessToFreeRefreshSystem = this.profileHelper.hasAccessToRepeatableFreeRefreshSystem(
|
||||
fullProfile.characters.pmc,
|
||||
);
|
||||
|
||||
// If the player has access and available refreshes:
|
||||
if (hasAccessToFreeRefreshSystem)
|
||||
{
|
||||
if (hasAccessToFreeRefreshSystem) {
|
||||
// Initialize/retrieve free refresh count for the desired subtype: daily/weekly
|
||||
fullProfile.spt.freeRepeatableRefreshUsedCount ||= {};
|
||||
const repeatableRefreshCounts = fullProfile.spt.freeRepeatableRefreshUsedCount;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
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 { IProcessBaseTradeRequestData } from "@spt/models/eft/trade/IProcessBaseTradeRequestData";
|
||||
import { IProcessBuyTradeRequestData } from "@spt/models/eft/trade/IProcessBuyTradeRequestData";
|
||||
import {
|
||||
IOfferRequest,
|
||||
IProcessRagfairTradeRequestData,
|
||||
} from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
|
||||
import { IOfferRequest, IProcessRagfairTradeRequestData } from "@spt/models/eft/trade/IProcessRagfairTradeRequestData";
|
||||
import { IProcessSellTradeRequestData } from "@spt/models/eft/trade/IProcessSellTradeRequestData";
|
||||
import { ISellScavItemsToFenceRequestData } from "@spt/models/eft/trade/ISellScavItemsToFenceRequestData";
|
||||
import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes";
|
||||
@ -37,10 +33,10 @@ import { HashUtil } from "@spt/utils/HashUtil";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class TradeController
|
||||
{
|
||||
export class TradeController {
|
||||
protected ragfairConfig: IRagfairConfig;
|
||||
protected traderConfig: ITraderConfig;
|
||||
|
||||
@ -62,8 +58,7 @@ export class TradeController
|
||||
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
|
||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
||||
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
||||
}
|
||||
@ -73,13 +68,11 @@ export class TradeController
|
||||
pmcData: IPmcData,
|
||||
request: IProcessBaseTradeRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// Buying
|
||||
if (request.type === "buy_from_trader")
|
||||
{
|
||||
if (request.type === "buy_from_trader") {
|
||||
const foundInRaid = this.traderConfig.purchasesAreFoundInRaid;
|
||||
const buyData = <IProcessBuyTradeRequestData>request;
|
||||
this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output);
|
||||
@ -88,8 +81,7 @@ export class TradeController
|
||||
}
|
||||
|
||||
// Selling
|
||||
if (request.type === "sell_to_trader")
|
||||
{
|
||||
if (request.type === "sell_to_trader") {
|
||||
const sellData = <IProcessSellTradeRequestData>request;
|
||||
this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID, output);
|
||||
|
||||
@ -107,15 +99,12 @@ export class TradeController
|
||||
pmcData: IPmcData,
|
||||
request: IProcessRagfairTradeRequestData,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
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);
|
||||
if (!fleaOffer)
|
||||
{
|
||||
if (!fleaOffer) {
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
`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(
|
||||
"ragfair-unable_to_purchase_0_count_item",
|
||||
this.itemHelper.getItem(fleaOffer.items[0]._tpl)[1]._name,
|
||||
@ -133,18 +121,14 @@ export class TradeController
|
||||
}
|
||||
|
||||
const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER;
|
||||
if (sellerIsTrader)
|
||||
{
|
||||
if (sellerIsTrader) {
|
||||
this.buyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.buyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
|
||||
}
|
||||
|
||||
// Exit loop early if problem found
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@ -166,11 +150,9 @@ export class TradeController
|
||||
fleaOffer: IRagfairOffer,
|
||||
requestOffer: IOfferRequest,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
// 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`;
|
||||
this.logger.debug(errorMessage);
|
||||
|
||||
@ -206,8 +188,7 @@ export class TradeController
|
||||
fleaOffer: IRagfairOffer,
|
||||
requestOffer: IOfferRequest,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
): void {
|
||||
const buyData: IProcessBuyTradeRequestData = {
|
||||
Action: "TradingConfirm",
|
||||
type: "buy_from_ragfair",
|
||||
@ -226,8 +207,7 @@ export class TradeController
|
||||
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
|
||||
output,
|
||||
);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
if (output.warnings.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -236,8 +216,7 @@ export class TradeController
|
||||
const offerBuyCount = requestOffer.count;
|
||||
|
||||
const isPlayerOffer = this.isPlayerOffer(fleaOffer._id, fleaOffer.user?.id);
|
||||
if (isPlayerOffer)
|
||||
{
|
||||
if (isPlayerOffer) {
|
||||
// Complete selling the offer now its been purchased
|
||||
this.ragfairOfferHelper.completeOffer(offerOwnerId, fleaOffer, offerBuyCount);
|
||||
|
||||
@ -254,17 +233,14 @@ export class TradeController
|
||||
* @param offerOwnerId Owner id
|
||||
* @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
|
||||
if (!offerOwnerId)
|
||||
{
|
||||
if (!offerOwnerId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
return false;
|
||||
}
|
||||
@ -280,8 +256,7 @@ export class TradeController
|
||||
* @param pmcData Player profile
|
||||
* @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;
|
||||
}
|
||||
|
||||
@ -290,8 +265,7 @@ export class TradeController
|
||||
pmcData: IPmcData,
|
||||
request: ISellScavItemsToFenceRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
this.mailMoneyToPlayer(sessionId, request.totalValue, Traders.FENCE);
|
||||
@ -305,8 +279,7 @@ export class TradeController
|
||||
* @param trader Trader to sell items to
|
||||
* @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`);
|
||||
|
||||
// Create single currency item with all currency on it
|
||||
@ -325,7 +298,7 @@ export class TradeController
|
||||
this.traderHelper.getTraderById(trader),
|
||||
MessageType.MESSAGE_WITH_ITEMS,
|
||||
this.randomUtil.getArrayValue(this.databaseService.getTrader(trader).dialogue.soldItems),
|
||||
curencyReward.flatMap((x) => x),
|
||||
curencyReward.flat(),
|
||||
this.timeUtil.getHoursAsSeconds(72),
|
||||
);
|
||||
}
|
||||
@ -343,21 +316,18 @@ export class TradeController
|
||||
items: Item[],
|
||||
handbookPrices: Record<string, number>,
|
||||
traderDetails: ITraderBase,
|
||||
): number
|
||||
{
|
||||
): number {
|
||||
const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(items, parentItemId);
|
||||
|
||||
let totalPrice = 0;
|
||||
for (const itemToSell of itemWithChildren)
|
||||
{
|
||||
for (const itemToSell of itemWithChildren) {
|
||||
const itemDetails = this.itemHelper.getItem(itemToSell._tpl);
|
||||
if (
|
||||
!(
|
||||
itemDetails[0]
|
||||
&& this.itemHelper.isOfBaseclasses(itemDetails[1]._id, traderDetails.items_buy.category)
|
||||
itemDetails[0] &&
|
||||
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
|
||||
continue;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper";
|
||||
@ -13,12 +12,12 @@ import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { FenceService } from "@spt/services/FenceService";
|
||||
import { TraderAssortService } from "@spt/services/TraderAssortService";
|
||||
import { TraderPurchasePersisterService } from "@spt/services/TraderPurchasePersisterService";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class TraderController
|
||||
{
|
||||
export class TraderController {
|
||||
protected traderConfig: ITraderConfig;
|
||||
|
||||
constructor(
|
||||
@ -35,8 +34,7 @@ export class TraderController
|
||||
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||
)
|
||||
{
|
||||
) {
|
||||
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
|
||||
* 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 traderResetStartsWithServer = this.traderConfig.tradersResetFromServerStart;
|
||||
|
||||
const traders = this.databaseService.getTraders();
|
||||
for (const traderId in traders)
|
||||
{
|
||||
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER)
|
||||
{
|
||||
for (const traderId in traders) {
|
||||
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (traderId === Traders.FENCE)
|
||||
{
|
||||
if (traderId === Traders.FENCE) {
|
||||
this.fenceBaseAssortGenerator.generateFenceBaseAssorts();
|
||||
this.fenceService.generateFenceAssorts();
|
||||
continue;
|
||||
@ -68,8 +62,7 @@ export class TraderController
|
||||
const trader = traders[traderId];
|
||||
|
||||
// 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);
|
||||
this.traderAssortService.setPristineTraderAssort(traderId, assortsClone);
|
||||
}
|
||||
@ -90,19 +83,14 @@ export class TraderController
|
||||
* Fence is handled slightly differently
|
||||
* @returns has run
|
||||
*/
|
||||
public update(): boolean
|
||||
{
|
||||
for (const traderId in this.databaseService.getTables().traders)
|
||||
{
|
||||
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER)
|
||||
{
|
||||
public update(): boolean {
|
||||
for (const traderId in this.databaseService.getTables().traders) {
|
||||
if (traderId === "ragfair" || traderId === Traders.LIGHTHOUSEKEEPER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (traderId === Traders.FENCE)
|
||||
{
|
||||
if (this.fenceService.needsPartialRefresh())
|
||||
{
|
||||
if (traderId === Traders.FENCE) {
|
||||
if (this.fenceService.needsPartialRefresh()) {
|
||||
this.fenceService.performPartialRefresh();
|
||||
}
|
||||
|
||||
@ -111,8 +99,7 @@ export class TraderController
|
||||
|
||||
// Trader needs to be refreshed
|
||||
const trader = this.databaseService.getTrader(traderId);
|
||||
if (this.traderAssortHelper.traderAssortsHaveExpired(traderId))
|
||||
{
|
||||
if (this.traderAssortHelper.traderAssortsHaveExpired(traderId)) {
|
||||
this.traderAssortHelper.resetExpiredTrader(trader);
|
||||
|
||||
// Reset purchase data per trader as they have independent reset times
|
||||
@ -129,21 +116,17 @@ export class TraderController
|
||||
* @param sessionID Session id
|
||||
* @returns array if ITraderBase objects
|
||||
*/
|
||||
public getAllTraders(sessionID: string): ITraderBase[]
|
||||
{
|
||||
public getAllTraders(sessionID: string): ITraderBase[] {
|
||||
const traders: ITraderBase[] = [];
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
for (const traderID in this.databaseService.getTables().traders)
|
||||
{
|
||||
if (this.databaseService.getTables().traders[traderID].base._id === "ragfair")
|
||||
{
|
||||
for (const traderID in this.databaseService.getTables().traders) {
|
||||
if (this.databaseService.getTables().traders[traderID].base._id === "ragfair") {
|
||||
continue;
|
||||
}
|
||||
|
||||
traders.push(this.traderHelper.getTrader(traderID, sessionID));
|
||||
|
||||
if (pmcData.Info)
|
||||
{
|
||||
if (pmcData.Info) {
|
||||
this.traderHelper.lvlUp(traderID, pmcData);
|
||||
}
|
||||
}
|
||||
@ -157,15 +140,12 @@ export class TraderController
|
||||
* @param traderB Second trader to compare
|
||||
* @returns 1,-1 or 0
|
||||
*/
|
||||
protected sortByTraderId(traderA: ITraderBase, traderB: ITraderBase): number
|
||||
{
|
||||
if (traderA._id > traderB._id)
|
||||
{
|
||||
protected sortByTraderId(traderA: ITraderBase, traderB: ITraderBase): number {
|
||||
if (traderA._id > traderB._id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (traderA._id < traderB._id)
|
||||
{
|
||||
if (traderA._id < traderB._id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -173,14 +153,12 @@ export class TraderController
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||
import { IWeatherData } from "@spt/models/eft/weather/IWeatherData";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class WeatherController
|
||||
{
|
||||
export class WeatherController {
|
||||
protected weatherConfig: IWeatherConfig;
|
||||
|
||||
constructor(
|
||||
@ -18,14 +17,12 @@ export class WeatherController
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||
)
|
||||
{
|
||||
) {
|
||||
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
||||
}
|
||||
|
||||
/** Handle client/weather */
|
||||
public generate(): IWeatherData
|
||||
{
|
||||
public generate(): IWeatherData {
|
||||
let result: IWeatherData = { acceleration: 0, time: "", date: "", weather: undefined, season: 1 }; // defaults, hydrated below
|
||||
|
||||
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)
|
||||
* @returns Date object
|
||||
*/
|
||||
public getCurrentInRaidTime(): Date
|
||||
{
|
||||
public getCurrentInRaidTime(): Date {
|
||||
return this.weatherGenerator.getInRaidTime();
|
||||
}
|
||||
|
||||
public generateLocal(sesssionID: string): IGetLocalWeatherResponseData
|
||||
{
|
||||
let result: IGetLocalWeatherResponseData = { season: this.seasonalEventService.getActiveWeatherSeason(), weather: [] };
|
||||
public generateLocal(sesssionID: string): IGetLocalWeatherResponseData {
|
||||
const result: IGetLocalWeatherResponseData = {
|
||||
season: this.seasonalEventService.getActiveWeatherSeason(),
|
||||
weather: [],
|
||||
};
|
||||
|
||||
result.weather.push(this.weatherGenerator.generateWeather());
|
||||
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IAddToWishlistRequest } from "@spt/models/eft/wishlist/IAddToWishlistRequest";
|
||||
import { IChangeWishlistItemCategoryRequest } from "@spt/models/eft/wishlist/IChangeWishlistItemCategoryRequest";
|
||||
import { IRemoveFromWishlistRequest } from "@spt/models/eft/wishlist/IRemoveFromWishlistRequest";
|
||||
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class WishlistController
|
||||
{
|
||||
constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder)
|
||||
{}
|
||||
export class WishlistController {
|
||||
constructor(@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder) {}
|
||||
|
||||
/** Handle AddToWishList */
|
||||
public addToWishList(pmcData: IPmcData, request: IAddToWishlistRequest, sessionID: string): IItemEventRouterResponse
|
||||
{
|
||||
for (const itemId of Object.keys(request.items))
|
||||
{
|
||||
public addToWishList(
|
||||
pmcData: IPmcData,
|
||||
request: IAddToWishlistRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse {
|
||||
for (const itemId of Object.keys(request.items)) {
|
||||
pmcData.WishList[itemId] = request.items[itemId];
|
||||
}
|
||||
|
||||
@ -28,10 +28,8 @@ export class WishlistController
|
||||
pmcData: IPmcData,
|
||||
request: IRemoveFromWishlistRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
for (const itemId of request.items)
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
for (const itemId of request.items) {
|
||||
delete pmcData.WishList[itemId];
|
||||
}
|
||||
|
||||
@ -43,8 +41,7 @@ export class WishlistController
|
||||
pmcData: IPmcData,
|
||||
request: IChangeWishlistItemCategoryRequest,
|
||||
sessionID: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
): IItemEventRouterResponse {
|
||||
pmcData.WishList[request.item] = request.category;
|
||||
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { DependencyContainer, Lifecycle } from "tsyringe";
|
||||
import { AchievementCallbacks } from "@spt/callbacks/AchievementCallbacks";
|
||||
import { BotCallbacks } from "@spt/callbacks/BotCallbacks";
|
||||
import { BuildsCallbacks } from "@spt/callbacks/BuildsCallbacks";
|
||||
@ -70,18 +69,18 @@ import { BotWeaponGenerator } from "@spt/generators/BotWeaponGenerator";
|
||||
import { FenceBaseAssortGenerator } from "@spt/generators/FenceBaseAssortGenerator";
|
||||
import { LocationLootGenerator } from "@spt/generators/LocationLootGenerator";
|
||||
import { LootGenerator } from "@spt/generators/LootGenerator";
|
||||
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
||||
import { PMCLootGenerator } from "@spt/generators/PMCLootGenerator";
|
||||
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
||||
import { RagfairAssortGenerator } from "@spt/generators/RagfairAssortGenerator";
|
||||
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
|
||||
import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator";
|
||||
import { RepeatableQuestRewardGenerator } from "@spt/generators/RepeatableQuestRewardGenerator";
|
||||
import { ScavCaseRewardGenerator } from "@spt/generators/ScavCaseRewardGenerator";
|
||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||
import { BarrelInventoryMagGen } from "@spt/generators/weapongen/implementations/BarrelInventoryMagGen";
|
||||
import { ExternalInventoryMagGen } from "@spt/generators/weapongen/implementations/ExternalInventoryMagGen";
|
||||
import { InternalMagazineInventoryMagGen } from "@spt/generators/weapongen/implementations/InternalMagazineInventoryMagGen";
|
||||
import { UbglExternalMagGen } from "@spt/generators/weapongen/implementations/UbglExternalMagGen";
|
||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||
import { AssortHelper } from "@spt/helpers/AssortHelper";
|
||||
import { BotDifficultyHelper } from "@spt/helpers/BotDifficultyHelper";
|
||||
import { BotGeneratorHelper } from "@spt/helpers/BotGeneratorHelper";
|
||||
@ -133,6 +132,10 @@ import { PostSptModLoader } from "@spt/loaders/PostSptModLoader";
|
||||
import { PreSptModLoader } from "@spt/loaders/PreSptModLoader";
|
||||
import { IAsyncQueue } from "@spt/models/spt/utils/IAsyncQueue";
|
||||
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 { BundleDynamicRouter } from "@spt/routers/dynamic/BundleDynamicRouter";
|
||||
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 { NotifierDynamicRouter } from "@spt/routers/dynamic/NotifierDynamicRouter";
|
||||
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 { HealthItemEventRouter } from "@spt/routers/item_events/HealthItemEventRouter";
|
||||
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 { TradeItemEventRouter } from "@spt/routers/item_events/TradeItemEventRouter";
|
||||
import { WishlistItemEventRouter } from "@spt/routers/item_events/WishlistItemEventRouter";
|
||||
import { ItemEventRouter } from "@spt/routers/ItemEventRouter";
|
||||
import { HealthSaveLoadRouter } from "@spt/routers/save_load/HealthSaveLoadRouter";
|
||||
import { InraidSaveLoadRouter } from "@spt/routers/save_load/InraidSaveLoadRouter";
|
||||
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 { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { DatabaseServer } from "@spt/servers/DatabaseServer";
|
||||
import { SptHttpListener } from "@spt/servers/http/SptHttpListener";
|
||||
import { HttpServer } from "@spt/servers/HttpServer";
|
||||
import { RagfairServer } from "@spt/servers/RagfairServer";
|
||||
import { SaveServer } from "@spt/servers/SaveServer";
|
||||
import { WebSocketServer } from "@spt/servers/WebSocketServer";
|
||||
import { SptHttpListener } from "@spt/servers/http/SptHttpListener";
|
||||
import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler";
|
||||
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
|
||||
import { DefaultSptWebSocketMessageHandler } from "@spt/servers/ws/message/DefaultSptWebSocketMessageHandler";
|
||||
import { ISptWebSocketMessageHandler } from "@spt/servers/ws/message/ISptWebSocketMessageHandler";
|
||||
import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler";
|
||||
import { AirdropService } from "@spt/services/AirdropService";
|
||||
import { BotEquipmentFilterService } from "@spt/services/BotEquipmentFilterService";
|
||||
import { BotEquipmentModPoolService } from "@spt/services/BotEquipmentModPoolService";
|
||||
import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService";
|
||||
import { BotLootCacheService } from "@spt/services/BotLootCacheService";
|
||||
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 { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { FenceService } from "@spt/services/FenceService";
|
||||
@ -219,13 +216,6 @@ import { MailSendService } from "@spt/services/MailSendService";
|
||||
import { MapMarkerService } from "@spt/services/MapMarkerService";
|
||||
import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService";
|
||||
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 { NotificationService } from "@spt/services/NotificationService";
|
||||
import { OpenZoneService } from "@spt/services/OpenZoneService";
|
||||
@ -246,12 +236,17 @@ import { RepairService } from "@spt/services/RepairService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { TraderAssortService } from "@spt/services/TraderAssortService";
|
||||
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 { 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 { DatabaseImporter } from "@spt/utils/DatabaseImporter";
|
||||
import { EncodingUtil } from "@spt/utils/EncodingUtil";
|
||||
@ -260,28 +255,30 @@ import { HttpFileUtil } from "@spt/utils/HttpFileUtil";
|
||||
import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil";
|
||||
import { ImporterUtil } from "@spt/utils/ImporterUtil";
|
||||
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 { ObjectId } from "@spt/utils/ObjectId";
|
||||
import { RandomUtil } from "@spt/utils/RandomUtil";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { VFS } from "@spt/utils/VFS";
|
||||
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
|
||||
*/
|
||||
export class Container
|
||||
{
|
||||
public static registerPostLoadTypes(container: DependencyContainer, childContainer: DependencyContainer): void
|
||||
{
|
||||
export class Container {
|
||||
public static registerPostLoadTypes(container: DependencyContainer, childContainer: DependencyContainer): void {
|
||||
container.register<SptHttpListener>("SptHttpListener", SptHttpListener, { lifecycle: Lifecycle.Singleton });
|
||||
childContainer.registerType("HttpListener", "SptHttpListener");
|
||||
}
|
||||
|
||||
public static registerTypes(depContainer: DependencyContainer): void
|
||||
{
|
||||
public static registerTypes(depContainer: DependencyContainer): void {
|
||||
depContainer.register("ApplicationContext", ApplicationContext, { lifecycle: Lifecycle.Singleton });
|
||||
Container.registerUtils(depContainer);
|
||||
|
||||
@ -304,16 +301,20 @@ export class Container
|
||||
Container.registerPrimaryDependencies(depContainer);
|
||||
}
|
||||
|
||||
public static registerPrimaryDependencies(depContainer: DependencyContainer): void
|
||||
{
|
||||
depContainer.register<ILogger>("PrimaryLogger", { useToken: "WinstonLogger" },
|
||||
{ lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<ICloner>("PrimaryCloner", { useToken: "RecursiveCloner" },
|
||||
{ lifecycle: Lifecycle.Singleton });
|
||||
public static registerPrimaryDependencies(depContainer: DependencyContainer): void {
|
||||
depContainer.register<ILogger>(
|
||||
"PrimaryLogger",
|
||||
{ useToken: "WinstonLogger" },
|
||||
{ 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("HttpListenerModService", { useValue: new HttpListenerModService(depContainer) });
|
||||
depContainer.register("OnUpdateModService", { useValue: new OnUpdateModService(depContainer) });
|
||||
@ -409,8 +410,7 @@ export class Container
|
||||
depContainer.registerType("SptWebSocketMessageHandler", "DefaultSptWebSocketMessageHandler");
|
||||
}
|
||||
|
||||
private static registerUtils(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerUtils(depContainer: DependencyContainer): void {
|
||||
// Utils
|
||||
depContainer.register<App>("App", App, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<DatabaseImporter>("DatabaseImporter", DatabaseImporter, {
|
||||
@ -444,8 +444,7 @@ export class Container
|
||||
depContainer.register<ICloner>("RecursiveCloner", RecursiveCloner, { lifecycle: Lifecycle.Singleton });
|
||||
}
|
||||
|
||||
private static registerRouters(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerRouters(depContainer: DependencyContainer): void {
|
||||
// Routers
|
||||
depContainer.register<HttpRouter>("HttpRouter", HttpRouter, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<ImageRouter>("ImageRouter", ImageRouter);
|
||||
@ -530,8 +529,7 @@ export class Container
|
||||
depContainer.register<BuildsStaticRouter>("BuildsStaticRouter", { useClass: BuildsStaticRouter });
|
||||
}
|
||||
|
||||
private static registerGenerators(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerGenerators(depContainer: DependencyContainer): void {
|
||||
// Generators
|
||||
depContainer.register<BotGenerator>("BotGenerator", BotGenerator);
|
||||
depContainer.register<BotWeaponGenerator>("BotWeaponGenerator", BotWeaponGenerator);
|
||||
@ -578,8 +576,7 @@ export class Container
|
||||
depContainer.registerType("InventoryMagGen", "UbglExternalMagGen");
|
||||
}
|
||||
|
||||
private static registerHelpers(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerHelpers(depContainer: DependencyContainer): void {
|
||||
// Helpers
|
||||
depContainer.register<AssortHelper>("AssortHelper", { useClass: AssortHelper });
|
||||
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
|
||||
depContainer.register<BundleLoader>("BundleLoader", BundleLoader, { 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
|
||||
depContainer.register<BotCallbacks>("BotCallbacks", { useClass: BotCallbacks });
|
||||
depContainer.register<BundleCallbacks>("BundleCallbacks", { useClass: BundleCallbacks });
|
||||
@ -691,8 +686,7 @@ export class Container
|
||||
depContainer.register<BuildsCallbacks>("BuildsCallbacks", { useClass: BuildsCallbacks });
|
||||
}
|
||||
|
||||
private static registerServices(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerServices(depContainer: DependencyContainer): void {
|
||||
// Services
|
||||
depContainer.register<DatabaseService>("DatabaseService", DatabaseService, { lifecycle: Lifecycle.Singleton });
|
||||
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
|
||||
depContainer.register<DatabaseServer>("DatabaseServer", DatabaseServer, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<HttpServer>("HttpServer", HttpServer, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<WebSocketServer>("WebSocketServer", WebSocketServer, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<IWebSocketConnectionHandler>("SptWebSocketConnectionHandler", SptWebSocketConnectionHandler, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<ISptWebSocketMessageHandler>("DefaultSptWebSocketMessageHandler", DefaultSptWebSocketMessageHandler, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<IWebSocketConnectionHandler>(
|
||||
"SptWebSocketConnectionHandler",
|
||||
SptWebSocketConnectionHandler,
|
||||
{ lifecycle: Lifecycle.Singleton },
|
||||
);
|
||||
depContainer.register<ISptWebSocketMessageHandler>(
|
||||
"DefaultSptWebSocketMessageHandler",
|
||||
DefaultSptWebSocketMessageHandler,
|
||||
{ lifecycle: Lifecycle.Singleton },
|
||||
);
|
||||
depContainer.register<RagfairServer>("RagfairServer", RagfairServer);
|
||||
depContainer.register<SaveServer>("SaveServer", SaveServer, { lifecycle: Lifecycle.Singleton });
|
||||
depContainer.register<ConfigServer>("ConfigServer", ConfigServer, { lifecycle: Lifecycle.Singleton });
|
||||
}
|
||||
|
||||
private static registerControllers(depContainer: DependencyContainer): void
|
||||
{
|
||||
private static registerControllers(depContainer: DependencyContainer): void {
|
||||
// Controllers
|
||||
depContainer.register<BotController>("BotController", { useClass: BotController });
|
||||
depContainer.register<ClientLogController>("ClientLogController", { useClass: ClientLogController });
|
||||
|
@ -1,5 +1,4 @@
|
||||
export interface OnLoad
|
||||
{
|
||||
onLoad(): Promise<void>
|
||||
getRoute(): string
|
||||
export interface OnLoad {
|
||||
onLoad(): Promise<void>;
|
||||
getRoute(): string;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
export interface OnUpdate
|
||||
{
|
||||
onUpdate(timeSinceLastRun: number): Promise<boolean>
|
||||
getRoute(): string
|
||||
export interface OnUpdate {
|
||||
onUpdate(timeSinceLastRun: number): Promise<boolean>;
|
||||
getRoute(): string;
|
||||
}
|
||||
|
@ -2,33 +2,26 @@ import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
|
||||
|
||||
export class Router
|
||||
{
|
||||
export class Router {
|
||||
protected handledRoutes: HandledRoute[] = [];
|
||||
|
||||
public getTopLevelRoute(): string
|
||||
{
|
||||
public getTopLevelRoute(): string {
|
||||
return "spt";
|
||||
}
|
||||
|
||||
protected getHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
protected getHandledRoutes(): HandledRoute[] {
|
||||
throw new Error("This method needs to be overrode by the router classes");
|
||||
}
|
||||
|
||||
protected getInternalHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
if (this.handledRoutes.length === 0)
|
||||
{
|
||||
protected getInternalHandledRoutes(): HandledRoute[] {
|
||||
if (this.handledRoutes.length === 0) {
|
||||
this.handledRoutes = this.getHandledRoutes();
|
||||
}
|
||||
return this.handledRoutes;
|
||||
}
|
||||
|
||||
public canHandle(url: string, partialMatch = false): boolean
|
||||
{
|
||||
if (partialMatch)
|
||||
{
|
||||
public canHandle(url: string, partialMatch = false): boolean {
|
||||
if (partialMatch) {
|
||||
return this.getInternalHandledRoutes()
|
||||
.filter((r) => r.dynamic)
|
||||
.some((r) => url.includes(r.route));
|
||||
@ -39,80 +32,64 @@ export class Router
|
||||
}
|
||||
}
|
||||
|
||||
export class StaticRouter extends Router
|
||||
{
|
||||
constructor(private routes: RouteAction[])
|
||||
{
|
||||
export class StaticRouter extends Router {
|
||||
constructor(private routes: RouteAction[]) {
|
||||
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);
|
||||
}
|
||||
|
||||
public override getHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
public override getHandledRoutes(): HandledRoute[] {
|
||||
return this.routes.map((route) => new HandledRoute(route.url, false));
|
||||
}
|
||||
}
|
||||
|
||||
export class DynamicRouter extends Router
|
||||
{
|
||||
constructor(private routes: RouteAction[])
|
||||
{
|
||||
export class DynamicRouter extends Router {
|
||||
constructor(private routes: RouteAction[]) {
|
||||
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);
|
||||
}
|
||||
|
||||
public override getHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
public override getHandledRoutes(): HandledRoute[] {
|
||||
return this.routes.map((route) => new HandledRoute(route.url, true));
|
||||
}
|
||||
}
|
||||
|
||||
// The name of this class should be ItemEventRouter, but that name is taken,
|
||||
// So instead I added the definition
|
||||
export class ItemEventRouterDefinition extends Router
|
||||
{
|
||||
export class ItemEventRouterDefinition extends Router {
|
||||
public async handleItemEvent(
|
||||
url: string,
|
||||
pmcData: IPmcData,
|
||||
body: any,
|
||||
sessionID: string,
|
||||
output: IItemEventRouterResponse,
|
||||
): Promise<any>
|
||||
{
|
||||
): Promise<any> {
|
||||
throw new Error("This method needs to be overrode by the router classes");
|
||||
}
|
||||
}
|
||||
|
||||
export class SaveLoadRouter extends Router
|
||||
{
|
||||
public handleLoad(profile: ISptProfile): ISptProfile
|
||||
{
|
||||
export class SaveLoadRouter extends Router {
|
||||
public handleLoad(profile: ISptProfile): ISptProfile {
|
||||
throw new Error("This method needs to be overrode by the router classes");
|
||||
}
|
||||
}
|
||||
|
||||
export class HandledRoute
|
||||
{
|
||||
export class HandledRoute {
|
||||
constructor(
|
||||
public route: string,
|
||||
public dynamic: boolean,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
}
|
||||
|
||||
export class RouteAction
|
||||
{
|
||||
export class RouteAction {
|
||||
constructor(
|
||||
public url: string,
|
||||
public action: (url: string, info: any, sessionID: string, output: string) => Promise<any>,
|
||||
)
|
||||
{}
|
||||
) {}
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { IncomingMessage, ServerResponse } from "node:http";
|
||||
|
||||
export class Serializer
|
||||
{
|
||||
public serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void
|
||||
{
|
||||
export class Serializer {
|
||||
public serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
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
Loading…
x
Reference in New Issue
Block a user