418d9f2a8f
- Ability to use @spt-aki path alias on the whole project. - Swapped all imports from relative paths, for imports using the path alias. Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/157 Co-authored-by: TheSparta <thesparta@noreply.dev.sp-tarkov.com> Co-committed-by: TheSparta <thesparta@noreply.dev.sp-tarkov.com>
202 lines
7.7 KiB
TypeScript
202 lines
7.7 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
|
|
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
|
import { ISuit } from "@spt-aki/models/eft/common/tables/ITrader";
|
|
import { ClothingItem, IBuyClothingRequestData } from "@spt-aki/models/eft/customization/IBuyClothingRequestData";
|
|
import { IWearClothingRequestData } from "@spt-aki/models/eft/customization/IWearClothingRequestData";
|
|
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
|
|
@injectable()
|
|
export class CustomizationController
|
|
{
|
|
protected readonly clothingIds = {
|
|
lowerParentId: "5cd944d01388ce000a659df9",
|
|
upperParentId: "5cd944ca1388ce03a44dc2a4"
|
|
};
|
|
|
|
constructor(
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper
|
|
)
|
|
{}
|
|
|
|
/**
|
|
* Get purchasable clothing items from trader that match players side (usec/bear)
|
|
* @param traderID trader to look up clothing for
|
|
* @param sessionID Session id
|
|
* @returns ISuit array
|
|
*/
|
|
public getTraderSuits(traderID: string, sessionID: string): ISuit[]
|
|
{
|
|
const pmcData: IPmcData = this.profileHelper.getPmcProfile(sessionID);
|
|
const templates = this.databaseServer.getTables().templates.customization;
|
|
const suits = this.databaseServer.getTables().traders[traderID].suits;
|
|
|
|
// Get an inner join of clothing from templates.customization and ragmans suits array
|
|
const matchingSuits = suits.filter(x => x.suiteId in templates);
|
|
|
|
// Return all suits that have a side array containing the players side (usec/bear)
|
|
return matchingSuits.filter(x => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
|
|
}
|
|
|
|
/**
|
|
* Handle CustomizationWear event
|
|
* Equip one to many clothing items to player
|
|
*/
|
|
public wearClothing(pmcData: IPmcData, wearClothingRequest: IWearClothingRequestData, sessionID: string): IItemEventRouterResponse
|
|
{
|
|
for (const suitId of wearClothingRequest.suites)
|
|
{
|
|
// Find desired clothing item in db
|
|
const dbSuit = this.databaseServer.getTables().templates.customization[suitId];
|
|
|
|
// Legs
|
|
if (dbSuit._parent === this.clothingIds.lowerParentId)
|
|
{
|
|
pmcData.Customization.Feet = dbSuit._props.Feet;
|
|
}
|
|
|
|
// Torso
|
|
if (dbSuit._parent === this.clothingIds.upperParentId)
|
|
{
|
|
pmcData.Customization.Body = dbSuit._props.Body;
|
|
pmcData.Customization.Hands = dbSuit._props.Hands;
|
|
}
|
|
}
|
|
|
|
return this.eventOutputHolder.getOutput(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Handle CustomizationBuy event
|
|
* Purchase/unlock a clothing item from a trader
|
|
* @param pmcData Player profile
|
|
* @param buyClothingRequest Request object
|
|
* @param sessionId Session id
|
|
* @returns IItemEventRouterResponse
|
|
*/
|
|
public buyClothing(pmcData: IPmcData, buyClothingRequest: IBuyClothingRequestData, sessionId: string): IItemEventRouterResponse
|
|
{
|
|
const db = this.databaseServer.getTables();
|
|
const output = this.eventOutputHolder.getOutput(sessionId);
|
|
|
|
const traderOffer = this.getTraderClothingOffer(sessionId, buyClothingRequest.offer);
|
|
if (!traderOffer)
|
|
{
|
|
this.logger.error(this.localisationService.getText("customisation-unable_to_find_suit_by_id", buyClothingRequest.offer));
|
|
|
|
return output;
|
|
}
|
|
|
|
const suitId = traderOffer.suiteId;
|
|
if (this.outfitAlreadyPurchased(suitId, sessionId))
|
|
{
|
|
const suitDetails = db.templates.customization[suitId];
|
|
this.logger.error(this.localisationService.getText("customisation-item_already_purchased", {itemId: suitDetails._id, itemName: suitDetails._name}));
|
|
|
|
return output;
|
|
}
|
|
|
|
// Pay for items
|
|
this.payForClothingItems(sessionId, pmcData, buyClothingRequest.items, output);
|
|
|
|
// Add clothing to profile
|
|
this.saveServer.getProfile(sessionId).suits.push(suitId);
|
|
|
|
return output;
|
|
}
|
|
|
|
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit
|
|
{
|
|
return this.getAllTraderSuits(sessionId).find(x => x._id === offerId);
|
|
}
|
|
|
|
/**
|
|
* Has an outfit been purchased by a player
|
|
* @param suitId clothing id
|
|
* @param sessionID Session id of profile to check for clothing in
|
|
* @returns true if already purchased
|
|
*/
|
|
protected outfitAlreadyPurchased(suitId: string, sessionID: string): boolean
|
|
{
|
|
return this.saveServer.getProfile(sessionID).suits.includes(suitId);
|
|
}
|
|
|
|
/**
|
|
* Update output object and player profile with purchase details
|
|
* @param sessionId Session id
|
|
* @param pmcData Player profile
|
|
* @param clothingItems Clothing purchased
|
|
* @param output Client response
|
|
*/
|
|
protected payForClothingItems(sessionId: string, pmcData: IPmcData, clothingItems: ClothingItem[], output: IItemEventRouterResponse): void
|
|
{
|
|
for (const sellItem of clothingItems)
|
|
{
|
|
this.payForClothingItem(sessionId, pmcData, sellItem, output);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update output object and player profile with purchase details for single piece of clothing
|
|
* @param sessionId Session id
|
|
* @param pmcData Player profile
|
|
* @param clothingItem Clothing item purchased
|
|
* @param output Client response
|
|
*/
|
|
protected payForClothingItem(sessionId: string, pmcData: IPmcData, clothingItem: ClothingItem, output: IItemEventRouterResponse): void
|
|
{
|
|
const relatedItem = pmcData.Inventory.items.find(x => x._id === clothingItem.id);
|
|
if (!relatedItem)
|
|
{
|
|
this.logger.error(this.localisationService.getText("customisation-unable_to_find_clothing_item_in_inventory", clothingItem.id));
|
|
|
|
return;
|
|
}
|
|
|
|
if (clothingItem.del === true)
|
|
{
|
|
output.profileChanges[sessionId].items.del.push(relatedItem);
|
|
pmcData.Inventory.items.splice(pmcData.Inventory.items.indexOf(relatedItem), 1);
|
|
}
|
|
|
|
if (relatedItem.upd.StackObjectsCount > clothingItem.count)
|
|
{
|
|
relatedItem.upd.StackObjectsCount -= clothingItem.count;
|
|
output.profileChanges[sessionId].items.change.push({
|
|
_id: relatedItem._id,
|
|
_tpl: relatedItem._tpl,
|
|
parentId: relatedItem.parentId,
|
|
slotId: relatedItem.slotId,
|
|
location: relatedItem.location,
|
|
upd: { StackObjectsCount: relatedItem.upd.StackObjectsCount }
|
|
});
|
|
}
|
|
}
|
|
|
|
protected getAllTraderSuits(sessionID: string): ISuit[]
|
|
{
|
|
const traders = this.databaseServer.getTables().traders;
|
|
let result: ISuit[] = [];
|
|
|
|
for (const traderID in traders)
|
|
{
|
|
if (traders[traderID].base.customization_seller === true)
|
|
{
|
|
result = [...result, ...this.getTraderSuits(traderID, sessionID)];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
} |