Merge branch 'master' into 3.8.0
# Conflicts: # project/.vscode/launch.json # project/assets/database/locations/bigmap/base.json # project/assets/database/locations/interchange/base.json # project/assets/database/locations/rezervbase/base.json # project/gulpfile.mjs # project/package.json # project/src/ErrorHandler.ts # project/src/Program.ts # project/src/callbacks/DataCallbacks.ts # project/src/callbacks/DialogueCallbacks.ts # project/src/callbacks/GameCallbacks.ts # project/src/callbacks/HandbookCallbacks.ts # project/src/callbacks/HealthCallbacks.ts # project/src/callbacks/HttpCallbacks.ts # project/src/callbacks/LauncherCallbacks.ts # project/src/callbacks/LocationCallbacks.ts # project/src/callbacks/MatchCallbacks.ts # project/src/callbacks/ModCallbacks.ts # project/src/callbacks/NotifierCallbacks.ts # project/src/callbacks/PresetCallbacks.ts # project/src/callbacks/ProfileCallbacks.ts # project/src/callbacks/RagfairCallbacks.ts # project/src/callbacks/TraderCallbacks.ts # project/src/context/ApplicationContext.ts # project/src/context/ContextVariableType.ts # project/src/controllers/BotController.ts # project/src/controllers/CustomizationController.ts # project/src/controllers/DialogueController.ts # project/src/controllers/GameController.ts # project/src/controllers/HealthController.ts # project/src/controllers/HideoutController.ts # project/src/controllers/InraidController.ts # project/src/controllers/InsuranceController.ts # project/src/controllers/InventoryController.ts # project/src/controllers/LauncherController.ts # project/src/controllers/LocationController.ts # project/src/controllers/MatchController.ts # project/src/controllers/QuestController.ts # project/src/controllers/RagfairController.ts # project/src/controllers/RepeatableQuestController.ts # project/src/controllers/TradeController.ts # project/src/di/Container.ts # project/src/di/Router.ts # project/src/generators/BotEquipmentModGenerator.ts # project/src/generators/BotLevelGenerator.ts # project/src/generators/BotWeaponGenerator.ts # project/src/generators/LocationGenerator.ts # project/src/generators/LootGenerator.ts # project/src/generators/RepeatableQuestGenerator.ts # project/src/generators/WeatherGenerator.ts # project/src/generators/weapongen/InventoryMagGen.ts # project/src/generators/weapongen/implementations/BarrelInventoryMagGen.ts # project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts # project/src/helpers/AssortHelper.ts # project/src/helpers/BotGeneratorHelper.ts # project/src/helpers/InRaidHelper.ts # project/src/helpers/ProfileHelper.ts # project/src/helpers/RagfairHelper.ts # project/src/helpers/RagfairOfferHelper.ts # project/src/helpers/TraderHelper.ts # project/src/loaders/ModLoadOrder.ts # project/src/loaders/PostDBModLoader.ts # project/src/loaders/PreAkiModLoader.ts # project/src/models/eft/common/IGlobals.ts # project/src/models/eft/common/ILocationBase.ts # project/src/models/eft/common/tables/IBotBase.ts # project/src/models/eft/common/tables/IProfileTemplate.ts # project/src/models/eft/common/tables/ITemplateItem.ts # project/src/models/eft/dialog/IAcceptFriendRequestData.ts # project/src/models/eft/dialog/IDeleteFriendRequest.ts # project/src/models/eft/game/IGameConfigResponse.ts # project/src/models/eft/game/IGameKeepAliveResponse.ts # project/src/models/eft/game/IGameStartResponse.ts # project/src/models/eft/match/IJoinMatchResult.ts # project/src/models/eft/notifier/INotifier.ts # project/src/models/eft/profile/GetProfileStatusResponseData.ts # project/src/models/eft/trade/IProcessBuyTradeRequestData.ts # project/src/models/eft/trade/IProcessSellTradeRequestData.ts # project/src/models/enums/WildSpawnTypeNumber.ts # project/src/models/spt/bots/BotGenerationDetails.ts # project/src/models/spt/config/IBotConfig.ts # project/src/models/spt/config/IBotDurability.ts # project/src/models/spt/config/IInRaidConfig.ts # project/src/models/spt/config/ILocationConfig.ts # project/src/models/spt/config/IQuestConfig.ts # project/src/models/spt/config/ISeasonalEventConfig.ts # project/src/models/spt/server/ILocations.ts # project/src/models/spt/utils/IUuidGenerator.ts # project/src/routers/dynamic/BotDynamicRouter.ts # project/src/routers/dynamic/BundleDynamicRouter.ts # project/src/routers/dynamic/CustomizationDynamicRouter.ts # project/src/routers/dynamic/DataDynamicRouter.ts # project/src/routers/dynamic/HttpDynamicRouter.ts # project/src/routers/dynamic/InraidDynamicRouter.ts # project/src/routers/dynamic/LocationDynamicRouter.ts # project/src/routers/dynamic/NotifierDynamicRouter.ts # project/src/routers/dynamic/TraderDynamicRouter.ts # project/src/routers/save_load/InsuranceSaveLoadRouter.ts # project/src/routers/save_load/ProfileSaveLoadRouter.ts # project/src/routers/serializers/NotifySerializer.ts # project/src/routers/static/BotStaticRouter.ts # project/src/routers/static/BundleStaticRouter.ts # project/src/routers/static/ClientLogStaticRouter.ts # project/src/routers/static/CustomizationStaticRouter.ts # project/src/routers/static/DataStaticRouter.ts # project/src/routers/static/DialogStaticRouter.ts # project/src/routers/static/GameStaticRouter.ts # project/src/routers/static/HealthStaticRouter.ts # project/src/routers/static/InraidStaticRouter.ts # project/src/routers/static/InsuranceStaticRouter.ts # project/src/routers/static/ItemEventStaticRouter.ts # project/src/routers/static/LauncherStaticRouter.ts # project/src/routers/static/LocationStaticRouter.ts # project/src/routers/static/MatchStaticRouter.ts # project/src/routers/static/NotifierStaticRouter.ts # project/src/routers/static/PresetStaticRouter.ts # project/src/routers/static/ProfileStaticRouter.ts # project/src/routers/static/QuestStaticRouter.ts # project/src/routers/static/RagfairStaticRouter.ts # project/src/routers/static/TraderStaticRouter.ts # project/src/routers/static/WeatherStaticRouter.ts # project/src/services/BotEquipmentFilterService.ts # project/src/services/BotGenerationCacheService.ts # project/src/services/BotWeaponModLimitService.ts # project/src/services/PaymentService.ts # project/src/services/ProfileFixerService.ts # project/src/services/RagfairOfferService.ts # project/src/services/RagfairTaxService.ts # project/src/services/RepairService.ts # project/src/services/SeasonalEventService.ts # project/src/utils/RagfairOfferHolder.ts # project/src/utils/TimeUtil.ts # project/src/utils/UUidGenerator.ts # project/src/utils/VFS.ts # project/src/utils/collections/queue/Queue.ts # project/src/utils/logging/AbstractWinstonLogger.ts # project/src/utils/logging/WinstonMainLogger.ts # project/src/utils/logging/WinstonRequestLogger.ts # project/tests/utils/TimeUtil.test.ts Manually resolved by Refringe.
This commit is contained in:
commit
6cd86e67b0
@ -800,14 +800,12 @@
|
||||
"_tpl": "5fd4c4fa16cac650092f6771",
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa0d",
|
||||
"_tpl": "64be79c487d1510151095552",
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa0e",
|
||||
"_tpl": "643ea5b23db6f9f57107d9fd",
|
||||
"upd": {
|
||||
@ -818,62 +816,52 @@
|
||||
},
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa0f",
|
||||
"_tpl": "6410745d5dd49d77bd078485",
|
||||
"parentId": "652a6f5c35258f985804aa0e",
|
||||
"slotId": "mod_stock"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa10",
|
||||
"_tpl": "6410758c857473525b08bb77",
|
||||
"parentId": "652a6f5c35258f985804aa0e",
|
||||
"slotId": "mod_barrel"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa11",
|
||||
"_tpl": "64119d1f2c6d6f921a0929f8",
|
||||
"parentId": "652a6f5c35258f985804aa10",
|
||||
"slotId": "mod_muzzle"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa12",
|
||||
"_tpl": "64119d672c6d6f921a0929fb",
|
||||
"parentId": "652a6f5c35258f985804aa11",
|
||||
"slotId": "mod_sight_front"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa13",
|
||||
"_tpl": "64119d90dcf48d656f0aa275",
|
||||
"parentId": "652a6f5c35258f985804aa10",
|
||||
"slotId": "mod_sight_rear"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa14",
|
||||
"_tpl": "64119cdbdcf48d656f0aa272",
|
||||
"parentId": "652a6f5c35258f985804aa0e",
|
||||
"slotId": "mod_reciever"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa15",
|
||||
"_tpl": "6422e1ea3c0f06190302161a",
|
||||
"parentId": "652a6f5c35258f985804aa0e",
|
||||
"slotId": "mod_magazine"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa16",
|
||||
"_tpl": "641dc35e19604f20c800be18",
|
||||
"parentId": "652a6f5c35258f985804aa0e",
|
||||
"slotId": "mod_scope"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa17",
|
||||
"_tpl": "5b3f7c1c5acfc40dc5296b1d",
|
||||
"parentId": "652a6f5c35258f985804aa16",
|
||||
"slotId": "mod_scope"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa18",
|
||||
"_tpl": "64748cb8de82c85eaf0a273a",
|
||||
"upd": {
|
||||
@ -884,32 +872,332 @@
|
||||
},
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa19",
|
||||
"_tpl": "64748d02d1c009260702b526",
|
||||
"parentId": "652a6f5c35258f985804aa18",
|
||||
"slotId": "mod_barrel"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa1a",
|
||||
"_tpl": "5f60cd6cf2bcbb675b00dac6",
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa1b",
|
||||
"_tpl": "5d5e7d28a4b936645d161203",
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"_id": "652a6f5c35258f985804aa1c",
|
||||
"_tpl": "5d1b376e86f774252519444e",
|
||||
"parentId": "652a6f5c35258f985804aa0b",
|
||||
"slotId": "main"
|
||||
}
|
||||
]
|
||||
},
|
||||
"GROUNDZERO": {
|
||||
"items": [{
|
||||
"_id": "65539de6fe677db90c0f53cb",
|
||||
"_tpl": "619cbf7d23893217ec30b689",
|
||||
"slotId": "main"
|
||||
}
|
||||
],
|
||||
"sender": "System",
|
||||
"messageText": "GROUNDZERO",
|
||||
"collectionTimeHours": 48,
|
||||
"associatedEvent": "Promo"
|
||||
},
|
||||
"IAMMIGHTY": {
|
||||
"items": [{
|
||||
"_id": "6553a38f2c33e9939d0a2391",
|
||||
"_tpl": "6176aca650224f204c1da3fb",
|
||||
"upd": {
|
||||
"FireMode": {
|
||||
"FireMode": "single"
|
||||
}
|
||||
},
|
||||
"parentId": "6553a38f2c33e9939d0a2390",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2392",
|
||||
"_tpl": "6193dcd0f8ee7e52e4210a28",
|
||||
"parentId": "6553a38f2c33e9939d0a2391",
|
||||
"slotId": "mod_pistol_grip"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2393",
|
||||
"_tpl": "617131a4568c120fdd29482d",
|
||||
"parentId": "6553a38f2c33e9939d0a2391",
|
||||
"slotId": "mod_magazine"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2394",
|
||||
"_tpl": "617153016c780c1e710c9a2f",
|
||||
"parentId": "6553a38f2c33e9939d0a2391",
|
||||
"slotId": "mod_stock"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2395",
|
||||
"_tpl": "617154aa1cb55961fa0fdb3b",
|
||||
"parentId": "6553a38f2c33e9939d0a2394",
|
||||
"slotId": "mod_stock_000"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2396",
|
||||
"_tpl": "61713a8fd92c473c770214a4",
|
||||
"parentId": "6553a38f2c33e9939d0a2391",
|
||||
"slotId": "mod_reciever"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2397",
|
||||
"_tpl": "6171407e50224f204c1da3c5",
|
||||
"parentId": "6553a38f2c33e9939d0a2396",
|
||||
"slotId": "mod_scope"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2398",
|
||||
"_tpl": "617151c1d92c473c770214ab",
|
||||
"parentId": "6553a38f2c33e9939d0a2397",
|
||||
"slotId": "mod_scope_000"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a2399",
|
||||
"_tpl": "61702be9faa1272e431522c3",
|
||||
"parentId": "6553a38f2c33e9939d0a2396",
|
||||
"slotId": "mod_barrel"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239a",
|
||||
"_tpl": "61713308d92c473c770214a0",
|
||||
"parentId": "6553a38f2c33e9939d0a2399",
|
||||
"slotId": "mod_muzzle"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239b",
|
||||
"_tpl": "61702f1b67085e45ef140b26",
|
||||
"parentId": "6553a38f2c33e9939d0a2399",
|
||||
"slotId": "mod_gas_block"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239c",
|
||||
"_tpl": "61712eae6c780c1e710c9a1d",
|
||||
"parentId": "6553a38f2c33e9939d0a2396",
|
||||
"slotId": "mod_handguard"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239d",
|
||||
"_tpl": "5bb20e49d4351e3bac1212de",
|
||||
"parentId": "6553a38f2c33e9939d0a2396",
|
||||
"slotId": "mod_sight_rear"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239e",
|
||||
"_tpl": "61702d8a67085e45ef140b24",
|
||||
"parentId": "6553a38f2c33e9939d0a2391",
|
||||
"slotId": "mod_charge"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a239f",
|
||||
"_tpl": "5447a9cd4bdc2dbd208b4567",
|
||||
"upd": {
|
||||
"FireMode": {
|
||||
"FireMode": "single"
|
||||
}
|
||||
},
|
||||
"parentId": "6553a38f2c33e9939d0a2390",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a0",
|
||||
"_tpl": "5cc9bcaed7f00c011c04e179",
|
||||
"parentId": "6553a38f2c33e9939d0a239f",
|
||||
"slotId": "mod_pistol_grip"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a1",
|
||||
"_tpl": "59c1383d86f774290a37e0ca",
|
||||
"parentId": "6553a38f2c33e9939d0a239f",
|
||||
"slotId": "mod_magazine"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a2",
|
||||
"_tpl": "5d4405aaa4b9361e6a4e6bd3",
|
||||
"parentId": "6553a38f2c33e9939d0a239f",
|
||||
"slotId": "mod_reciever"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a3",
|
||||
"_tpl": "59f9d81586f7744c7506ee62",
|
||||
"parentId": "6553a38f2c33e9939d0a23a2",
|
||||
"slotId": "mod_scope"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a4",
|
||||
"_tpl": "55d35ee94bdc2d61338b4568",
|
||||
"parentId": "6553a38f2c33e9939d0a23a2",
|
||||
"slotId": "mod_barrel"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a5",
|
||||
"_tpl": "5cff9e5ed7ad1a09407397d4",
|
||||
"parentId": "6553a38f2c33e9939d0a23a4",
|
||||
"slotId": "mod_muzzle"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a6",
|
||||
"_tpl": "5d00ec68d7ad1a04a067e5be",
|
||||
"parentId": "6553a38f2c33e9939d0a23a4",
|
||||
"slotId": "mod_gas_block"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a7",
|
||||
"_tpl": "5c78f2612e221600114c9f0d",
|
||||
"parentId": "6553a38f2c33e9939d0a23a2",
|
||||
"slotId": "mod_handguard"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a8",
|
||||
"_tpl": "5b7be4895acfc400170e2dd5",
|
||||
"parentId": "6553a38f2c33e9939d0a23a7",
|
||||
"slotId": "mod_foregrip"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23a9",
|
||||
"_tpl": "588226d124597767ad33f787",
|
||||
"parentId": "6553a38f2c33e9939d0a23a8",
|
||||
"slotId": "mod_foregrip"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23aa",
|
||||
"_tpl": "5ba26b01d4351e0085325a51",
|
||||
"parentId": "6553a38f2c33e9939d0a23a7",
|
||||
"slotId": "mod_sight_front"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23ab",
|
||||
"_tpl": "5c78f2882e22165df16b832e",
|
||||
"parentId": "6553a38f2c33e9939d0a23a7",
|
||||
"slotId": "mod_muzzle"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23ac",
|
||||
"_tpl": "5b07dd285acfc4001754240d",
|
||||
"parentId": "6553a38f2c33e9939d0a23a7",
|
||||
"slotId": "mod_tactical_002"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23ad",
|
||||
"_tpl": "5ba26b17d4351e00367f9bdd",
|
||||
"parentId": "6553a38f2c33e9939d0a23a2",
|
||||
"slotId": "mod_sight_rear"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23ae",
|
||||
"_tpl": "5d120a10d7ad1a4e1026ba85",
|
||||
"parentId": "6553a38f2c33e9939d0a239f",
|
||||
"slotId": "mod_stock"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23af",
|
||||
"_tpl": "5d120a28d7ad1a1c8962e295",
|
||||
"parentId": "6553a38f2c33e9939d0a23ae",
|
||||
"slotId": "mod_stock"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23b0",
|
||||
"_tpl": "5d44334ba4b9362b346d1948",
|
||||
"parentId": "6553a38f2c33e9939d0a239f",
|
||||
"slotId": "mod_charge"
|
||||
}, {
|
||||
"_id": "6553a38f2c33e9939d0a23b1",
|
||||
"_tpl": "5c0e655586f774045612eeb2",
|
||||
"parentId": "6553a38f2c33e9939d0a2390",
|
||||
"slotId": "main"
|
||||
}
|
||||
],
|
||||
"sender": "System",
|
||||
"messageText": "IAMMIGHTY",
|
||||
"collectionTimeHours": 48,
|
||||
"associatedEvent": "Promo"
|
||||
},
|
||||
"ARMORPLATES": {
|
||||
"items": [{
|
||||
"_id": "6553aa39382bf255500f5a73",
|
||||
"_tpl": "5c0e722886f7740458316a57",
|
||||
"parentId": "6553aa39382bf255500f5a72",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553aa39382bf255500f5a74",
|
||||
"_tpl": "544a5caa4bdc2d1a388b4568",
|
||||
"parentId": "6553aa39382bf255500f5a72",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553aa39382bf255500f5a75",
|
||||
"_tpl": "5b40e2bc5acfc40016388216",
|
||||
"parentId": "6553aa39382bf255500f5a72",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553aa39382bf255500f5a76",
|
||||
"_tpl": "5b40e2bc5acfc40016388216",
|
||||
"parentId": "6553aa39382bf255500f5a72",
|
||||
"slotId": "main"
|
||||
}
|
||||
],
|
||||
"sender": "System",
|
||||
"messageText": "ARMORPLATES",
|
||||
"collectionTimeHours": 48,
|
||||
"associatedEvent": "Promo"
|
||||
},
|
||||
"RICHANDEXPENSIVE": {
|
||||
"items": [{
|
||||
"_id": "6553adb4e56992db30039456",
|
||||
"_tpl": "5c0e530286f7747fa1419862",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db30039457",
|
||||
"_tpl": "5c0e530286f7747fa1419862",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db30039458",
|
||||
"_tpl": "5c0e533786f7747fa23f4d47",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db30039459",
|
||||
"_tpl": "5c0e533786f7747fa23f4d47",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945a",
|
||||
"_tpl": "5ed5160a87bb8443d10680b5",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945b",
|
||||
"_tpl": "5ed5160a87bb8443d10680b5",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945c",
|
||||
"_tpl": "637b620db7afa97bfc3d7009",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945d",
|
||||
"_tpl": "637b620db7afa97bfc3d7009",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945e",
|
||||
"_tpl": "5c0e534186f7747fa1419867",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553adb4e56992db3003945f",
|
||||
"_tpl": "5c0e534186f7747fa1419867",
|
||||
"parentId": "6553adb4e56992db30039455",
|
||||
"slotId": "main"
|
||||
}
|
||||
],
|
||||
"sender": "System",
|
||||
"messageText": "RICHANDEXPENSIVE",
|
||||
"collectionTimeHours": 48,
|
||||
"associatedEvent": "Promo"
|
||||
},
|
||||
"LEFTHANDHEADEYES": {
|
||||
"items": [{
|
||||
"_id": "6553aef5e83f97d5d30172d6",
|
||||
"_tpl": "5d03775b86f774203e7e0c4b",
|
||||
"parentId": "6553aef5e83f97d5d30172d5",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553aef5e83f97d5d30172d7",
|
||||
"_tpl": "5c12613b86f7743bbe2c3f76",
|
||||
"parentId": "6553aef5e83f97d5d30172d5",
|
||||
"slotId": "main"
|
||||
}, {
|
||||
"_id": "6553aef5e83f97d5d30172d8",
|
||||
"_tpl": "5d1b36a186f7742523398433",
|
||||
"parentId": "6553aef5e83f97d5d30172d5",
|
||||
"slotId": "main"
|
||||
}
|
||||
],
|
||||
"sender": "System",
|
||||
"messageText": "LEFTHANDHEADEYES",
|
||||
"collectionTimeHours": 48,
|
||||
"associatedEvent": "Promo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
"Exit_E10_coop"
|
||||
],
|
||||
"carExtractBaseStandingGain": 0.4,
|
||||
"coopExtractBaseStandingGain": 0.25,
|
||||
"scavExtractGain": 0.01,
|
||||
"keepFiRSecureContainerOnDeath": false
|
||||
}
|
||||
|
@ -144,8 +144,8 @@
|
||||
"minPlayerLevel": 5,
|
||||
"rewardScaling": {
|
||||
"levels": [1, 10, 20, 30, 40, 50, 60],
|
||||
"experience": [1000, 5000, 11000, 59000, 147000, 250000, 450000],
|
||||
"roubles": [15000, 40000, 75000, 100000, 140000, 170000, 210000],
|
||||
"experience": [1000, 2000, 8000, 14000, 20000, 24000, 28000],
|
||||
"roubles": [11000, 20000, 45000, 60000, 77000, 95000, 115000],
|
||||
"items": [2, 4, 5, 5, 5, 5, 5],
|
||||
"reputation": [0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.03],
|
||||
"rewardSpread": 0.5,
|
||||
@ -166,25 +166,98 @@
|
||||
},
|
||||
"traderWhitelist": [{
|
||||
"traderId": "54cb50c76803fa8b248b4571",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "prapor",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"543be6564bdc2df4348b4568",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"5422acb9af1c889c16000029"
|
||||
]
|
||||
}, {
|
||||
"traderId": "54cb57776803fa99248b456e",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "therapist",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"57864a66245977548f04a81f",
|
||||
"5448f39d4bdc2d0a728b4568",
|
||||
"5448f3ac4bdc2dce718b4569",
|
||||
"5448f3a64bdc2d60728b456a",
|
||||
"57864c322459775490116fbf",
|
||||
"57864c8c245977548867e7f1",
|
||||
"5448e8d04bdc2ddf718b4569",
|
||||
"57864e4c24597754843f8723",
|
||||
"57864ee62459775490116fc1"
|
||||
]
|
||||
}, {
|
||||
"traderId": "58330581ace78e27b8b10cee",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "skier",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5a341c4086f77401f2541505",
|
||||
"5448e8d64bdc2dce718b4568",
|
||||
"5448e8d04bdc2ddf718b4569",
|
||||
"5422acb9af1c889c16000029",
|
||||
"55818ad54bdc2ddc698b4569",
|
||||
"57864a3d24597754843f8721",
|
||||
"5a341c4686f77469e155819e",
|
||||
"55818b224bdc2dde698b456f",
|
||||
"5c99f98d86f7745c314214b3",
|
||||
"55818aeb4bdc2ddc698b456a",
|
||||
"55818acf4bdc2dde698b456b",
|
||||
"57864bb7245977548b3b66c2",
|
||||
"590c745b86f7743cc433c5f2"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5935c25fb3acc3127c3d8cd9",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "peacekeeper",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5422acb9af1c889c16000029",
|
||||
"543be6564bdc2df4348b4568",
|
||||
"5448e5284bdc2dcb718b4567",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"57864a3d24597754843f8721",
|
||||
"55818af64bdc2d5b648b4570"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5a7c2eca46aef81a7ca2145d",
|
||||
"questTypes": ["Completion", "Exploration"]
|
||||
"name": "mechanic",
|
||||
"questTypes": ["Completion", "Exploration"],
|
||||
"rewardBaseWhitelist": [
|
||||
"55818af64bdc2d5b648b4570",
|
||||
"5448bc234bdc2d3c308b4569",
|
||||
"55818b164bdc2ddc698b456c",
|
||||
"55818a684bdc2ddd698b456d",
|
||||
"550aa4cd4bdc2dd8348b456c",
|
||||
"5422acb9af1c889c16000029",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"55818b224bdc2dde698b456f"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5ac3b934156ae10c4430e83c",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "ragman",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5a341c4086f77401f2541505",
|
||||
"5448e5724bdc2ddf718b4568",
|
||||
"5448e54d4bdc2dcc718b4568",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"57bef4c42459772e8d35a53b",
|
||||
"5448e53e4bdc2d60728b4567",
|
||||
"5448e5284bdc2dcb718b4567",
|
||||
"57864a66245977548f04a81f"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5c0647fdd443bc2504c2d371",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "jaeger",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5448f3ac4bdc2dce718b4569",
|
||||
"5c99f98d86f7745c314214b3",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"57864bb7245977548b3b66c2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"questConfig": {
|
||||
@ -692,7 +765,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"rewardBaseTypeBlacklist": ["543be5e94bdc2df1348b4568", "5b3f15d486f77432d0509248"],
|
||||
"rewardBaseTypeBlacklist": ["543be5e94bdc2df1348b4568", "5b3f15d486f77432d0509248", "59f32c3b86f77472a31742f0", "59f32bb586f774757e1e8442"],
|
||||
"rewardBlacklist": ["627bce33f21bc425b06ab967"],
|
||||
"rewardAmmoStackMinSize": 20
|
||||
}, {
|
||||
@ -731,25 +804,98 @@
|
||||
},
|
||||
"traderWhitelist": [{
|
||||
"traderId": "54cb50c76803fa8b248b4571",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "prapor",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"543be6564bdc2df4348b4568",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"5422acb9af1c889c16000029"
|
||||
]
|
||||
}, {
|
||||
"traderId": "54cb57776803fa99248b456e",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "therapist",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"57864a66245977548f04a81f",
|
||||
"5448f39d4bdc2d0a728b4568",
|
||||
"5448f3ac4bdc2dce718b4569",
|
||||
"5448f3a64bdc2d60728b456a",
|
||||
"57864c322459775490116fbf",
|
||||
"57864c8c245977548867e7f1",
|
||||
"5448e8d04bdc2ddf718b4569",
|
||||
"57864e4c24597754843f8723",
|
||||
"57864ee62459775490116fc1"
|
||||
]
|
||||
}, {
|
||||
"traderId": "58330581ace78e27b8b10cee",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "skier",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5a341c4086f77401f2541505",
|
||||
"5448e8d64bdc2dce718b4568",
|
||||
"5448e8d04bdc2ddf718b4569",
|
||||
"5422acb9af1c889c16000029",
|
||||
"55818ad54bdc2ddc698b4569",
|
||||
"57864a3d24597754843f8721",
|
||||
"5a341c4686f77469e155819e",
|
||||
"55818b224bdc2dde698b456f",
|
||||
"5c99f98d86f7745c314214b3",
|
||||
"55818aeb4bdc2ddc698b456a",
|
||||
"55818acf4bdc2dde698b456b",
|
||||
"57864bb7245977548b3b66c2",
|
||||
"590c745b86f7743cc433c5f2"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5935c25fb3acc3127c3d8cd9",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "peacekeeper",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5422acb9af1c889c16000029",
|
||||
"543be6564bdc2df4348b4568",
|
||||
"5448e5284bdc2dcb718b4567",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"57864a3d24597754843f8721",
|
||||
"55818af64bdc2d5b648b4570"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5a7c2eca46aef81a7ca2145d",
|
||||
"questTypes": ["Completion", "Exploration"]
|
||||
"name": "mechanic",
|
||||
"questTypes": ["Completion", "Exploration"],
|
||||
"rewardBaseWhitelist": [
|
||||
"55818af64bdc2d5b648b4570",
|
||||
"5448bc234bdc2d3c308b4569",
|
||||
"55818b164bdc2ddc698b456c",
|
||||
"55818a684bdc2ddd698b456d",
|
||||
"550aa4cd4bdc2dd8348b456c",
|
||||
"5422acb9af1c889c16000029",
|
||||
"5485a8684bdc2da71d8b4567",
|
||||
"55818b224bdc2dde698b456f"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5ac3b934156ae10c4430e83c",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "ragman",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5a341c4086f77401f2541505",
|
||||
"5448e5724bdc2ddf718b4568",
|
||||
"5448e54d4bdc2dcc718b4568",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"57bef4c42459772e8d35a53b",
|
||||
"5448e53e4bdc2d60728b4567",
|
||||
"5448e5284bdc2dcb718b4567",
|
||||
"57864a66245977548f04a81f"
|
||||
]
|
||||
}, {
|
||||
"traderId": "5c0647fdd443bc2504c2d371",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"]
|
||||
"name": "jaeger",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination"],
|
||||
"rewardBaseWhitelist": [
|
||||
"5448f3ac4bdc2dce718b4569",
|
||||
"5c99f98d86f7745c314214b3",
|
||||
"590c745b86f7743cc433c5f2",
|
||||
"57864bb7245977548b3b66c2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"questConfig": {
|
||||
@ -1323,7 +1469,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"rewardBaseTypeBlacklist": ["543be5e94bdc2df1348b4568", "5b3f15d486f77432d0509248"],
|
||||
"rewardBaseTypeBlacklist": ["543be5e94bdc2df1348b4568", "5b3f15d486f77432d0509248", "59f32c3b86f77472a31742f0", "59f32bb586f774757e1e8442"],
|
||||
"rewardBlacklist": ["627bce33f21bc425b06ab967"],
|
||||
"rewardAmmoStackMinSize": 15
|
||||
}, {
|
||||
@ -1362,7 +1508,15 @@
|
||||
},
|
||||
"traderWhitelist": [{
|
||||
"traderId": "579dc571d53a0658a154fbec",
|
||||
"questTypes": ["Completion", "Exploration", "Elimination", "Pickup"]
|
||||
"questTypes": ["Completion", "Exploration", "Elimination", "Pickup"],
|
||||
"rewardBaseWhitelist": [
|
||||
"55818a684bdc2ddd698b456d",
|
||||
"55818a594bdc2db9688b456a",
|
||||
"57864c8c245977548867e7f1",
|
||||
"5448ecbe4bdc2d60728b4568",
|
||||
"5422acb9af1c889c16000029",
|
||||
"57864bb7245977548b3b66c2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"questConfig": {
|
||||
|
@ -2117,7 +2117,7 @@
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 2,
|
||||
"2": 0
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
@ -2136,7 +2136,7 @@
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 2,
|
||||
"2": 1
|
||||
"2": 3
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
@ -2162,16 +2162,16 @@
|
||||
},
|
||||
"specialItems": {
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 0
|
||||
"0": 0,
|
||||
"1": 1
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"0": 1,
|
||||
"1": 1,
|
||||
"2": 0
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
|
@ -2134,7 +2134,7 @@
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 2,
|
||||
"2": 0
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
@ -2153,7 +2153,7 @@
|
||||
"weights": {
|
||||
"0": 1,
|
||||
"1": 2,
|
||||
"2": 1
|
||||
"2": 2
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
@ -2186,9 +2186,9 @@
|
||||
},
|
||||
"stims": {
|
||||
"weights": {
|
||||
"0": 2,
|
||||
"1": 1,
|
||||
"2": 0
|
||||
"0": 1,
|
||||
"1": 2,
|
||||
"2": 1
|
||||
},
|
||||
"whitelist": []
|
||||
},
|
||||
|
@ -181,6 +181,7 @@
|
||||
"ragfair-offer_not_found_unable_to_hide": "hideItem() offerId: %s not found, unable to hide offer",
|
||||
"ragfair-tpl_not_a_valid_item": "generateFleaOffersForTrader() tpl: %s not a valid item, skipping",
|
||||
"ragfair-unable_to_find_item_in_inventory": "Unable to find item with id: {{id}} in inventory",
|
||||
"ragfair-unable_to_find_offer_to_remove": "Unable to find offer with id: %s to remove",
|
||||
"ragfair-unable_to_find_requested_items_in_inventory": "Unable to find any requested items in the inventory",
|
||||
"ragfair-unable_to_pay_commission_fee": "Unable to pay commission fee: %s roubles",
|
||||
"ragfair-offer_no_longer_exists": "Offer no longer exists",
|
||||
@ -592,8 +593,9 @@
|
||||
"launcher-profile_standard": "Same as live, basic stash size (10x28), 500,000 roubles",
|
||||
"launcher-profile_leftbehind": "Same as Standard plus; larger stash size (10x38), extra equipment/items, 500 dollars",
|
||||
"launcher-profile_preparetoescape": "Same as Left Behind plus; larger stash size (10x48), extra equipment/items, higher starting reputation with traders, 250 euros",
|
||||
"launcher-edgeofdarkness": "Same as Prepare To Escape plus; larger stash size (10x68), extra equipment/items, higher starting reputation with traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile-edgeofdarkness": "Same as Prepare To Escape plus; larger stash size (10x68), extra equipment/items, higher starting reputation with traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile_spteasystart": "Lots of Roubles/Dollars/Euros, Some QoL skills are level 20, trader rep maxed, starting level is 69, no quests completed",
|
||||
"launcher-profile_sptzerotohero": "Start with almost nothing, no Roubles/Dollars/Euros, no trader rep, 1 knife, no quests completed",
|
||||
"launcher-profile_sptdeveloper": "Testing profile, starting level is 69, lots of Roubles/Dollars/Euros, USEC start with all quests ready to start, BEAR start with all quests ready to hand in, invincibility balaclava"
|
||||
"launcher-profile_sptdeveloper": "Testing profile, starting level is 69, lots of Roubles/Dollars/Euros, USEC start with all quests ready to start, BEAR start with all quests ready to hand in, invincibility balaclava",
|
||||
"launcher-missing_property": "Profile: %s is missing a descriptionLocaleKey property"
|
||||
}
|
@ -402,7 +402,7 @@
|
||||
"launcher-profile_standard": "Igual que en multijugador, el tamaño básico del escondite (10x28), 500.000 rublos",
|
||||
"launcher-profile_leftbehind": "Igual que Standard plus; mayor tamaño de inventario(10x38), equipamiento extra, 500 dólares",
|
||||
"launcher-profile_preparetoescape": "Igual que Left Behind plus; mayor tamaño de inventario (10x48), equipamiento adicional/artículos, mayor reputación inicial con los comerciantes, 250 euros",
|
||||
"launcher-edgeofdarkness": "Igual que Prepare to Escape plus; mayor tamaño de escondite (10x68), equipo adicional/artículos, mayor reputación inicial con los comerciantes, 1000 dólares, 500 euros",
|
||||
"launcher-profile-edgeofdarkness": "Igual que Prepare to Escape plus; mayor tamaño de escondite (10x68), equipo adicional/artículos, mayor reputación inicial con los comerciantes, 1000 dólares, 500 euros",
|
||||
"launcher-profile_spteasystart": "Muchos rublos/dólares/euros, algunas habilidades de QoL son de nivel 20, la reputación de los comerciantes al máximo, el nivel inicial es 69, no hay misiones completadas",
|
||||
"launcher-profile_sptzerotohero": "Comienza con casi nada, sin rublos/dólares/euros, sin reputación de comerciantes, 1 cuchillo y sin misiones completadas",
|
||||
"launcher-profile_sptdeveloper": "Prefil de prueba, el nivel inicial es 69, muchos rublos/dólares/euros, USEC comienza con todas las misiones listas para empezar, BEAR comienza con todas las misiones listas para entregar, pasamontañas de invencibilidad"
|
||||
|
@ -553,7 +553,7 @@
|
||||
"launcher-profile_standard": "Edition Standard, stash basique (10x28), 500 000 roubles",
|
||||
"launcher-profile_leftbehind": "Edition Left Behind, stash moyenne (10x38), équipement en plus, 500 dollars",
|
||||
"launcher-profile_preparetoescape": "Edition Prepare for Escape, stash grande (10x48), plus d'équipement, meilleure rép de démarrage vendeurs, 250 euros",
|
||||
"launcher-edgeofdarkness": "Edition EoD, stash de taille maximale (10x68), un max d'équipement, gros boost avec la rép des traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile-edgeofdarkness": "Edition EoD, stash de taille maximale (10x68), un max d'équipement, gros boost avec la rép des traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile_spteasystart": "SPT mode touriste. Beaucoup de Roubles/Dollars/Euros, certains skills confort sont à 20, réputation traders maximale, level de départ à 69. Aucune quête validée",
|
||||
"launcher-profile_sptzerotohero": "SPT zero to hero ! Tu démarres avec rien, juste ta bite et ton couteau ! Aucune quête validée",
|
||||
"launcher-profile_sptdeveloper": "SPT dév/testing/debug, lvl 69, max Roubles/Dollars/Euros, USEC ==> toutes les quêtes prêtes à être débutées, BEAR ==> toutes les quêtes terminées prêtes à être validées, item balaclava d'invincibilité"
|
||||
|
@ -208,7 +208,7 @@
|
||||
"launcher-profile_standard": "Uguale al profilo standard di live EFT, dimensioni standard del nascondiglio (10x28), 500,000 rubli",
|
||||
"launcher-profile_leftbehind": "Uguale al profilo standard di live EFT ed in più; dimensioni del nascondiglio aumentato (10x38), oggetti/equipaggiamento extra, 500 dollari",
|
||||
"launcher-profile_preparetoescape": "Uguale al profilo Left Behind di live EFT ed in più; dimensioni del nascondiglio aumentato (10x48), oggetti/equipaggiamento extra, reputazione con i mercanti più alta, 250 euro",
|
||||
"launcher-edgeofdarkness": "Uguale al profilo Prepare to Escape di live EFT ed in più; dimensioni del nascondiglio massime (10x68), oggetti/equipaggiamento extra, reputazione con i mercanti più alta, 1000 dollari, 500 euro",
|
||||
"launcher-profile-edgeofdarkness": "Uguale al profilo Prepare to Escape di live EFT ed in più; dimensioni del nascondiglio massime (10x68), oggetti/equipaggiamento extra, reputazione con i mercanti più alta, 1000 dollari, 500 euro",
|
||||
"launcher-profile_spteasystart": "Gran numero di Rubli/Dollari/Euro, alcune abilità utili sono già livello 20, reputazione con i mercanti al massimo, i PMC iniziano al livello 69, nessuna missione completata",
|
||||
"launcher-profile_sptzerotohero": "Inizi con quasi nulla, niente Rubli/Dollari/Euro, reputazione mercanti a zero, un coltello, nessuna missione completata",
|
||||
"launcher-profile_sptdeveloper": "Profile per testare, i PMC iniziano al livello 69, molti Rubli/Dollari/Euro, USEC cominciano con tutte le quest pronte a cominciare, BEAR comincia con tutte le quest pronte ad essere consegnate, balaclava dell'invicibilità incluso",
|
||||
|
@ -479,7 +479,7 @@
|
||||
"launcher-profile_standard": "라이브와 동일, 기본 보관함 크기(10x28), 500,000루블",
|
||||
"launcher-profile_leftbehind": "스탠다드 + 더 큰 보관함 크기(10x38), 추가 장비/아이템, 500달러",
|
||||
"launcher-profile_preparetoescape": "레프트 비하인드와 동일 더 큰 보관함 크기(10x48), 추가 장비/아이템, 상인과의 시작 평판 상승, 250유로",
|
||||
"launcher-edgeofdarkness": "프리페어 투 이스케이프와 동일 더 큰 은신처 크기(10x68), 추가 장비/아이템, 상인과의 높은 시작 평판, 1000달러, 500유로",
|
||||
"launcher-profile-edgeofdarkness": "프리페어 투 이스케이프와 동일 더 큰 은신처 크기(10x68), 추가 장비/아이템, 상인과의 높은 시작 평판, 1000달러, 500유로",
|
||||
"launcher-profile_spteasystart": "많은 루블/달러/유로, 일부 품질 관리 기술 레벨 20, 상인 담당자 최대 레벨, 시작 레벨 69, 완료된 퀘스트 없음",
|
||||
"launcher-profile_sptzerotohero": "루블/달러/유로, 상인 대표, 칼 1개, 퀘스트 완료 등 거의 아무것도 없이 시작하세요.",
|
||||
"launcher-profile_sptdeveloper": "테스트 프로필, 시작 레벨은 69, 많은 루블/달러/유로, USEC는 모든 퀘스트를 시작할 준비가 된 상태로 시작, BEAR는 모든 퀘스트를 제출할 준비가 된 상태로 시작, 무적 발라 클라바"
|
||||
|
@ -553,7 +553,7 @@
|
||||
"launcher-profile_standard": "Zelfde als live, basis stash grootte (10x28), 500,000 roubles",
|
||||
"launcher-profile_leftbehind": "Zelfde als Standard plus; grotere stash (10x38), extra uitrusting/items, 500 dollars",
|
||||
"launcher-profile_preparetoescape": "Zelfde als Left Behind plus; grotere stash (10x48), extra uitrusting/items, hogere start reputatie met traders, 250 euros",
|
||||
"launcher-edgeofdarkness": "Zelfde als Prepare To Escape plus; grotere stash (10x68), extra uitrusting/items, hogere start reputatie met traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile-edgeofdarkness": "Zelfde als Prepare To Escape plus; grotere stash (10x68), extra uitrusting/items, hogere start reputatie met traders, 1000 dollars, 500 euros",
|
||||
"launcher-profile_spteasystart": "Heel veel Roubles/Dollars/Euros, Een aantal QoL skills zijn level 20, trader rep maximaal, start level is 69, geen voltooide missies",
|
||||
"launcher-profile_sptzerotohero": "Start met bijna niks, geen Roubles/Dollars/Euros, geen trader rep, 1 mes, geen voltooide missies",
|
||||
"launcher-profile_sptdeveloper": "Test profiel, start level is 69, heel veel Roubles/Dollars/Euros, USEC start met alle missied klaar om te starten, BEAR start met alle missies klaar om ingeleverd te vorden, invincibility balaclava"
|
||||
|
@ -553,7 +553,7 @@
|
||||
"launcher-profile_standard": "Tak samo jak na oficjalnym, podstawowy rozmiar schowka (10x28), 500 000 rubli",
|
||||
"launcher-profile_leftbehind": "Tak samo jak Standard plus; większy rozmiar schowka (10x38), dodatkowy sprzęt/przedmioty, 500 dolarów",
|
||||
"launcher-profile_preparetoescape": "Tak samo jak Left Behind plus; większy rozmiar schowka (10x48), dodatkowy sprzęt/przedmioty, wyższa startowa reputacja u handlarzy, 250 euro",
|
||||
"launcher-edgeofdarkness": "Tak samo jak Prepare To Escape plus; większy rozmiar schowka (10x68), dodatkowy sprzęt/przedmioty, wyższa startowa reputacja u handlarzy, 1000 dolarów, 500 euro",
|
||||
"launcher-profile-edgeofdarkness": "Tak samo jak Prepare To Escape plus; większy rozmiar schowka (10x68), dodatkowy sprzęt/przedmioty, wyższa startowa reputacja u handlarzy, 1000 dolarów, 500 euro",
|
||||
"launcher-profile_spteasystart": "Dużo rubli/dolarów/euro, niektóre pomocne umiejętności na poziomie 20, maksymalna reputacja u handlarzy, początkowy poziom to 69, żadne zadania nie zostały ukończone",
|
||||
"launcher-profile_sptzerotohero": "Zaczynaj praktycznie od zera, bez rubli/dolarów/euro, bez reputacji u handlarzy, 1 nóż, żadne zadania nie zostały ukończone",
|
||||
"launcher-profile_sptdeveloper": "Profil testowy, początkowy poziom to 69, dużo rubli/dolarów/euro, USEC zaczyna z wszystkimi zadaniami gotowymi do rozpoczęcia, BEAR zaczyna z wszystkimi zadaniami gotowymi do oddania, kominiarka nieśmiertelności"
|
||||
|
@ -548,7 +548,7 @@
|
||||
"launcher-profile_standard": "Вариант Standard, базовый размер схрона (10x28), 500,000 рублей",
|
||||
"launcher-profile_leftbehind": "Вариант Left Behind; увеличенный размер схрона (10x38), дополнительное снаряжение/предметы, 500 долларов",
|
||||
"launcher-profile_preparetoescape": "Вариант Prepare for Escape; увеличенный размер схрона (10x48), дополнительное снаряжение/предметы, изначально повышенная репутация со всеми торговцами в игре, 250 евро",
|
||||
"launcher-edgeofdarkness": "Вариант Edge of Darkness; увеличенный размер схрона (10x68), дополнительное снаряжение/предметы, изначально повышенная репутация со всеми торговцами в игре, 1000 долларов, 500 евро",
|
||||
"launcher-profile-edgeofdarkness": "Вариант Edge of Darkness; увеличенный размер схрона (10x68), дополнительное снаряжение/предметы, изначально повышенная репутация со всеми торговцами в игре, 1000 долларов, 500 евро",
|
||||
"launcher-profile_spteasystart": "Много рублей/долларов/евро, некоторые QoL навыки 20 уровня, максимальная репа у троговцев, начальный уровень 69, задания не выполнены",
|
||||
"launcher-profile_sptzerotohero": "Старт практические без всего, нет рублей/долларов/евро, нет репутации у торговцев, 1 нож, задания не выполнены",
|
||||
"launcher-profile_sptdeveloper": "Профиль для тестирования, начальный уровень 69, много рублей/долларов/евро, USEC начинают со всеми заданиями, готовыми к принятию, BEAR начинают со всеми заданиями, готовыми к сдаче, балаклава неуязвимости"
|
||||
|
@ -553,7 +553,7 @@
|
||||
"launcher-profile_standard": "与在线相同,基本储物空间(10×28),500,000卢布",
|
||||
"launcher-profile_leftbehind": "包含标准版内容以及;更大的储物空间;(10×38),额外的装备和物品,500美元",
|
||||
"launcher-profile_preparetoescape": "包含落后版内容以及;更大的储物空间(10×48),额外的装备和物品,更高的商人初始声望,250欧元",
|
||||
"launcher-edgeofdarkness": "包含准备逃离版内容以及;更大的储物空间(10×68),额外的装备和物品,更高的商人初始声望,1000美元,500欧元",
|
||||
"launcher-profile-edgeofdarkness": "包含准备逃离版内容以及;更大的储物空间(10×68),额外的装备和物品,更高的商人初始声望,1000美元,500欧元",
|
||||
"launcher-profile_spteasystart": "大量卢布、美元和欧元,一些基础技能等级20,商人声望满,初始等级69,没有已完成任务",
|
||||
"launcher-profile_sptzerotohero": "开局几乎没有物资,没有卢布、美元或欧元,没有商人声望,就一把刀,没有已完成任务",
|
||||
"launcher-profile_sptdeveloper": "测试用存档,初始等级69,大量卢布、美元和欧元,USEC开局所有的任务已准备开始,BEAR开局所有任务已准备接取,无敌面罩"
|
||||
|
@ -131,7 +131,7 @@
|
||||
"BossEscortType": "followerBully",
|
||||
"BossName": "bossBully",
|
||||
"BossPlayer": false,
|
||||
"BossZone": "ZoneDormitory",
|
||||
"BossZone": "ZoneDormitory,ZoneGasStation,ZoneScavBase",
|
||||
"Delay": 0,
|
||||
"ForceSpawn": false,
|
||||
"IgnoreMaxBots": false,
|
||||
|
@ -49,7 +49,7 @@
|
||||
"BossEscortType": "followerTagilla",
|
||||
"BossName": "bossKilla",
|
||||
"BossPlayer": false,
|
||||
"BossZone": "ZoneCenter",
|
||||
"BossZone": "ZoneCenterBot,ZoneCenter,ZoneOLI,ZoneIDEA,ZoneGoshan",
|
||||
"Delay": 0,
|
||||
"ForceSpawn": false,
|
||||
"IgnoreMaxBots": false,
|
||||
|
@ -49,7 +49,7 @@
|
||||
"BossEscortType": "followerGluharAssault",
|
||||
"BossName": "bossGluhar",
|
||||
"BossPlayer": false,
|
||||
"BossZone": "ZoneRailStrorage",
|
||||
"BossZone": "ZoneRailStrorage,ZoneRailStrorage,ZoneRailStrorage,ZonePTOR1,ZonePTOR2,ZoneBarrack,ZoneBarrack,ZoneBarrack,ZoneSubStorage",
|
||||
"Delay": 0,
|
||||
"ForceSpawn": false,
|
||||
"IgnoreMaxBots": false,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aki-server",
|
||||
"version": "3.7.1",
|
||||
"version": "3.8.0",
|
||||
"author": "SPT-AKI Server",
|
||||
"license": "NCSA",
|
||||
"main": "obj/bundle.js",
|
||||
@ -49,7 +49,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.3.3",
|
||||
"@jest/globals": "29.7.0",
|
||||
"@pnpm/exe": "8.9.0",
|
||||
"@swc/cli": "0.1.62",
|
||||
"@swc/core": "1.3.92",
|
||||
|
@ -490,13 +490,7 @@ export class HideoutController
|
||||
|
||||
const itemToReturn = hideoutArea.slots.find((x) => x.locationIndex === slotIndexToRemove).item[0];
|
||||
|
||||
const newReq = {
|
||||
items: [{
|
||||
item_id: itemToReturn._tpl,
|
||||
count: 1,
|
||||
}],
|
||||
tid: "ragfair",
|
||||
};
|
||||
const newReq = { items: [{ item_id: itemToReturn._tpl, count: 1 }], tid: "ragfair" };
|
||||
|
||||
output = this.inventoryHelper.addItem(
|
||||
pmcData,
|
||||
@ -821,7 +815,7 @@ export class HideoutController
|
||||
}
|
||||
|
||||
// check if the recipe is the same as the last one
|
||||
const area = pmcData.Hideout.Areas[recipe.areaType];
|
||||
const area = pmcData.Hideout.Areas.find((x) => x.type === recipe.areaType);
|
||||
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
|
||||
@ -866,7 +860,7 @@ export class HideoutController
|
||||
counterHoursCrafting.value = hoursCrafting;
|
||||
|
||||
// Null production data now it's complete - will be cleaned up later by update() process
|
||||
pmcData.Hideout.Production[prodId] = null;
|
||||
pmcData.Hideout.Production[prodId].sptIsComplete = true;
|
||||
};
|
||||
|
||||
// Remove the old production from output object before its sent to client
|
||||
@ -950,8 +944,8 @@ export class HideoutController
|
||||
|
||||
const callback = () =>
|
||||
{
|
||||
// Null production data now it's complete - will be cleaned up later by update() process
|
||||
pmcData.Hideout.Production[prodId] = null;
|
||||
// Flag as complete - will be cleaned up later by update() process
|
||||
pmcData.Hideout.Production[prodId].sptIsComplete = true;
|
||||
};
|
||||
|
||||
return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, true);
|
||||
|
@ -32,6 +32,7 @@ import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
||||
export class InsuranceController
|
||||
{
|
||||
protected insuranceConfig: IInsuranceConfig;
|
||||
protected roubleTpl = "5449016a4bdc2d6f028b456f";
|
||||
|
||||
constructor(
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@ -590,17 +591,17 @@ export class InsuranceController
|
||||
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)
|
||||
{
|
||||
inventoryItemsHash[item._id] = item;
|
||||
}
|
||||
|
||||
// get the price of all items
|
||||
// Get price of all items being insured
|
||||
for (const key of body.items)
|
||||
{
|
||||
itemsToPay.push({
|
||||
id: inventoryItemsHash[key]._id,
|
||||
id: this.roubleTpl, // TODO: update to handle different currencies
|
||||
count: Math.round(this.insuranceService.getPremium(pmcData, inventoryItemsHash[key], body.tid)),
|
||||
});
|
||||
}
|
||||
@ -608,7 +609,7 @@ export class InsuranceController
|
||||
const options: IProcessBuyTradeRequestData = {
|
||||
scheme_items: itemsToPay,
|
||||
tid: body.tid,
|
||||
Action: "",
|
||||
Action: "SptInsure",
|
||||
type: "",
|
||||
item_id: "",
|
||||
count: 0,
|
||||
|
@ -11,6 +11,7 @@ import { IConnectResponse } from "@spt-aki/models/eft/profile/IConnectResponse";
|
||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||
import { ICoreConfig } from "@spt-aki/models/spt/config/ICoreConfig";
|
||||
import { IPackageJsonData } from "@spt-aki/models/spt/mod/IPackageJsonData";
|
||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
||||
@ -23,6 +24,7 @@ export class LauncherController
|
||||
protected coreConfig: ICoreConfig;
|
||||
|
||||
constructor(
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@inject("SaveServer") protected saveServer: SaveServer,
|
||||
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
|
||||
@ -47,22 +49,26 @@ export class LauncherController
|
||||
}
|
||||
|
||||
/**
|
||||
* Get descriptive text for each of the profile editions a player can choose
|
||||
* @returns
|
||||
* 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>
|
||||
{
|
||||
return {
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
Standard: this.localisationService.getText("launcher-profile_standard"),
|
||||
"Left Behind": this.localisationService.getText("launcher-profile_leftbehind"),
|
||||
"Prepare To Escape": this.localisationService.getText("launcher-profile_preparetoescape"),
|
||||
"Edge Of Darkness": this.localisationService.getText("launcher-edgeofdarkness"),
|
||||
"SPT Easy start": this.localisationService.getText("launcher-profile_spteasystart"),
|
||||
"SPT Zero to hero": this.localisationService.getText("launcher-profile_sptzerotohero"),
|
||||
"SPT Developer": this.localisationService.getText("launcher-profile_sptdeveloper"),
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
};
|
||||
const result = {};
|
||||
const dbProfiles = this.databaseServer.getTables().templates.profiles;
|
||||
for (const profileKey in dbProfiles)
|
||||
{
|
||||
const localeKey = dbProfiles[profileKey]?.descriptionLocaleKey;
|
||||
if (!localeKey)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("launcher-missing_property", profileKey));
|
||||
continue;
|
||||
}
|
||||
|
||||
result[profileKey] = this.localisationService.getText(localeKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public find(sessionIdKey: string): Info
|
||||
|
@ -191,6 +191,7 @@ export class MatchController
|
||||
|
||||
if (extractName && this.extractWasViaCoop(extractName) && this.traderConfig.fence.coopExtractGift.sendGift)
|
||||
{
|
||||
this.handleCoopExtract(pmcData, extractName);
|
||||
this.sendCoopTakenFenceMessage(sessionId);
|
||||
}
|
||||
}
|
||||
@ -240,6 +241,41 @@ export class MatchController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle when a player extracts using a coop extract - add rep to fence
|
||||
* @param pmcData Profile
|
||||
* @param extractName Name of extract taken
|
||||
*/
|
||||
protected handleCoopExtract(pmcData: IPmcData, extractName: string): void
|
||||
{
|
||||
if (!pmcData.CoopExtractCounts)
|
||||
{
|
||||
pmcData.CoopExtractCounts = {};
|
||||
}
|
||||
|
||||
// Ensure key exists for extract
|
||||
if (!(extractName in pmcData.CoopExtractCounts))
|
||||
{
|
||||
pmcData.CoopExtractCounts[extractName] = 0;
|
||||
}
|
||||
|
||||
// Increment extract count value
|
||||
pmcData.CoopExtractCounts[extractName] += 1;
|
||||
|
||||
// Get new fence standing value
|
||||
const newFenceStanding = this.getFenceStandingAfterExtract(
|
||||
pmcData,
|
||||
this.inraidConfig.coopExtractBaseStandingGain,
|
||||
pmcData.CoopExtractCounts[extractName],
|
||||
);
|
||||
const fenceId: string = Traders.FENCE;
|
||||
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
|
||||
|
||||
// Check if new standing has leveled up trader
|
||||
this.traderHelper.lvlUp(fenceId, pmcData);
|
||||
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Was extract by car
|
||||
* @param extractName name of extract
|
||||
@ -278,9 +314,17 @@ export class MatchController
|
||||
// Increment extract count value
|
||||
pmcData.CarExtractCounts[extractName] += 1;
|
||||
|
||||
// Not exact replica of Live behaviour
|
||||
// Simplified for now, no real reason to do the whole (unconfirmed) extra 0.01 standing per day regeneration mechanic
|
||||
const newFenceStanding = this.getFenceStandingAfterExtract(
|
||||
pmcData,
|
||||
this.inraidConfig.carExtractBaseStandingGain,
|
||||
pmcData.CarExtractCounts[extractName],
|
||||
);
|
||||
const fenceId: string = Traders.FENCE;
|
||||
this.updateFenceStandingInProfile(pmcData, fenceId, extractName);
|
||||
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
|
||||
|
||||
// Check if new standing has leveled up trader
|
||||
this.traderHelper.lvlUp(fenceId, pmcData);
|
||||
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
||||
|
||||
@ -290,25 +334,25 @@ export class MatchController
|
||||
}
|
||||
|
||||
/**
|
||||
* Update players fence trader standing value in profile
|
||||
* @param pmcData Player profile
|
||||
* @param fenceId Id of fence trader
|
||||
* @param extractName Name of extract used
|
||||
* Get the fence rep gain from using a car or coop extract
|
||||
* @param pmcData Profile
|
||||
* @param baseGain amount gained for the first extract
|
||||
* @param extractCount Number of times extract was taken
|
||||
* @returns Fence standing after taking extract
|
||||
*/
|
||||
protected updateFenceStandingInProfile(pmcData: IPmcData, fenceId: string, extractName: string): void
|
||||
protected getFenceStandingAfterExtract(pmcData: IPmcData, baseGain: number, extractCount: number): number
|
||||
{
|
||||
// Get current standing
|
||||
const fenceId: string = Traders.FENCE;
|
||||
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
||||
|
||||
// Not exact replica of Live behaviour... Simplified for now. No real reason to do the whole (unconfirmed)
|
||||
// extra 0.01 standing per day regeneration mechanic.
|
||||
const baseGain: number = this.inraidConfig.carExtractBaseStandingGain;
|
||||
const extractCount: number = pmcData.CarExtractCounts[extractName];
|
||||
|
||||
// get standing after taking extract x times, x.xx format, gain from extract can be no smaller than 0.01
|
||||
fenceStanding += Math.max(baseGain / extractCount, 0.01);
|
||||
|
||||
// Ensure fence loyalty level is not above/below the range -7 - 15
|
||||
// Ensure fence loyalty level is not above/below the range -7 to 15
|
||||
const newFenceStanding = Math.min(Math.max(fenceStanding, -7), 15);
|
||||
this.logger.debug(`Old vs new fence standing: ${pmcData.TradersInfo[fenceId].standing}, ${newFenceStanding}`);
|
||||
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
|
||||
|
||||
return Number(newFenceStanding.toFixed(2));
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ export class ProfileController
|
||||
pmcData.Hideout.Seed = this.timeUtil.getTimestamp() + (8 * 60 * 60 * 24 * 365); // 8 years in future why? who knows, we saw it in live
|
||||
pmcData.RepeatableQuests = [];
|
||||
pmcData.CarExtractCounts = {};
|
||||
pmcData.CoopExtractCounts = {};
|
||||
|
||||
if (!pmcData.UnlockedInfo)
|
||||
{
|
||||
|
@ -459,9 +459,6 @@ export class Container
|
||||
con.register<ScavCaseRewardGenerator>("ScavCaseRewardGenerator", ScavCaseRewardGenerator, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
con.register<ScavCaseRewardGenerator>("ScavCaseRewardGenerator", ScavCaseRewardGenerator, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
con.register<RagfairAssortGenerator>("RagfairAssortGenerator", { useClass: RagfairAssortGenerator });
|
||||
con.register<RagfairOfferGenerator>("RagfairOfferGenerator", { useClass: RagfairOfferGenerator });
|
||||
con.register<WeatherGenerator>("WeatherGenerator", { useClass: WeatherGenerator });
|
||||
|
@ -196,7 +196,7 @@ export class BotEquipmentModGenerator
|
||||
// Get pool of mods that fit weapon
|
||||
const compatibleModsPool = modPool[parentTemplate._id];
|
||||
|
||||
// Null guard against bad input weapon
|
||||
// biome-ignore lint/complexity/useSimplifiedLogicExpression: <explanation>
|
||||
if (
|
||||
!((parentTemplate._props.Slots.length || parentTemplate._props.Cartridges?.length)
|
||||
|| parentTemplate._props.Chambers?.length)
|
||||
|
@ -121,9 +121,9 @@ export class BotGenerator
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${
|
||||
output[0].Info.Settings.Role
|
||||
} (${botGenerationDetails.eventRole ?? ""}) bots`,
|
||||
`Generated ${botGenerationDetails.botCountToGenerate} ${output[0].Info.Settings.Role} (${
|
||||
botGenerationDetails.eventRole ?? ""
|
||||
}) bots`,
|
||||
);
|
||||
|
||||
return output;
|
||||
|
@ -317,11 +317,7 @@ export class LootGenerator
|
||||
}
|
||||
|
||||
// Add preset to return object
|
||||
itemsToReturn.push({
|
||||
count: 1,
|
||||
item_id: chosenWeaponPreset._id,
|
||||
isPreset: true,
|
||||
});
|
||||
itemsToReturn.push({ count: 1, item_id: chosenWeaponPreset._id, isPreset: true });
|
||||
|
||||
// Get items related to chosen weapon
|
||||
const linkedItemsToWeapon = this.ragfairLinkedItemService.getLinkedDbItems(chosenWeaponTpl);
|
||||
@ -380,11 +376,7 @@ export class LootGenerator
|
||||
|
||||
// No need to add ammo to box, inventoryHelper.addItem() will handle it
|
||||
const chosenAmmoBox = this.randomUtil.getArrayValue(ammoBoxesMatchingCaliber);
|
||||
rewards.push({
|
||||
count: rewardCount,
|
||||
item_id: chosenAmmoBox._id,
|
||||
isPreset: false,
|
||||
});
|
||||
rewards.push({ count: rewardCount, item_id: chosenAmmoBox._id, isPreset: false });
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { RagfairServerHelper } from "@spt-aki/helpers/RagfairServerHelper";
|
||||
import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
|
||||
import { Exit, ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
|
||||
import { TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import {
|
||||
ICompletion,
|
||||
ICompletionAvailableFor,
|
||||
@ -157,10 +158,10 @@ export class RepeatableQuestGenerator
|
||||
// a random combination of listed conditions can be required
|
||||
// possible conditions elements and their relative probability can be defined in QuestConfig.js
|
||||
// We use ProbabilityObjectArray to draw by relative probability. e.g. for targets:
|
||||
// targets: {
|
||||
// Savage: 7,
|
||||
// AnyPmc: 2,
|
||||
// bossBully: 0.5
|
||||
// "targets": {
|
||||
// "Savage": 7,
|
||||
// "AnyPmc": 2,
|
||||
// "bossBully": 0.5
|
||||
// }
|
||||
// higher is more likely. We define the difficulty to be the inverse of the relative probability.
|
||||
|
||||
@ -497,7 +498,7 @@ export class RepeatableQuestGenerator
|
||||
const levelsConfig = repeatableConfig.rewardScaling.levels;
|
||||
const roublesConfig = repeatableConfig.rewardScaling.roubles;
|
||||
|
||||
// in the available dumps only 2 distinct items were ever requested
|
||||
// In the available dumps only 2 distinct items were ever requested
|
||||
let numberDistinctItems = 1;
|
||||
if (Math.random() > 0.75)
|
||||
{
|
||||
@ -506,18 +507,20 @@ export class RepeatableQuestGenerator
|
||||
|
||||
const quest = this.generateRepeatableTemplate("Completion", traderId, repeatableConfig.side) as ICompletion;
|
||||
|
||||
// Filter the items.json items to items the player must retrieve to complete queist: shouldn't be a quest item or "non-existant"
|
||||
let itemSelection = this.getRewardableItems(repeatableConfig);
|
||||
// Filter the items.json items to items the player must retrieve to complete quest: shouldn't be a quest item or "non-existant"
|
||||
const possibleItemsToRetrievePool = this.getRewardableItems(repeatableConfig, traderId);
|
||||
|
||||
// Be fair, don't let the items be more expensive than the reward
|
||||
let roublesBudget = Math.floor(
|
||||
this.mathUtil.interp1(pmcLevel, levelsConfig, roublesConfig) * this.randomUtil.getFloat(0.5, 1),
|
||||
);
|
||||
roublesBudget = Math.max(roublesBudget, 5000);
|
||||
itemSelection = itemSelection.filter((x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget);
|
||||
let itemSelection = possibleItemsToRetrievePool.filter((x) =>
|
||||
this.itemHelper.getItemPrice(x[0]) < roublesBudget
|
||||
);
|
||||
|
||||
// We also have the option to use whitelist and/or blacklist which is defined in repeatableQuests.json as
|
||||
// [{minPlayerLevel: 1, itemIds: ["id1",...]}, {minPlayerLevel: 15, itemIds: ["id3",...]}]
|
||||
// [{"minPlayerLevel": 1, "itemIds": ["id1",...]}, {"minPlayerLevel": 15, "itemIds": ["id3",...]}]
|
||||
if (repeatableConfig.questConfig.Completion.useWhitelist)
|
||||
{
|
||||
const itemWhitelist =
|
||||
@ -870,14 +873,16 @@ export class RepeatableQuestGenerator
|
||||
|
||||
// Possible improvement -> draw trader-specific items e.g. with this.itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink)
|
||||
let roublesBudget = rewardRoubles;
|
||||
let chosenRewardItems = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget);
|
||||
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
|
||||
|
||||
// Add xp reward
|
||||
const rewards: IRewards = {
|
||||
Started: [],
|
||||
Success: [{ value: rewardXP, type: "Experience", index: 0 }],
|
||||
Fail: [],
|
||||
};
|
||||
|
||||
// Add money reward
|
||||
if (traderId === Traders.PEACEKEEPER)
|
||||
{
|
||||
// convert to equivalent dollars
|
||||
@ -891,13 +896,14 @@ export class RepeatableQuestGenerator
|
||||
}
|
||||
|
||||
let index = 2;
|
||||
if (chosenRewardItems.length > 0)
|
||||
if (rewardItemPool.length > 0)
|
||||
{
|
||||
let weaponRewardCount = 0;
|
||||
for (let i = 0; i < rewardNumItems; i++)
|
||||
{
|
||||
let value = 1;
|
||||
let children = null;
|
||||
const itemSelected = chosenRewardItems[this.randomUtil.randInt(chosenRewardItems.length)];
|
||||
let itemCount = 1;
|
||||
let children: Item[] = null;
|
||||
const itemSelected = rewardItemPool[this.randomUtil.randInt(rewardItemPool.length)];
|
||||
if (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.AMMO))
|
||||
{
|
||||
// Dont reward ammo that stacks to less than what's defined in config
|
||||
@ -906,39 +912,55 @@ export class RepeatableQuestGenerator
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we provide ammo we don't want to provide just one bullet
|
||||
value = this.randomUtil.randInt(
|
||||
// Randomise the cartridge count returned
|
||||
itemCount = this.randomUtil.randInt(
|
||||
repeatableConfig.rewardAmmoStackMinSize,
|
||||
itemSelected._props.StackMaxSize,
|
||||
);
|
||||
}
|
||||
else if (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.WEAPON))
|
||||
{
|
||||
const defaultPreset = this.presetHelper.getDefaultPreset(itemSelected._id);
|
||||
if (defaultPreset)
|
||||
if (weaponRewardCount >= 1)
|
||||
{
|
||||
children = this.ragfairServerHelper.reparentPresets(
|
||||
defaultPreset._items[0],
|
||||
defaultPreset._items,
|
||||
// Limit weapon rewards to 1 per daily
|
||||
rewardItemPool = rewardItemPool.filter((x) =>
|
||||
!this.itemHelper.isOfBaseclass(x._id, BaseClasses.WEAPON)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let defaultPreset = this.presetHelper.getDefaultPreset(itemSelected._id);
|
||||
if (!defaultPreset)
|
||||
{
|
||||
// No default for chosen weapon found, get any random default weapon preset
|
||||
const defaultPresets = Object.values(this.presetHelper.getDefaultPresets());
|
||||
defaultPreset = this.randomUtil.getArrayValue(defaultPresets);
|
||||
}
|
||||
rewards.Success.push(this.generateRewardItem(itemSelected._id, value, index, children));
|
||||
|
||||
// TODO: maybe also non-default use ragfair to calculate the price
|
||||
// this.ragfairServer.getWeaponPresetPrice(item, items, existingPrice)
|
||||
children = this.ragfairServerHelper.reparentPresets(defaultPreset._items[0], defaultPreset._items);
|
||||
weaponRewardCount++;
|
||||
}
|
||||
|
||||
roublesBudget -= value * this.itemHelper.getStaticItemPrice(itemSelected._id);
|
||||
// 25% chance to double reward stack (item should be stackable and not weapon)
|
||||
if (this.increaseRewardItemStackSize(itemSelected))
|
||||
{
|
||||
itemCount = 2;
|
||||
}
|
||||
|
||||
rewards.Success.push(this.generateRewardItem(itemSelected._id, itemCount, index, children));
|
||||
const itemCost = (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.WEAPON))
|
||||
? this.itemHelper.getItemMaxPrice(children[0]._tpl) // use if preset is not default : this.itemHelper.getWeaponPresetPrice(children[0], children, this.itemHelper.getStaticItemPrice(itemSelected._id))
|
||||
: this.itemHelper.getStaticItemPrice(itemSelected._id);
|
||||
roublesBudget -= itemCount * itemCost;
|
||||
index += 1;
|
||||
|
||||
// if we still have budget narrow down the items
|
||||
if (roublesBudget > 0)
|
||||
{
|
||||
// Filter possible reward items to only items with a price below the remaining budget
|
||||
chosenRewardItems = chosenRewardItems.filter((x) =>
|
||||
rewardItemPool = rewardItemPool.filter((x) =>
|
||||
this.itemHelper.getStaticItemPrice(x._id) < roublesBudget
|
||||
);
|
||||
if (chosenRewardItems.length === 0)
|
||||
if (rewardItemPool.length === 0)
|
||||
{
|
||||
break; // No reward items left, exit
|
||||
}
|
||||
@ -972,6 +994,18 @@ export class RepeatableQuestGenerator
|
||||
return rewards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should reward item have stack size increased (25% chance)
|
||||
* @param item Item to possibly increase stack size of
|
||||
* @returns True if it should
|
||||
*/
|
||||
protected increaseRewardItemStackSize(item: ITemplateItem): boolean
|
||||
{
|
||||
return item._props.StackMaxSize > 1
|
||||
&& !this.itemHelper.isOfBaseclass(item._id, BaseClasses.WEAPON)
|
||||
&& this.randomUtil.getChance100(25);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a number of items that have a colelctive value of the passed in parameter
|
||||
* @param repeatableConfig Config
|
||||
@ -981,15 +1015,17 @@ export class RepeatableQuestGenerator
|
||||
protected chooseRewardItemsWithinBudget(
|
||||
repeatableConfig: IRepeatableQuestConfig,
|
||||
roublesBudget: number,
|
||||
traderId: string,
|
||||
): ITemplateItem[]
|
||||
{
|
||||
// First filter for type and baseclass to avoid lookup in handbook for non-available items
|
||||
const rewardableItems = this.getRewardableItems(repeatableConfig);
|
||||
const rewardableItemPool = this.getRewardableItems(repeatableConfig, traderId);
|
||||
const minPrice = Math.min(25000, 0.5 * roublesBudget);
|
||||
let itemSelection = rewardableItems.filter((x) =>
|
||||
|
||||
let rewardableItemPoolWithinBudget = rewardableItemPool.filter((x) =>
|
||||
this.itemHelper.getItemPrice(x[0]) < roublesBudget && this.itemHelper.getItemPrice(x[0]) > minPrice
|
||||
).map((x) => x[1]);
|
||||
if (itemSelection.length === 0)
|
||||
if (rewardableItemPoolWithinBudget.length === 0)
|
||||
{
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("repeatable-no_reward_item_found_in_price_range", {
|
||||
@ -998,12 +1034,12 @@ export class RepeatableQuestGenerator
|
||||
}),
|
||||
);
|
||||
// In case we don't find any items in the price range
|
||||
itemSelection = rewardableItems.filter((x) => this.itemHelper.getItemPrice(x[0]) < roublesBudget).map((x) =>
|
||||
x[1]
|
||||
);
|
||||
rewardableItemPoolWithinBudget = rewardableItemPool.filter((x) =>
|
||||
this.itemHelper.getItemPrice(x[0]) < roublesBudget
|
||||
).map((x) => x[1]);
|
||||
}
|
||||
|
||||
return itemSelection;
|
||||
return rewardableItemPoolWithinBudget;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1037,12 +1073,17 @@ export class RepeatableQuestGenerator
|
||||
* @param repeatableQuestConfig Config file
|
||||
* @returns List of rewardable items [[_tpl, itemTemplate],...]
|
||||
*/
|
||||
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][]
|
||||
protected getRewardableItems(
|
||||
repeatableQuestConfig: IRepeatableQuestConfig,
|
||||
traderId: string,
|
||||
): [string, ITemplateItem][]
|
||||
{
|
||||
// check for specific baseclasses which don't make sense as reward item
|
||||
// also check if the price is greater than 0; there are some items whose price can not be found
|
||||
// those are not in the game yet (e.g. AGS grenade launcher)
|
||||
return Object.entries(this.databaseServer.getTables().templates.items).filter(([tpl, itemTemplate]) =>
|
||||
return Object.entries(this.databaseServer.getTables().templates.items).filter(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
([tpl, itemTemplate]) =>
|
||||
{
|
||||
// Base "Item" item has no parent, ignore it
|
||||
if (itemTemplate._parent === "")
|
||||
@ -1050,8 +1091,10 @@ export class RepeatableQuestGenerator
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isValidRewardItem(tpl, repeatableQuestConfig);
|
||||
});
|
||||
const traderWhitelist = repeatableQuestConfig.traderWhitelist.find((x) => x.traderId === traderId);
|
||||
return this.isValidRewardItem(tpl, repeatableQuestConfig, traderWhitelist?.rewardBaseWhitelist);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1060,12 +1103,21 @@ export class RepeatableQuestGenerator
|
||||
* @param {string} tpl template id of item to check
|
||||
* @returns True if item is valid reward
|
||||
*/
|
||||
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean
|
||||
protected isValidRewardItem(
|
||||
tpl: string,
|
||||
repeatableQuestConfig: IRepeatableQuestConfig,
|
||||
itemBaseWhitelist: string[],
|
||||
): boolean
|
||||
{
|
||||
let valid = this.itemHelper.isValidItem(tpl);
|
||||
if (!valid)
|
||||
if (!this.itemHelper.isValidItem(tpl))
|
||||
{
|
||||
return valid;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check global blacklist
|
||||
if (this.itemFilterService.isItemBlacklisted(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Item is on repeatable or global blacklist
|
||||
@ -1080,24 +1132,22 @@ export class RepeatableQuestGenerator
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.itemHelper.isOfBaseclasses(tpl, [
|
||||
BaseClasses.DOG_TAG_USEC,
|
||||
BaseClasses.DOG_TAG_BEAR,
|
||||
BaseClasses.MOUNT,
|
||||
BaseClasses.KEY,
|
||||
BaseClasses.ARMBAND,
|
||||
])
|
||||
)
|
||||
// Skip boss items
|
||||
if (this.itemFilterService.isBossItem(tpl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip globally blacklisted items + boss items
|
||||
// biome-ignore lint/complexity/useSimplifiedLogicExpression: <explanation>
|
||||
valid = !this.itemFilterService.isItemBlacklisted(tpl) && !this.itemFilterService.isBossItem(tpl);
|
||||
// Trader has specific item base types they can give as rewards to player
|
||||
if (itemBaseWhitelist !== undefined)
|
||||
{
|
||||
if (!this.itemHelper.isOfBaseclasses(tpl, [...itemBaseWhitelist]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,9 +197,6 @@ export class InRaidHelper
|
||||
profileData.Info.Experience += profileData.Stats.Eft.TotalSessionExperience;
|
||||
profileData.Stats.Eft.TotalSessionExperience = 0;
|
||||
|
||||
// Remove the Lab card
|
||||
this.removeMapAccessKey(saveProgressRequest, sessionID);
|
||||
|
||||
this.setPlayerInRaidLocationStatusToNone(sessionID);
|
||||
|
||||
if (!saveProgressRequest.isPlayerScav)
|
||||
@ -332,32 +329,6 @@ export class InRaidHelper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some maps have one-time-use keys (e.g. Labs
|
||||
* Remove the relevant key from an inventory based on the post-raid request data passed in
|
||||
* @param offraidData post-raid data
|
||||
* @param sessionID Session id
|
||||
*/
|
||||
protected removeMapAccessKey(offraidData: ISaveProgressRequestData, sessionID: string): void
|
||||
{
|
||||
const locationName = this.saveServer.getProfile(sessionID).inraid.location.toLowerCase();
|
||||
const mapKey = this.databaseServer.getTables().locations[locationName].base.AccessKeys[0];
|
||||
|
||||
if (!mapKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const item of offraidData.profile.Inventory.items)
|
||||
{
|
||||
if (item._tpl === mapKey && item.slotId.toLowerCase() !== "hideout")
|
||||
{
|
||||
this.inventoryHelper.removeItem(offraidData.profile, item._id, sessionID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SPT inraid location Profile property to 'none'
|
||||
* @param sessionID Session id
|
||||
|
@ -30,7 +30,8 @@ export interface IBotBase
|
||||
RepeatableQuests: IPmcDataRepeatableQuest[];
|
||||
Bonuses: Bonus[];
|
||||
Notes: Notes;
|
||||
CarExtractCounts: CarExtractCounts;
|
||||
CarExtractCounts: Record<string, number>;
|
||||
CoopExtractCounts: Record<string, number>;
|
||||
SurvivorClass: SurvivorClass;
|
||||
WishList: string[];
|
||||
/** SPT specific property used during bot generation in raid */
|
||||
@ -387,6 +388,8 @@ export interface Productive
|
||||
/** Used when sending data to client */
|
||||
NeedFuelForAllProductionTime?: boolean;
|
||||
sptIsScavCase?: boolean;
|
||||
/** Some crafts are always inProgress, but need to be reset, e.g. water collector */
|
||||
sptIsComplete?: boolean;
|
||||
}
|
||||
|
||||
export interface Production extends Productive
|
||||
|
@ -3,14 +3,20 @@ import { Dialogue, IUserBuilds } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
|
||||
export interface IProfileTemplates
|
||||
{
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
Standard: IProfileSides;
|
||||
"Left Behind": IProfileSides;
|
||||
"Prepare To Escape": IProfileSides;
|
||||
"Edge Of Darkness": IProfileSides;
|
||||
"SPT Developer": IProfileSides;
|
||||
"SPT Easy start": IProfileSides;
|
||||
"SPT Zero to hero": IProfileSides;
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
}
|
||||
|
||||
export interface IProfileSides
|
||||
{
|
||||
descriptionLocaleKey: string;
|
||||
usec: TemplateSide;
|
||||
bear: TemplateSide;
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
// biome-ignore lint/suspicious/noEmptyInterface: <explanation>
|
||||
export interface IAcceptFriendRequestData extends IBaseFriendRequest
|
||||
{
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noEmptyInterface: <explanation>
|
||||
export interface ICancelFriendRequestData extends IBaseFriendRequest
|
||||
{
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcess
|
||||
|
||||
export interface IProcessBuyTradeRequestData extends IProcessBaseTradeRequestData
|
||||
{
|
||||
Action: "buy_from_trader" | "TradingConfirm" | "RestoreHealth" | "";
|
||||
Action: "buy_from_trader" | "TradingConfirm" | "RestoreHealth" | "SptInsure" | "SptRepair" | "";
|
||||
type: string;
|
||||
tid: string;
|
||||
item_id: string;
|
||||
@ -14,6 +14,7 @@ export interface IProcessBuyTradeRequestData extends IProcessBaseTradeRequestDat
|
||||
|
||||
export interface SchemeItem
|
||||
{
|
||||
/** Id of stack to take money from, is money tpl when Action is `SptInsure` */
|
||||
id: string;
|
||||
count: number;
|
||||
}
|
||||
|
@ -40,6 +40,6 @@ export enum WildSpawnTypeNumber
|
||||
PEACEFULLZRYACHIYEVENT = 38,
|
||||
SECTACTPRIESTEVENT = 39,
|
||||
RAVANGEZRYACHIYEVENT = 40,
|
||||
SPTUSEC = 41,
|
||||
SPTBEAR = 42,
|
||||
SPTUSEC = 38,
|
||||
SPTBEAR = 39,
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ export interface IInRaidConfig extends IBaseConfig
|
||||
carExtracts: string[];
|
||||
/** Names of coop extracts */
|
||||
coopExtracts: string[];
|
||||
/** Fene rep gain from a single car extract */
|
||||
/** Fence rep gain from a single car extract */
|
||||
carExtractBaseStandingGain: number;
|
||||
/** Fence rep gain from a single coop extract */
|
||||
coopExtractBaseStandingGain: number;
|
||||
/** Fence rep gain when successfully extracting as pscav */
|
||||
scavExtractGain: number;
|
||||
/** On death should items in your secure keep their Find in raid status regardless of how you finished the raid */
|
||||
|
@ -76,6 +76,7 @@ export interface ITraderWhitelist
|
||||
{
|
||||
traderId: string;
|
||||
questTypes: string[];
|
||||
rewardBaseWhitelist: string[];
|
||||
}
|
||||
|
||||
export interface IRepeatableQuestTypesConfig
|
||||
|
@ -83,6 +83,9 @@ export class EventOutputHolder
|
||||
);
|
||||
profileChanges.improvements = this.jsonUtil.clone(this.getImprovementsFromProfileAndFlagComplete(pmcData));
|
||||
profileChanges.traderRelations = this.constructTraderRelations(pmcData.TradersInfo);
|
||||
|
||||
// Fixes container craft from water collector not resetting after collection
|
||||
this.resetSptIsCompleteFlaggedCrafts(pmcData.Hideout.Production);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,6 +156,13 @@ export class EventOutputHolder
|
||||
continue;
|
||||
}
|
||||
|
||||
// Complete and is a water collector craft
|
||||
// Needed as canister craft (water collector) is continuous
|
||||
if (production.sptIsComplete && productionKey === "5d5589c1f934db045e6c5492")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip completed
|
||||
if (!production.inProgress)
|
||||
{
|
||||
@ -176,4 +186,22 @@ export class EventOutputHolder
|
||||
|
||||
return productions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required as continuous productions don't reset and stay at 100% completion but client thinks it hasn't started
|
||||
* @param productions Productions in a profile
|
||||
*/
|
||||
protected resetSptIsCompleteFlaggedCrafts(productions: Record<string, Productive>): void
|
||||
{
|
||||
for (const productionKey in productions)
|
||||
{
|
||||
const production = productions[productionKey];
|
||||
if (production.sptIsComplete)
|
||||
{
|
||||
production.sptIsComplete = false;
|
||||
production.Progress = 0;
|
||||
production.StartTimestamp = this.timeUtil.getTimestamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,6 @@ import { IAkiProfile } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
@injectable()
|
||||
export class InsuranceSaveLoadRouter extends SaveLoadRouter
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public override getHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
return [new HandledRoute("aki-insurance", false)];
|
||||
|
@ -7,11 +7,6 @@ import { IAkiProfile } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
@injectable()
|
||||
export class ProfileSaveLoadRouter extends SaveLoadRouter
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public override getHandledRoutes(): HandledRoute[]
|
||||
{
|
||||
return [new HandledRoute("aki-profile", false)];
|
||||
|
@ -51,9 +51,7 @@ export class PaymentService
|
||||
// Track the amounts of each type of currency involved in the trade.
|
||||
const currencyAmounts: { [key: string]: number; } = {};
|
||||
|
||||
// Delete barter items and track currencies if the action is "TradingConfirm".
|
||||
if (request.Action === "TradingConfirm")
|
||||
{
|
||||
// Delete barter items and track currencies
|
||||
for (const index in request.scheme_items)
|
||||
{
|
||||
// Find the corresponding item in the player's inventory.
|
||||
@ -69,10 +67,15 @@ export class PaymentService
|
||||
else
|
||||
{
|
||||
// If the item is money, add its count to the currencyAmounts object.
|
||||
currencyAmounts[item._tpl] = (currencyAmounts[item._tpl] || 0)
|
||||
+ request.scheme_items[index].count;
|
||||
currencyAmounts[item._tpl] = (currencyAmounts[item._tpl] || 0) + request.scheme_items[index].count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used by `SptInsure`
|
||||
// Handle differently, `id` is the money type tpl
|
||||
const currencyTpl = request.scheme_items[index].id;
|
||||
currencyAmounts[currencyTpl] = (currencyAmounts[currencyTpl] || 0) + request.scheme_items[index].count;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,6 +90,7 @@ export class PaymentService
|
||||
|
||||
if (currencyAmount > 0)
|
||||
{
|
||||
// Find money stacks in inventory and remove amount needed + update output object to inform client of changes
|
||||
output = this.addPaymentToOutput(pmcData, currencyTpl, currencyAmount, sessionID, output);
|
||||
|
||||
// If there are warnings, exit early.
|
||||
@ -214,6 +218,7 @@ export class PaymentService
|
||||
{
|
||||
const request = {
|
||||
items: [{
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
item_id: currency,
|
||||
count: calcAmount,
|
||||
}],
|
||||
@ -258,7 +263,7 @@ export class PaymentService
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove currency from player stash/inventory
|
||||
* Remove currency from player stash/inventory and update client object with changes
|
||||
* @param pmcData Player profile to find and remove currency from
|
||||
* @param currencyTpl Type of currency to pay
|
||||
* @param amountToPay money value to pay
|
||||
|
@ -6,8 +6,8 @@ import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
||||
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
||||
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
|
||||
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
||||
import { IHideoutImprovement } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { Bonus, HideoutSlot, IQuestStatus } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { IHideoutImprovement } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
|
||||
import { StageBonus } from "@spt-aki/models/eft/hideout/IHideoutArea";
|
||||
import { IAkiProfile } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
@ -83,7 +83,7 @@ export class ProfileFixerService
|
||||
this.reorderHideoutAreasWithResouceInputs(pmcProfile);
|
||||
|
||||
if (
|
||||
pmcProfile.Hideout.Areas[HideoutAreas.GENERATOR].slots.length
|
||||
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR).slots.length
|
||||
< (6
|
||||
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
|
||||
.Generator.Slots)
|
||||
@ -100,7 +100,7 @@ export class ProfileFixerService
|
||||
}
|
||||
|
||||
if (
|
||||
pmcProfile.Hideout.Areas[HideoutAreas.WATER_COLLECTOR].slots.length
|
||||
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR).slots.length
|
||||
< (1
|
||||
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
|
||||
.WaterCollector.Slots)
|
||||
@ -117,7 +117,7 @@ export class ProfileFixerService
|
||||
}
|
||||
|
||||
if (
|
||||
pmcProfile.Hideout.Areas[HideoutAreas.AIR_FILTERING].slots.length
|
||||
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.AIR_FILTERING).slots.length
|
||||
< (3
|
||||
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
|
||||
.AirFilteringUnit.Slots)
|
||||
@ -135,7 +135,7 @@ export class ProfileFixerService
|
||||
|
||||
// BTC Farm doesnt have extra slots for hideout management, but we still check for modded stuff!!
|
||||
if (
|
||||
pmcProfile.Hideout.Areas[HideoutAreas.BITCOIN_FARM].slots.length
|
||||
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM).slots.length
|
||||
< (50
|
||||
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
|
||||
.BitcoinFarm.Slots)
|
||||
@ -571,7 +571,7 @@ export class ProfileFixerService
|
||||
*/
|
||||
protected addMissingWallImprovements(pmcProfile: IPmcData): void
|
||||
{
|
||||
const profileWallArea = pmcProfile.Hideout.Areas[HideoutAreas.EMERGENCY_WALL];
|
||||
const profileWallArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
|
||||
const wallDb = this.databaseServer.getTables().hideout.areas.find((x) =>
|
||||
x.type === HideoutAreas.EMERGENCY_WALL
|
||||
);
|
||||
@ -647,8 +647,7 @@ export class ProfileFixerService
|
||||
|
||||
for (const areaId of areasToCheck)
|
||||
{
|
||||
const area = pmcProfile.Hideout.Areas[areaId];
|
||||
|
||||
const area = pmcProfile.Hideout.Areas.find((x) => x.type === areaId);
|
||||
if (!area)
|
||||
{
|
||||
this.logger.debug(`unable to sort ${areaId} slots, no area found`);
|
||||
|
@ -239,9 +239,6 @@ export class RagfairOfferService
|
||||
const sessionID = profile.sessionId;
|
||||
const offerIndex = profile.RagfairInfo.offers.findIndex((o) => o._id === offer._id);
|
||||
|
||||
profile.RagfairInfo.rating -= this.ragfairConfig.sell.reputation.loss;
|
||||
profile.RagfairInfo.isRatingGrowing = false;
|
||||
|
||||
if (offerIndex === -1)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("ragfair-unable_to_find_offer_to_remove", offer._id));
|
||||
@ -251,6 +248,9 @@ export class RagfairOfferService
|
||||
);
|
||||
}
|
||||
|
||||
profile.RagfairInfo.rating -= this.ragfairConfig.sell.reputation.loss;
|
||||
profile.RagfairInfo.isRatingGrowing = false;
|
||||
|
||||
if (offer.items[0].upd.StackObjectsCount > offer.items[0].upd.OriginalStackObjectsCount)
|
||||
{
|
||||
offer.items[0].upd.StackObjectsCount = offer.items[0].upd.OriginalStackObjectsCount;
|
||||
|
@ -123,7 +123,7 @@ export class RepairService
|
||||
const options: IProcessBuyTradeRequestData = {
|
||||
scheme_items: [{ id: repairedItemId, count: Math.round(repairCost) }],
|
||||
tid: traderId,
|
||||
Action: "",
|
||||
Action: "SptRepair",
|
||||
type: "",
|
||||
item_id: "",
|
||||
count: 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user