Server/project/src/controllers/HealthController.ts

239 lines
9.1 KiB
TypeScript

import { inject, injectable } from "tsyringe";
import { HealthHelper } from "@spt-aki/helpers/HealthHelper";
import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { BodyPart, IHealthTreatmentRequestData } from "@spt-aki/models/eft/health/IHealthTreatmentRequestData";
import { IOffraidEatRequestData } from "@spt-aki/models/eft/health/IOffraidEatRequestData";
import { IOffraidHealRequestData } from "@spt-aki/models/eft/health/IOffraidHealRequestData";
import { ISyncHealthRequestData } from "@spt-aki/models/eft/health/ISyncHealthRequestData";
import { IWorkoutData } from "@spt-aki/models/eft/health/IWorkoutData";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
import { Traders } from "@spt-aki/models/enums/Traders";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PaymentService } from "@spt-aki/services/PaymentService";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable()
export class HealthController
{
constructor(
@inject("WinstonLogger") protected logger: ILogger,
@inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("PaymentService") protected paymentService: PaymentService,
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("HealthHelper") protected healthHelper: HealthHelper,
)
{}
/**
* stores in-raid player health
* @param pmcData Player profile
* @param info Request data
* @param sessionID Player id
* @param addEffects Should effects found be added or removed from profile
* @param deleteExistingEffects Should all prior effects be removed before apply new ones
*/
public saveVitality(
pmcData: IPmcData,
info: ISyncHealthRequestData,
sessionID: string,
addEffects = true,
deleteExistingEffects = true,
): void
{
this.healthHelper.saveVitality(pmcData, info, sessionID, addEffects, deleteExistingEffects);
}
/**
* When healing in menu
* @param pmcData Player profile
* @param request Healing request
* @param sessionID Player id
* @returns IItemEventRouterResponse
*/
public offraidHeal(pmcData: IPmcData, request: IOffraidHealRequestData, sessionID: string): IItemEventRouterResponse
{
const output = this.eventOutputHolder.getOutput(sessionID);
// Update medkit used (hpresource)
const healingItemToUse = pmcData.Inventory.items.find((item) => item._id === request.item);
if (!healingItemToUse)
{
const errorMessage = this.localisationService.getText(
"health-healing_item_not_found",
healingItemToUse._id,
);
this.logger.error(errorMessage);
return this.httpResponse.appendErrorToOutput(output, errorMessage);
}
// Ensure item has a upd object
if (!healingItemToUse.upd)
{
healingItemToUse.upd = {};
}
if (healingItemToUse.upd.MedKit)
{
healingItemToUse.upd.MedKit.HpResource -= request.count;
}
else
{
// Get max healing from db
const maxhp = this.itemHelper.getItem(healingItemToUse._tpl)[1]._props.MaxHpResource;
healingItemToUse.upd.MedKit = { HpResource: maxhp - request.count }; // Subtract amout used from max
}
// Resource in medkit is spent, delete it
if (healingItemToUse.upd.MedKit.HpResource <= 0)
{
this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output);
}
return output;
}
/**
* Handle Eat event
* Consume food/water outside of a raid
* @param pmcData Player profile
* @param request Eat request
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public offraidEat(pmcData: IPmcData, request: IOffraidEatRequestData, sessionID: string): IItemEventRouterResponse
{
let output = this.eventOutputHolder.getOutput(sessionID);
let resourceLeft = 0;
const itemToConsume = pmcData.Inventory.items.find((x) => x._id === request.item);
if (!itemToConsume)
{
// Item not found, very bad
return this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("health-unable_to_find_item_to_consume", request.item),
);
}
const consumedItemMaxResource = this.itemHelper.getItem(itemToConsume._tpl)[1]._props.MaxResource;
if (consumedItemMaxResource > 1)
{
if (itemToConsume.upd.FoodDrink === undefined)
{
itemToConsume.upd.FoodDrink = { HpPercent: consumedItemMaxResource - request.count };
}
else
{
itemToConsume.upd.FoodDrink.HpPercent -= request.count;
}
resourceLeft = itemToConsume.upd.FoodDrink.HpPercent;
}
// Remove item from inventory if resource has dropped below threshold
if (consumedItemMaxResource === 1 || resourceLeft < 1)
{
output = this.inventoryHelper.removeItem(pmcData, request.item, sessionID, output);
}
return output;
}
/**
* Handle RestoreHealth event
* Occurs on post-raid healing page
* @param pmcData player profile
* @param healthTreatmentRequest Request data from client
* @param sessionID Session id
* @returns IItemEventRouterResponse
*/
public healthTreatment(
pmcData: IPmcData,
healthTreatmentRequest: IHealthTreatmentRequestData,
sessionID: string,
): IItemEventRouterResponse
{
let output = this.eventOutputHolder.getOutput(sessionID);
const payMoneyRequest: IProcessBuyTradeRequestData = {
Action: healthTreatmentRequest.Action,
tid: Traders.THERAPIST,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_items: healthTreatmentRequest.items,
type: "",
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id: "",
count: 0,
// eslint-disable-next-line @typescript-eslint/naming-convention
scheme_id: 0,
};
output = this.paymentService.payMoney(pmcData, payMoneyRequest, sessionID, output);
if (output.warnings.length > 0)
{
return output;
}
for (const bodyPartKey in healthTreatmentRequest.difference.BodyParts)
{
// Get body part from request + from pmc profile
const partRequest: BodyPart = healthTreatmentRequest.difference.BodyParts[bodyPartKey];
const profilePart = pmcData.Health.BodyParts[bodyPartKey];
// Bodypart healing is chosen when part request hp is above 0
if (partRequest.Health > 0)
{
// Heal bodypart
profilePart.Health.Current = profilePart.Health.Maximum;
}
// Check for effects to remove
if (partRequest.Effects?.length > 0)
{
// Found some, loop over them and remove from pmc profile
for (const effect of partRequest.Effects)
{
delete pmcData.Health.BodyParts[bodyPartKey].Effects[effect];
}
// Remove empty effect object
if (Object.keys(pmcData.Health.BodyParts[bodyPartKey].Effects).length === 0)
{
delete pmcData.Health.BodyParts[bodyPartKey].Effects;
}
}
}
// Inform client of new post-raid, post-therapist heal values
output.profileChanges[sessionID].health = this.jsonUtil.clone(pmcData.Health);
return output;
}
/**
* applies skills from hideout workout.
* @param pmcData Player profile
* @param info Request data
* @param sessionID
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public applyWorkoutChanges(pmcData: IPmcData, info: IWorkoutData, sessionId: string): void
{
// https://dev.sp-tarkov.com/SPT-AKI/Server/issues/2674
// TODO:
// Health effects (fractures etc) are handled in /player/health/sync.
pmcData.Skills.Common = info.skills.Common;
}
}