diff --git a/project/assets/configs/trader.json b/project/assets/configs/trader.json index 4e900f2d..4833c800 100644 --- a/project/assets/configs/trader.json +++ b/project/assets/configs/trader.json @@ -241,7 +241,8 @@ } }, "allowBossItems": false - } + }, + "btrDeliveryExpireHours": 240 } } diff --git a/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json b/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json new file mode 100644 index 00000000..02e2e378 --- /dev/null +++ b/project/assets/database/traders/656f0f98d80a697f855d34b1/dialogue.json @@ -0,0 +1,7 @@ +{ + "itemsDelivered": [ + "657399489b19e826a721d75c 0", + "657399489b19e826a721d75c 1", + "657399489b19e826a721d75c 2" + ] +} \ No newline at end of file diff --git a/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json b/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json new file mode 100644 index 00000000..501ec691 --- /dev/null +++ b/project/assets/database/traders/656f0f98d80a697f855d34b1/services.json @@ -0,0 +1,8 @@ +[ + { + "serviceType": "BtrItemsDelivery" + }, + { + "serviceType": "PlayerTaxi" + } +] \ No newline at end of file diff --git a/project/src/callbacks/InraidCallbacks.ts b/project/src/callbacks/InraidCallbacks.ts index 71d2dc22..945d60be 100644 --- a/project/src/callbacks/InraidCallbacks.ts +++ b/project/src/callbacks/InraidCallbacks.ts @@ -5,6 +5,8 @@ import { INullResponseData } from "@spt-aki/models/eft/httpResponse/INullRespons import { IRegisterPlayerRequestData } from "@spt-aki/models/eft/inRaid/IRegisterPlayerRequestData"; import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; +import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData"; +import { IItemDeliveryRequestData } from "@spt-aki/models/eft/inRaid/IItemDeliveryRequestData"; /** * Handle client requests @@ -80,4 +82,23 @@ export class InraidCallbacks { return this.httpResponse.noBody(this.inraidController.getAirdropConfig()); } + + /** + * Handle singleplayer/traderServices/getTraderServices + */ + public getTraderServices(url: string, info: IEmptyRequestData, sessionId: string): string + { + const lastSlashPos = url.lastIndexOf('/'); + const traderId = url.substring(lastSlashPos + 1); + return this.httpResponse.noBody(this.inraidController.getTraderServices(sessionId, traderId)); + } + + /** + * Handle singleplayer/traderServices/itemDelivery + */ + public itemDelivery(url: string, request: IItemDeliveryRequestData, sessionId: string): INullResponseData + { + this.inraidController.itemDelivery(sessionId, request.traderId, request.items); + return this.httpResponse.nullResponse(); + } } diff --git a/project/src/controllers/InraidController.ts b/project/src/controllers/InraidController.ts index 69bba792..454f6c87 100644 --- a/project/src/controllers/InraidController.ts +++ b/project/src/controllers/InraidController.ts @@ -29,6 +29,13 @@ import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCa import { PmcChatResponseService } from "@spt-aki/services/PmcChatResponseService"; import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil"; +import { TraderServicesService } from "@spt-aki/services/TraderServicesService"; +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; +import { Item } from "@spt-aki/models/eft/common/tables/IItem"; +import { MailSendService } from "@spt-aki/services/MailSendService"; +import { RandomUtil } from "@spt-aki/utils/RandomUtil"; +import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig"; +import { MessageType } from "@spt-aki/models/enums/MessageType"; /** * Logic for handling In Raid callbacks @@ -38,6 +45,7 @@ export class InraidController { protected airdropConfig: IAirdropConfig; protected inraidConfig: IInRaidConfig; + protected traderConfig: ITraderConfig; constructor( @inject("WinstonLogger") protected logger: ILogger, @@ -53,14 +61,18 @@ export class InraidController @inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator, @inject("HealthHelper") protected healthHelper: HealthHelper, @inject("TraderHelper") protected traderHelper: TraderHelper, + @inject("TraderServicesService") protected traderServicesService: TraderServicesService, @inject("InsuranceService") protected insuranceService: InsuranceService, @inject("InRaidHelper") protected inRaidHelper: InRaidHelper, @inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ConfigServer") protected configServer: ConfigServer, + @inject("MailSendService") protected mailSendService: MailSendService, + @inject("RandomUtil") protected randomUtil: RandomUtil, ) { this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.inraidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID); + this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); } /** @@ -498,4 +510,32 @@ export class InraidController { return this.airdropConfig; } + + /** + * Handle singleplayer/traderServices/getTraderServices + * @returns Trader services data + */ + public getTraderServices(sessionId: string, traderId: string): ITraderServiceModel[] + { + return this.traderServicesService.getTraderServices(traderId); + } + + /** + * Handle singleplayer/traderServices/itemDelivery + */ + public itemDelivery(sessionId: string, traderId: string, items: Item[]): void + { + const dialogueTemplates = this.databaseServer.getTables().traders[traderId].dialogue; + const messageId = this.randomUtil.getArrayValue(dialogueTemplates.itemsDelivered); + const messageStoreTime = this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.btrDeliveryExpireHours); + + this.mailSendService.sendLocalisedNpcMessageToPlayer( + sessionId, + this.traderHelper.getTraderById(traderId), + MessageType.BTR_ITEMS_DELIVERY, + messageId, + items, + messageStoreTime, + ); + } } diff --git a/project/src/di/Container.ts b/project/src/di/Container.ts index d634aee0..797d9c32 100644 --- a/project/src/di/Container.ts +++ b/project/src/di/Container.ts @@ -250,6 +250,7 @@ import { VFS } from "@spt-aki/utils/VFS"; import { Watermark, WatermarkLocale } from "@spt-aki/utils/Watermark"; import { WinstonMainLogger } from "@spt-aki/utils/logging/WinstonMainLogger"; import { WinstonRequestLogger } from "@spt-aki/utils/logging/WinstonRequestLogger"; +import { TraderServicesService } from "@spt-aki/services/TraderServicesService"; /** * Handle the registration of classes to be used by the Dependency Injection code @@ -656,6 +657,9 @@ export class Container depContainer.register("TraderAssortService", TraderAssortService, { lifecycle: Lifecycle.Singleton, }); + depContainer.register("TraderServicesService", TraderServicesService, { + lifecycle: Lifecycle.Singleton, + }); depContainer.register("RagfairPriceService", RagfairPriceService, { lifecycle: Lifecycle.Singleton, diff --git a/project/src/models/eft/common/tables/ITrader.ts b/project/src/models/eft/common/tables/ITrader.ts index ffe8ada7..25dd97fd 100644 --- a/project/src/models/eft/common/tables/ITrader.ts +++ b/project/src/models/eft/common/tables/ITrader.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Item } from "@spt-aki/models/eft/common/tables/IItem"; +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; export interface ITrader { @@ -8,6 +9,7 @@ export interface ITrader dialogue?: Record; questassort: Record>; suits?: ISuit[]; + services?: ITraderServiceModel[]; } export interface ITraderBase diff --git a/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts b/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts new file mode 100644 index 00000000..4c2337df --- /dev/null +++ b/project/src/models/eft/inRaid/IItemDeliveryRequestData.ts @@ -0,0 +1,7 @@ +import { Item } from "../common/tables/IItem"; + +export interface IItemDeliveryRequestData +{ + items: Item[], + traderId: string +} \ No newline at end of file diff --git a/project/src/models/enums/TraderServiceType.ts b/project/src/models/enums/TraderServiceType.ts new file mode 100644 index 00000000..406ec0bc --- /dev/null +++ b/project/src/models/enums/TraderServiceType.ts @@ -0,0 +1,9 @@ +export enum TraderServiceType +{ + EXUSEC_LOYALTY = "ExUsecLoyalty", + ZRYACHIY_AID = "ZryachiyAid", + CULTISTS_AID = "CultistsAid", + BTR_ITEMS_DELIVERY = "BtrItemsDelivery", + PLAYER_TAXI = "PlayerTaxi", + BTR_BOT_COVER = "BtrBotCover" +} \ No newline at end of file diff --git a/project/src/models/spt/config/ITraderConfig.ts b/project/src/models/spt/config/ITraderConfig.ts index 9f320ed3..b9a1dd37 100644 --- a/project/src/models/spt/config/ITraderConfig.ts +++ b/project/src/models/spt/config/ITraderConfig.ts @@ -44,6 +44,7 @@ export interface FenceConfig blacklistSeasonalItems: boolean; blacklist: string[]; coopExtractGift: CoopExtractReward; + btrDeliveryExpireHours: number; } export interface CoopExtractReward extends LootRequest diff --git a/project/src/models/spt/services/ITraderServiceModel.ts b/project/src/models/spt/services/ITraderServiceModel.ts new file mode 100644 index 00000000..50fcfa8e --- /dev/null +++ b/project/src/models/spt/services/ITraderServiceModel.ts @@ -0,0 +1,8 @@ +import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType"; + +export interface ITraderServiceModel +{ + serviceType: TraderServiceType, + itemsToPay?: Record[], + subServices?: Record[], +} \ No newline at end of file diff --git a/project/src/routers/dynamic/InraidDynamicRouter.ts b/project/src/routers/dynamic/InraidDynamicRouter.ts index 72ccea1d..26e89f72 100644 --- a/project/src/routers/dynamic/InraidDynamicRouter.ts +++ b/project/src/routers/dynamic/InraidDynamicRouter.ts @@ -16,6 +16,13 @@ export class InraidDynamicRouter extends DynamicRouter return this.inraidCallbacks.registerPlayer(url, info, sessionID); }, ), + new RouteAction( + "/singleplayer/traderServices/getTraderServices/", + (url: string, info: any, sessionID: string, output: string): any => + { + return this.inraidCallbacks.getTraderServices(url, info, sessionID); + }, + ), ]); } diff --git a/project/src/routers/static/InraidStaticRouter.ts b/project/src/routers/static/InraidStaticRouter.ts index 3d3eda8d..ac8c040c 100644 --- a/project/src/routers/static/InraidStaticRouter.ts +++ b/project/src/routers/static/InraidStaticRouter.ts @@ -41,6 +41,13 @@ export class InraidStaticRouter extends StaticRouter return this.inraidCallbacks.getAirdropConfig(); }, ), + new RouteAction( + "/singleplayer/traderServices/itemDelivery", + (url: string, info: any, sessionID: string, output: string): any => + { + return this.inraidCallbacks.itemDelivery(url, info, sessionID); + } + ), ]); } } diff --git a/project/src/services/TraderServicesService.ts b/project/src/services/TraderServicesService.ts new file mode 100644 index 00000000..8f6216ef --- /dev/null +++ b/project/src/services/TraderServicesService.ts @@ -0,0 +1,20 @@ +import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; +import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; +import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; +import { inject, injectable } from "tsyringe"; + +@injectable() +export class TraderServicesService +{ + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("DatabaseServer") protected databaseServer: DatabaseServer, + ) + {} + + public getTraderServices(traderId: string): ITraderServiceModel[] + { + const traderServices = this.databaseServer.getTables().traders[traderId]?.services; + return traderServices ?? []; + } +} \ No newline at end of file