import { inject, injectable } from "tsyringe"; import { Category } from "@spt-aki/models/eft/common/tables/IHandbookBase"; import { Money } from "@spt-aki/models/enums/Money"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { JsonUtil } from "@spt-aki/utils/JsonUtil"; class LookupItem { readonly byId: Map; readonly byParent: Map; constructor() { this.byId = new Map(); this.byParent = new Map(); } } export class LookupCollection { readonly items: LookupItem; readonly categories: LookupItem; constructor() { this.items = new LookupItem(); this.categories = new LookupItem(); } } @injectable() export class HandbookHelper { protected lookupCacheGenerated = false; protected handbookPriceCache = new LookupCollection(); constructor( @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("JsonUtil") protected jsonUtil: JsonUtil ) {} /** * Create an in-memory cache of all items with associated handbook price in handbookPriceCache class */ public hydrateLookup(): void { const handbookDb = this.jsonUtil.clone(this.databaseServer.getTables().templates.handbook); for (const handbookItem of handbookDb.Items) { this.handbookPriceCache.items.byId.set(handbookItem.Id, handbookItem.Price); if (!this.handbookPriceCache.items.byParent.has(handbookItem.ParentId)) { this.handbookPriceCache.items.byParent.set(handbookItem.ParentId, []); } this.handbookPriceCache.items.byParent.get(handbookItem.ParentId).push(handbookItem.Id); } for (const handbookCategory of handbookDb.Categories) { this.handbookPriceCache.categories.byId.set(handbookCategory.Id, handbookCategory.ParentId || null); if (handbookCategory.ParentId) { if (!this.handbookPriceCache.categories.byParent.has(handbookCategory.ParentId)) { this.handbookPriceCache.categories.byParent.set(handbookCategory.ParentId, []); } this.handbookPriceCache.categories.byParent.get(handbookCategory.ParentId).push(handbookCategory.Id); } } } /** * Get price from internal cache, if cache empty look up price directly in handbook (expensive) * If no values found, return 1 * @param tpl item tpl to look up price for * @returns price in roubles */ public getTemplatePrice(tpl: string): number { if (!this.lookupCacheGenerated) { this.hydrateLookup(); this.lookupCacheGenerated = true; } if (this.handbookPriceCache.items.byId.has(tpl)) { return this.handbookPriceCache.items.byId.get(tpl); } const handbookItem = this.databaseServer.getTables().templates.handbook.Items.find((x) => x.Id === tpl); if (!handbookItem) { const newValue = 0; this.handbookPriceCache.items.byId.set(tpl, newValue); return newValue; } return handbookItem.Price; } /** * Get all items in template with the given parent category * @param parentId * @returns string array */ public templatesWithParent(parentId: string): string[] { return this.handbookPriceCache.items.byParent.get(parentId) ?? []; } /** * Does category exist in handbook cache * @param category * @returns true if exists in cache */ public isCategory(category: string): boolean { return this.handbookPriceCache.categories.byId.has(category); } /** * Get all items associated with a categories parent * @param categoryParent * @returns string array */ public childrenCategories(categoryParent: string): string[] { return this.handbookPriceCache.categories.byParent.get(categoryParent) ?? []; } /** * Convert non-roubles into roubles * @param nonRoubleCurrencyCount Currency count to convert * @param currencyTypeFrom What current currency is * @returns Count in roubles */ public inRUB(nonRoubleCurrencyCount: number, currencyTypeFrom: string): number { if (currencyTypeFrom === Money.ROUBLES) { return nonRoubleCurrencyCount; } return Math.round(nonRoubleCurrencyCount * (this.getTemplatePrice(currencyTypeFrom) || 0)); } /** * Convert roubles into another currency * @param roubleCurrencyCount roubles to convert * @param currencyTypeTo Currency to convert roubles into * @returns currency count in desired type */ public fromRUB(roubleCurrencyCount: number, currencyTypeTo: string): number { if (currencyTypeTo === Money.ROUBLES) { return roubleCurrencyCount; } // Get price of currency from handbook const price = this.getTemplatePrice(currencyTypeTo); return price ? Math.round(roubleCurrencyCount / price) : 0; } public getCategoryById(handbookId: string): Category { return this.databaseServer.getTables().templates.handbook.Categories.find(x => x.Id === handbookId); } }