Improve handling of a mail profileChangeEvents property

This commit is contained in:
Dev 2023-12-07 20:16:04 +00:00
parent 7c2d93e87b
commit c015882eac
10 changed files with 133 additions and 13 deletions

View File

@ -19,6 +19,7 @@ import { IInventoryTagRequestData } from "@spt-aki/models/eft/inventory/IInvento
import { IInventoryToggleRequestData } from "@spt-aki/models/eft/inventory/IInventoryToggleRequestData"; import { IInventoryToggleRequestData } from "@spt-aki/models/eft/inventory/IInventoryToggleRequestData";
import { IInventoryTransferRequestData } from "@spt-aki/models/eft/inventory/IInventoryTransferRequestData"; import { IInventoryTransferRequestData } from "@spt-aki/models/eft/inventory/IInventoryTransferRequestData";
import { IOpenRandomLootContainerRequestData } from "@spt-aki/models/eft/inventory/IOpenRandomLootContainerRequestData"; import { IOpenRandomLootContainerRequestData } from "@spt-aki/models/eft/inventory/IOpenRandomLootContainerRequestData";
import { IRedeemProfileRequestData } from "@spt-aki/models/eft/inventory/IRedeemProfileRequestData";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
@injectable() @injectable()
@ -156,4 +157,12 @@ export class InventoryCallbacks
{ {
return this.inventoryController.openRandomLootContainer(pmcData, body, sessionID); return this.inventoryController.openRandomLootContainer(pmcData, body, sessionID);
} }
public redeemProfileReward(pmcData: IPmcData,
body: IRedeemProfileRequestData,
sessionId: string
): IItemEventRouterResponse
{
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId)
}
} }

View File

@ -27,6 +27,7 @@ import { IInventoryTagRequestData } from "@spt-aki/models/eft/inventory/IInvento
import { IInventoryToggleRequestData } from "@spt-aki/models/eft/inventory/IInventoryToggleRequestData"; import { IInventoryToggleRequestData } from "@spt-aki/models/eft/inventory/IInventoryToggleRequestData";
import { IInventoryTransferRequestData } from "@spt-aki/models/eft/inventory/IInventoryTransferRequestData"; import { IInventoryTransferRequestData } from "@spt-aki/models/eft/inventory/IInventoryTransferRequestData";
import { IOpenRandomLootContainerRequestData } from "@spt-aki/models/eft/inventory/IOpenRandomLootContainerRequestData"; import { IOpenRandomLootContainerRequestData } from "@spt-aki/models/eft/inventory/IOpenRandomLootContainerRequestData";
import { IRedeemProfileRequestData } from "@spt-aki/models/eft/inventory/IRedeemProfileRequestData";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes"; import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
import { SkillTypes } from "@spt-aki/models/enums/SkillTypes"; import { SkillTypes } from "@spt-aki/models/enums/SkillTypes";
@ -36,6 +37,7 @@ import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { FenceService } from "@spt-aki/services/FenceService"; import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PlayerService } from "@spt-aki/services/PlayerService";
import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService"; import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
@ -60,6 +62,7 @@ export class InventoryController
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("PaymentHelper") protected paymentHelper: PaymentHelper, @inject("PaymentHelper") protected paymentHelper: PaymentHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("PlayerService") protected playerService: PlayerService,
@inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
@ -649,19 +652,32 @@ export class InventoryController
if (itemId) if (itemId)
{ {
// item found this.flagItemsAsInspectedAndRewardXp([itemId], pmcData);
const item = this.databaseServer.getTables().templates.items[itemId];
pmcData.Info.Experience += item._props.ExamineExperience;
pmcData.Encyclopedia[itemId] = true;
// TODO: update this with correct calculation using values from globals json
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, 0.5);
} }
return this.eventOutputHolder.getOutput(sessionID); return this.eventOutputHolder.getOutput(sessionID);
} }
protected flagItemsAsInspectedAndRewardXp(itemTpls: string[], pmcProfile: IPmcData): void
{
for (const itemTpl of itemTpls)
{
// item found
const item = this.databaseServer.getTables().templates.items[itemTpl];
if (!item)
{
this.logger.warning(`Unable to find item with id ${itemTpl}, skipping inspection`)
return;
}
pmcProfile.Info.Experience += item._props.ExamineExperience;
pmcProfile.Encyclopedia[itemTpl] = true;
}
// TODO: update this with correct calculation using values from globals json
this.profileHelper.addSkillPointsToPlayer(pmcProfile, SkillTypes.INTELLECT, 0.05 * itemTpls.length);
}
/** /**
* Get the tplid of an item from the examine request object * Get the tplid of an item from the examine request object
* @param body response request * @param body response request
@ -910,4 +926,59 @@ export class InventoryController
return output; return output;
} }
public redeemProfileReward(pmcData: IPmcData, request: IRedeemProfileRequestData, sessionId: string): IItemEventRouterResponse
{
const output = this.eventOutputHolder.getOutput(sessionId);
const fullprofile = this.profileHelper.getFullProfile(sessionId);
for (const event of request.events)
{
// Hard coded to `SYSTEM` for now
// TODO: make this dynamic
const dialog = fullprofile.dialogues["59e7125688a45068a6249071"];
const mail = dialog.messages.find(x => x._id === event.MessageId);
const mailEvent = mail.profileChangeEvents.find(x => x._id === event.EventId);
switch (mailEvent.Type)
{
case "TraderSalesSum":
pmcData.TradersInfo[mailEvent.entity].salesSum = mailEvent.value;
this.logger.success(`Set trader ${mailEvent.entity}: Sales Sum to: ${mailEvent.value}`);
break;
case "TraderStanding":
pmcData.TradersInfo[mailEvent.entity].standing = mailEvent.value;
this.logger.success(`Set trader ${mailEvent.entity}: Standing to: ${mailEvent.value}`);
break;
case "ProfileLevel":
pmcData.Info.Experience = mailEvent.value;
pmcData.Info.Level = this.playerService.calculateLevel(pmcData);
this.logger.success(`Set profile xp to: ${mailEvent.value}`);
break;
case "SkillPoints":
{
const profileSkill = pmcData.Skills.Common.find(x => x.Id === mailEvent.entity);
profileSkill.Progress = mailEvent.value;
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
break;
}
case "ExamineAllItems":
{
const itemsToInspect = this.itemHelper.getItems().filter(x => x._type !== "Node");
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map(x => x._id), pmcData);
this.logger.success(`Flagged ${itemsToInspect.length} items as examined`);
break;
}
case "UnlockTrader":
pmcData.TradersInfo[mailEvent.entity].unlocked = true;
this.logger.success(`Trader ${mailEvent.entity} Unlocked`);
break;
default:
this.logger.success(`Unhandled profile reward event: ${mailEvent.Type}`);
break;
}
}
return output;
}
} }

View File

@ -0,0 +1,13 @@
import { IInventoryBaseActionRequestData } from "./IInventoryBaseActionRequestData";
export interface IRedeemProfileRequestData extends IInventoryBaseActionRequestData
{
Action: "RedeemProfileReward";
events: IRedeemProfileRequestEvent[]
}
export interface IRedeemProfileRequestEvent
{
MessageId: string
EventId: string
}

View File

@ -3,6 +3,7 @@ import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { EquipmentBuildType } from "@spt-aki/models/enums/EquipmentBuildType"; import { EquipmentBuildType } from "@spt-aki/models/enums/EquipmentBuildType";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"; import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { MessageType } from "@spt-aki/models/enums/MessageType"; import { MessageType } from "@spt-aki/models/enums/MessageType";
import { IProfileChangeEvent } from "@spt-aki/models/spt/dialog/ISendMessageDetails";
export interface IAkiProfile export interface IAkiProfile
{ {
@ -120,7 +121,7 @@ export interface Message
items?: MessageItems; items?: MessageItems;
maxStorageTime?: number; maxStorageTime?: number;
systemData?: ISystemData; systemData?: ISystemData;
profileChangeEvents?: any[]; profileChangeEvents?: IProfileChangeEvent[];
} }
export interface MessagePreview export interface MessagePreview

View File

@ -1,5 +1,4 @@
export enum ItemEventActions export enum ItemEventActions {
{
MOVE = "Move", MOVE = "Move",
REMOVE = "Remove", REMOVE = "Remove",
SPLIT = "Split", SPLIT = "Split",
@ -24,4 +23,5 @@ export enum ItemEventActions
REMOVE_BUILD = "RemoveBuild", REMOVE_BUILD = "RemoveBuild",
SAVE_EQUIPMENT_BUILD = "SaveEquipmentBuild", SAVE_EQUIPMENT_BUILD = "SaveEquipmentBuild",
REMOVE_EQUIPMENT_BUILD = "RemoveEquipmentBuild", REMOVE_EQUIPMENT_BUILD = "RemoveEquipmentBuild",
REDEEM_PROFILE_REWARD = "RedeemProfileReward"
} }

View File

@ -4,6 +4,7 @@ import { GiftSenderType } from "@spt-aki/models/enums/GiftSenderType";
import { SeasonalEventType } from "@spt-aki/models/enums/SeasonalEventType"; import { SeasonalEventType } from "@spt-aki/models/enums/SeasonalEventType";
import { Traders } from "@spt-aki/models/enums/Traders"; import { Traders } from "@spt-aki/models/enums/Traders";
import { IBaseConfig } from "@spt-aki/models/spt/config/IBaseConfig"; import { IBaseConfig } from "@spt-aki/models/spt/config/IBaseConfig";
import { IProfileChangeEvent } from "../dialog/ISendMessageDetails";
export interface IGiftsConfig extends IBaseConfig export interface IGiftsConfig extends IBaseConfig
{ {
@ -29,4 +30,6 @@ export interface Gift
timestampToSend?: number; timestampToSend?: number;
associatedEvent: SeasonalEventType; associatedEvent: SeasonalEventType;
collectionTimeHours: number; collectionTimeHours: number;
/** Optional, can be used to change profile settings like level/skills */
profileChangeEvents?: IProfileChangeEvent[];
} }

View File

@ -27,6 +27,14 @@ export interface ISendMessageDetails
systemData?: ISystemData; systemData?: ISystemData;
/** Optional - Used by ragfair messages */ /** Optional - Used by ragfair messages */
ragfairDetails?: MessageContentRagfair; ragfairDetails?: MessageContentRagfair;
/** Optional - Usage not known, unsure of purpose, even dumps dont have it */ /** OPTIONAL - allows modification of profile settings via mail */
profileChangeEvents?: any[]; profileChangeEvents?: IProfileChangeEvent[];
}
export interface IProfileChangeEvent
{
_id: string
Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader"
value: number
entity?: string
} }

View File

@ -40,6 +40,7 @@ export class InventoryItemEventRouter extends ItemEventRouterDefinition
new HandledRoute(ItemEventActions.EDIT_MAP_MARKER, false), new HandledRoute(ItemEventActions.EDIT_MAP_MARKER, false),
new HandledRoute(ItemEventActions.OPEN_RANDOM_LOOT_CONTAINER, false), new HandledRoute(ItemEventActions.OPEN_RANDOM_LOOT_CONTAINER, false),
new HandledRoute(ItemEventActions.HIDEOUT_QTE_EVENT, false), new HandledRoute(ItemEventActions.HIDEOUT_QTE_EVENT, false),
new HandledRoute(ItemEventActions.REDEEM_PROFILE_REWARD, false),
]; ];
} }
@ -90,6 +91,8 @@ export class InventoryItemEventRouter extends ItemEventRouterDefinition
return this.inventoryCallbacks.openRandomLootContainer(pmcData, body, sessionID); return this.inventoryCallbacks.openRandomLootContainer(pmcData, body, sessionID);
case ItemEventActions.HIDEOUT_QTE_EVENT: case ItemEventActions.HIDEOUT_QTE_EVENT:
return this.hideoutCallbacks.handleQTEEvent(pmcData, body, sessionID); return this.hideoutCallbacks.handleQTEEvent(pmcData, body, sessionID);
case ItemEventActions.REDEEM_PROFILE_REWARD:
return this.inventoryCallbacks.redeemProfileReward(pmcData, body, sessionID);
default: default:
throw new Error(`Unhandled event ${url}`); throw new Error(`Unhandled event ${url}`);
} }

View File

@ -77,6 +77,7 @@ export class GiftService
playerId, playerId,
giftData.localeTextId, giftData.localeTextId,
giftData.items, giftData.items,
giftData.profileChangeEvents,
this.timeUtil.getHoursAsSeconds(giftData.collectionTimeHours), this.timeUtil.getHoursAsSeconds(giftData.collectionTimeHours),
); );
} }

View File

@ -198,6 +198,7 @@ export class MailSendService
sessionId: string, sessionId: string,
messageLocaleId: string, messageLocaleId: string,
items: Item[] = [], items: Item[] = [],
profileChangeEvents = [],
maxStorageTimeSeconds = null, maxStorageTimeSeconds = null,
): void ): void
{ {
@ -214,6 +215,11 @@ export class MailSendService
details.itemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds ?? 172800; // 48 hours if no value supplied details.itemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds ?? 172800; // 48 hours if no value supplied
} }
if (profileChangeEvents.length > 0)
{
details.profileChangeEvents = profileChangeEvents;
}
this.sendMessageToPlayer(details); this.sendMessageToPlayer(details);
} }
@ -279,6 +285,11 @@ export class MailSendService
// Store reward items inside message and set appropriate flags inside message // Store reward items inside message and set appropriate flags inside message
this.addRewardItemsToMessage(message, itemsToSendToPlayer, messageDetails.itemsMaxStorageLifetimeSeconds); this.addRewardItemsToMessage(message, itemsToSendToPlayer, messageDetails.itemsMaxStorageLifetimeSeconds);
if (messageDetails.profileChangeEvents)
{
message.profileChangeEvents = messageDetails.profileChangeEvents;
}
// Add message to dialog // Add message to dialog
senderDialog.messages.push(message); senderDialog.messages.push(message);